为什么要投“外部看跌期权”?到函数指针“(void(*)(char*))&puts”?

发布于 2024-10-18 02:55:13 字数 1490 浏览 1 评论 0原文

我正在查看 abo3.c 的示例 不安全编程,我不会在下面的示例中进行转换。有人可以启发我吗?

int main(int argv,char **argc)   
{  
    extern system,puts;  
    void (*fn)(char*)=(void(*)(char*))&system;  
    char buf[256];  

    fn=(void(*)(char*))&puts;  
    strcpy(buf,argc[1]);  
    fn(argc[2]);  
    exit(1);  
}

那么 - 系统和看跌期权的铸造是怎样的呢?它们都返回一个 int 那么为什么要把它转换为 void 呢?

我真的很感谢对整个程序的解释,以正确地看待它。

[编辑]
谢谢你们的意见!

Jonathan Leffler,代码“糟糕”实际上是有原因的。它应该是可利用的,溢出缓冲区和函数指针等。 mishou.org 有一篇关于如何利用上述代码的博客文章。其中很多仍然在我的脑海中。

bta,我从上面的博客文章中了解到,转换系统会以某种方式阻止链接器删除它。

目前还不清楚的一件事是系统和放置地址都写入同一位置,我认为这可能就是 gera 所说的“因此链接器不会删除它”。

虽然我们正在讨论函数指针的主题,但既然语法更清晰了,我想问一个后续问题。我正在研究一些使用函数指针的更高级的示例,并偶然发现了这个令人厌恶的东西,它取自托管 shellcode 的网站。

#include <stdio.h>

char shellcode[] = "some shellcode";

int main(void)
{
    fprintf(stdout,"Length: %d\n",strlen(shellcode));
    (*(void(*)()) shellcode)();
}

所以数组被转换为返回 void 的函数,被引用和调用?这看起来很糟糕——那么上面代码的目的是什么?

[/编辑]

I'm looking at example abo3.c from Insecure Programming and I'm not grokking the casting in the example below. Could someone enlighten me?

int main(int argv,char **argc)   
{  
    extern system,puts;  
    void (*fn)(char*)=(void(*)(char*))&system;  
    char buf[256];  

    fn=(void(*)(char*))&puts;  
    strcpy(buf,argc[1]);  
    fn(argc[2]);  
    exit(1);  
}

So - what's with the casting for system and puts? They both return an int so why cast it to void?

I'd really appreciate an explanation of the whole program to put it in perspective.

[EDIT]
Thank you both for your input!

Jonathan Leffler, there is actually a reason for the code to be 'bad'. It's supposed to be exploitable, overflowing buffers and function pointers etc. mishou.org has a blog post on how to exploit the above code. A lot of it is still above my head.

bta, I gather from the above blog post that casting system would somehow prevent the linker from removing it.

One thing that is not immediately clear is that the system and puts addresses are both written to the same location, I think that might be what gera is talking about “so the linker doesn’t remove it”.

While we are on the subject of function pointers, I'd like to ask a follow-up question now that the syntax is clearer. I was looking at some more advanced examples using function pointers and stumbled upon this abomination, taken from a site hosting shellcode.

#include <stdio.h>

char shellcode[] = "some shellcode";

int main(void)
{
    fprintf(stdout,"Length: %d\n",strlen(shellcode));
    (*(void(*)()) shellcode)();
}

So the array is getting cast to a function returning void, referenced and called? That just looks nasty - so what's the purpose of the above code?

[/EDIT]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

雨后咖啡店 2024-10-25 02:55:13

原始问题

用户bta对演员阵容给出了正确的解释 - 并评论了演员阵容系统的不恰当.

我要补充一点:

extern 行充其量是奇怪的。在严格的C99下是错误的,因为没有类型,这使得它无效;在 C89 下,类型将被假定为 int。该行显示“有一个外部定义的整数称为 system,另一个称为 put”,这是不正确的 - 有一对具有这些名称的函数。该代码实际上可能“工作”,因为链接器可能将函数与假定的整数相关联。但对于 64 位机器来说这是不安全的,因为指针的大小与 int 不同。当然,代码应包含正确的标头( for puts() for system()exit(),以及 用于 strcpy())。

exit(1); 在两个单独的方面都是不好的。

  • 它表示失败 - 无条件。您可以使用 0 或 EXIT_SUCCESS 退出来表示成功。

  • 在我看来,在 main() 末尾使用 return 比使用 exit() 更好。不一定每个人都同意我的观点,但我不喜欢将 exit() 视为 main() 的最后一行。唯一的借口是避免其他不良实践带来的问题,例如使用 atexit() 注册的函数依赖于 main() 中定义的局部变量的持续存在>.


/usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -c nasty.c
nasty.c: In function ‘main’:
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘system’
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘puts’
nasty.c:3: warning: built-in function ‘puts’ declared as non-function
nasty.c:8: warning: implicit declaration of function ‘strcpy’
nasty.c:8: warning: incompatible implicit declaration of built-in function ‘strcpy’
nasty.c:10: warning: implicit declaration of function ‘exit’
nasty.c:10: warning: incompatible implicit declaration of built-in function ‘exit’
nasty.c: At top level:
nasty.c:1: warning: unused parameter ‘argv’

不好的代码!我担心包含此类代码的信息源并不能解释所有的可怕之处(因为显示如此混乱的代码的唯一借口是剖析它并纠正它)。


代码中还有另一个奇怪的地方:

int main(int argv,char **argc)   

这是“正确的”(它会起作用),但 100% 是非传统的。正常的声明是:

int main(int argc, char **argv)

这些名称是“参数计数”和“参数向量”的缩写,并且使用 argc 作为字符串向量(数组)的名称是不正常的并且完全令人困惑。


访问所引用的站点后,您可以看到它正在经历一组分级示例。我不确定作者是否只是在 argc/argv 问题上有盲点或者故意搞乱('abo1' 表明他正在玩,但在我看来这没有帮助)。这些示例应该可以满足您的需求,但对它们的作用没有太多解释。我不认为我可以推荐该网站。


延伸问题

这段代码中的演员在做什么?

#include <stdio.h>

char shellcode[] = "some shellcode";

int main(void)
{
    fprintf(stdout,"Length: %d\n",strlen(shellcode));
    (*(void(*)()) shellcode)();
}

它获取字符串“shellcode”的地址,并将其视为指向一个函数的指针,该函数接受一组不确定的参数,不返回任何值,并且不带参数执行它。该字符串包含某些漏洞利用的二进制汇编代码(通常运行 shell),入侵者的目标是获得 root 权限的程序来执行其 shellcode,并为他们提供具有 root 权限的命令提示符。从那时起,该系统就属于他们自己了。当然,为了练习,第一步是让一个非 root 程序来执行 shellcode。

回顾分析

Mishou 中的分析的网站并不像我希望的那样权威:

一,这段代码使用了C语言中的extern关键字来使system和puts函数可用。它的作用(我认为)基本上是直接引用(隐含的)头文件中定义的函数的位置...我的印象是 GDB 会自动神奇地包含用于系统的头文件 stdlib.h 和用于 put 的头文件 stdio.h 。目前尚不清楚的一件事是系统地址和放置地址都写入同一位置,我认为这可能就是 gera 所说的“因此链接器不会删除它”。

剖析评论:

  1. 第一句话不太准确;它告诉编译器符号 systemputs 在其他地方定义(作为整数)。当代码被链接时,puts()函数的地址是已知的;代码会将其视为整型变量,但整型变量的地址实际上是函数的地址 - 因此强制转换强制编译器将其视为函数指针。
  2. 第二句话不完全准确;链接器通过 C 库中的函数符号 system()puts() 解析外部“变量”的地址。
  3. GDB 不执行任何编译或链接过程。
  4. 最后一句话根本没有任何意义。地址只会写入同一位置,因为您对同一变量进行了初始化和赋值。

必须说,这并没有促使我阅读整篇文章。尽职调查迫使我前进;后来的解释更好了,尽管仍然没有我想象的那么清楚。但使用过长但精心设计的参数字符串来溢出缓冲区的操作是该操作的核心。该代码提到了 puts()system(),因此当在非利用模式下运行时,puts() 函数是已知的符号(否则,您必须使用 dlopen() 来查找其地址),因此当在漏洞利用模式下运行时,代码具有符号 system()可以直接使用。未使用的外部引用在可执行文件中不可用 - 当您意识到典型的系统标头中有多少个符号与包含标头的程序使用的符号数量相比,这是一件好事。

显示了一些巧妙的技巧 - 尽管这些技巧的实现没有显示在特定页面上;我假设(未经验证)getenvaddr 程序的信息可用。

abo3.c 代码可以写为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)   
{  
    void (*fn)(char*) = (void(*)(char*))system;  
    char buf[256];  

    fn = (void(*)(char*))puts;  
    strcpy(buf, argv[1]);  
    fn(argv[2]);  
    exit(1);  
}

现在,它使用我最初使用的繁琐编译选项仅发出一个警告 - 这就是未使用“argc”的准确警告。它与原始版本一样可被利用;但它是“更好”的代码,因为它编译干净。间接寻址是不必要的神秘,不是使代码可利用的关键部分。

Original question

User bta has given a correct explanation of the cast - and commented on the infelicity of casting system.

I'm going to add:

The extern line is at best weird. It is erroneous under strict C99 because there is no type, which makes it invalid; under C89, the type will be assumed to be int. The line says 'there is an externally defined integer called system, and another called puts', which is not correct - there are a pair of functions with those names. The code may actually 'work' because the linker might associate the functions with the supposed integers. But it is not safe for a 64-bit machine where pointers are not the same size as int. Of course, the code should include the correct headers (<stdio.h> for puts() and <stdlib.h> for system() and exit(), and <string.h> for strcpy()).

The exit(1); is bad on two separate counts.

  • It indicates failure - unconditionally. You exit with 0 or EXIT_SUCCESS to indicate success.

  • In my view, it is better to use return at the end of main() than exit(). Not everyone necessarily agrees with me, but I do not like to see exit() as the last line of main(). About the only excuse for it is to avoid problems from other bad practices, such as functions registered with atexit() that depend on the continued existence of local variables defined in main().


/usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -c nasty.c
nasty.c: In function ‘main’:
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘system’
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘puts’
nasty.c:3: warning: built-in function ‘puts’ declared as non-function
nasty.c:8: warning: implicit declaration of function ‘strcpy’
nasty.c:8: warning: incompatible implicit declaration of built-in function ‘strcpy’
nasty.c:10: warning: implicit declaration of function ‘exit’
nasty.c:10: warning: incompatible implicit declaration of built-in function ‘exit’
nasty.c: At top level:
nasty.c:1: warning: unused parameter ‘argv’

Not good code! I worry about a source of information that contains such code and doesn't explain all the awfulness (because the only excuse for showing such messy code is to dissect it and correct it).


There's another weirdness in the code:

int main(int argv,char **argc)   

That is 'correct' (it will work) but 100% aconventional. The normal declaration is:

int main(int argc, char **argv)

The names are short for 'argument count' and 'argument vector', and using argc as the name for the vector (array) of strings is abnormal and downright confusing.


Having visited the site referenced, you can see that it is going through a set of graduated examples. I'm not sure whether the author simply has a blind spot on the argc/argv issue or is deliberately messing around ('abo1' suggests that he is playing, but it is not helpful in my view). The examples are supposed to feed your mind, but there isn't much explanation of what they do. I don't think I could recommend the site.


Extension question

What's the cast in this code doing?

#include <stdio.h>

char shellcode[] = "some shellcode";

int main(void)
{
    fprintf(stdout,"Length: %d\n",strlen(shellcode));
    (*(void(*)()) shellcode)();
}

This takes the address of the string 'shellcode' and treats it as a pointer to a function that takes an indeterminate set of arguments and returns no values and executes it with no arguments. The string contains the binary assembler code for some exploit - usually running the shell - and the objective of the intruder is to get a root-privileged program to execute their shellcode and give them a command prompt, with root privileges. From there, the system is theirs to own. For practicing, the first step is to get a non-root program to execute the shellcode, of course.

Reviewing the analysis

The analysis at Mishou's web site is not as authoritative as I'd like:

One, this code uses the extern keyword in the C language to make the system and puts functions available. What this does (I think) is basically references directly the location of a function defined in the (implied) header files…I get the impression that GDB is auto-magically including the header files stdlib.h for system and stdio.h for puts. One thing that is not immediately clear is that the system and puts addresses are both written to the same location, I think that might be what gera is talking about “so the linker doesn’t remove it”.

Dissecting the commentary:

  1. The first sentence isn't very accurate; it tells the compiler that the symbols system and puts are defined (as integers) somewhere else. When the code is linked, the address of puts()-the-function is known; the code will treat it as an integer variable, but the address of the integer variable is, in fact, the address of the function - so the cast forces the compiler to treat it as a function pointer after all.
  2. The second sentence is not fully accurate; the linker resolves the addresses of the external 'variables' via the function symbols system() and puts() in the C library.
  3. GDB has nothing whatsoever to do the compilation or linking process.
  4. The last sentence does not make any sense at all. The addresses only get written to the same location because you have an initialization and an assignment to the same variable.

This didn't motivate me to read the whole article, it must be said. Due diligence forces me onwards; the explanation afterwards is better, though still not as clear as I think it could be. But the operation of overflowing the buffer with an overlong but carefully crafted argument string is the core of the operation. The code mentions both puts() and system() so that when run in non-exploit mode, the puts() function is a known symbol (otherwise, you'd have to use dlopen() to find its address), and so that when run in exploit mode, the code has the symbol system() available for direct use. Unused external references are not made available in the executable - a good thing when you realize how many symbols there are in a typical system header compared with the number used by a program that includes the header.

There are some neat tricks shown - though the implementation of those tricks is not shown on the specific page; I assume (without having verified it) that the information for getenvaddr program is available.

The abo3.c code can be written as:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)   
{  
    void (*fn)(char*) = (void(*)(char*))system;  
    char buf[256];  

    fn = (void(*)(char*))puts;  
    strcpy(buf, argv[1]);  
    fn(argv[2]);  
    exit(1);  
}

Now it compiles with only one warning with the fussy compilation options I originally used - and that's the accurate warning that 'argc' is not used. It is just as exploitable as the original; it is 'better' code though because it compiles cleanly. The indirections were unnecessary mystique, not a crucial part of making the code exploitable.

岁月如刀 2024-10-25 02:55:13

systemputs 通常都返回 int。代码将它们转换为返回 void 的指针,大概是因为它们想要忽略返回的任何值。如果转换没有更改返回类型,这应该相当于使用 (void)fn(argc[2]); 作为倒数第二行。有时,回调函数会放弃返回类型,此代码片段似乎是回调的简单示例。

如果从未使用过 system ,为什么要进行强制转换,这超出了我的理解。我假设还有更多代码未在此处显示。

Both system and puts normally return int. The code is casting them to a pointer that returns void, presumably because they want to ignore whatever value is returned. This should be equivalent to using (void)fn(argc[2]); as the penultimate line if the cast didn't change the return type. Casting away the return type is sometimes done for callback functions, and this code snippet seems to be a simplistic example of a callback.

Why the cast for system if it is never used is beyond me. I'm assuming that there's more code that isn't shown here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文