为什么铸造“外部放置”到函数指针“(void(*)(char *))& puts”? [英] Why cast "extern puts" to a function pointer "(void(*)(char*))&puts"?

查看:182
本文介绍了为什么铸造“外部放置”到函数指针“(void(*)(char *))& puts”?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在查看 abo3.c =http://community.corest.com/~gera/InsecureProgramming =nofollow>不安全的编程,我不是grokking的铸造在下面的例子。



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);  
}

所以 - 系统和货币的投资是什么?它们都返回一个 int 所以为什么把它转换为void?

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.



感谢您的意见!


Thank you both for your input!

Jonathan Leffler ,实际上有一个原因代码是坏。它应该是可利用的,溢出缓冲区和函数指针等。 mishou.org 有一篇博文,介绍如何利用上述代码。很多它仍然在我的头上。

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 ,我从上面的博文中收集到系统会以某种方式阻止链接器删除它。

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


有一件事是不能立即清楚的,系统和地址都写在同一个位置,我认为可能是什么gera正在谈论所以链接器不会删除它。

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".

虽然我们的主题的函数指针, d现在想问一个后续问题的语法更清楚。我正在看一些更高级的例子使用函数指针,偶然在这可憎的,从站点托管shellcode。

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)();
}

所以数组被转换为一个函数返回 void ,引用和调用?

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]

推荐答案

原始问题



用户 bta 已经对铸件给出了正确的解释 - 并且评论了铸件系统的非线性。

Original question

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

要添加:

extern 行最多是奇怪的。在严格的C99下是错误的,因为没有类型,这使它无效;在C89下,类型将被假定为 int 。该行说有一个外部定义的整数称为系统,另一个称为puts',这是不正确的 - 有一对函数与这些名称。代码可能实际上工作,因为链接器可能将函数与假定的整数相关联。但是对于64位机器来说是不安全的,其中指针的大小与 int 不同。当然,代码应该包括正确的头文件(< stdio.h> for puts() c $ c>< stdlib.h> for system() exit()< string.h> 为 strcpy())。

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()).

exit(1); 在两个单独的计数上是不好的。

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


  • 它无条件地指示故障。您以0或 EXIT_SUCCESS 退出以表示成功。

在我看来, return 结束时 >。不是每个人都同意我,但我不喜欢 exit()作为 main()的最后一行。关于唯一的借口是避免其他不良做法的问题,例如注册到 atexit()的函数,这取决于<$ c中定义的局部变量的继续存在$ c> main()

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)   

这是正确的(它将工作),但100%aconventional。正常的声明是:

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

int main(int argc, char **argv)

这些名称是'argument count'和'argument vector'的缩写,并使用 argc 因为字符串向量(数组)的名称是异常的并且完全混乱。

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.

可以看到它正在通过一组分级的例子。我不知道作者是否对argc / argv问题有一个盲点,或者故意搞乱('abo1'表明他在玩,但它在我看来没有帮助)。例子应该是你的想法,但没有太多的解释他们做什么。我不认为我可以推荐该网站。

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.


此代码执行的操作是什么?

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)();
}

这需要字符串'shellcode'的地址,到一个函数,它接受一个不确定的参数集,并返回没有值,并且没有参数执行它。该字符串包含一些exploit的二进制汇编代码 - 通常运行s​​hell - 并且入侵者的目标是获得一个root特权程序来执行他们的shellcode并给他们一个命令提示符,具有root权限。从那里,系统是他们的拥有。

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.

Mishou 的网站上进行的分析不像我想要的那样权威:

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


一,这个代码使用C语言中的extern关键字使系统和puts功能可用。这是什么(我认为)基本上是直接引用定义在(隐含)头文件中的函数的位置...我得到的印象是,GDB是自动神奇地包括头文件stdlib.h为system和stdio.h为puts 。一个不立即清楚的事情是,系统和地址都写在同一位置,我认为可能是gera在说什么所以链接器不会删除它。

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".

解释评论:


  1. 第一句话不是很准确;它告诉编译器符号 system puts 在其他地方定义(作为整数)。当代码被链接时, puts()的函数的地址是已知的;代码将把它当作一个整数变量,但是整数变量的地址实际上是函数的地址 - 所以强制转换强制编译器把它作为一个函数指针。

  2. 第二句话不完全准确;链接器通过函数符号 system() puts()解析外部变量的地址C库。

  3. GDB没有任何编译或链接过程。

  4. 最后一句没有任何意义。

  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.

这并没有激励我们的地址只能写入相同的位置,因为它有一个初始化和赋值给同一个变量。我读整篇文章,一定要说。尽职调查迫使我向前;后来的解释更好,虽然仍然不是那么清楚,我认为它可以是。但是,用一个超长但精心设计的参数字符串溢出缓冲区的操作是操作的核心。代码提到 puts() system(),以便在非漏洞模式下运行时, puts()函数是一个已知符号(否则,你必须使用 dlopen()找到它的地址)并且因此在exploit模式下运行时,代码具有可以直接使用的符号 system()。未使用的外部引用在可执行文件中不可用 - 当你意识到在一个典型的系统标题中有多少个符号,与包含头的程序所使用的数字相比,这是一件好事。

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.

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

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.

abo3.c代码可以写成:

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);  
}

现在它只编译一个警告与我最初使用的繁琐的编译选项 - 这是'argc'不使用的准确警告。它与原来一样可以利用;它是'更好的'代码,因为它编译干净。间接是不必要的神秘,不是使代码可利用的关键部分。

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.

这篇关于为什么铸造“外部放置”到函数指针“(void(*)(char *))&amp; puts”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆