为什么要投“外部看跌期权”?到函数指针“(void(*)(char*))&puts”?
我正在查看 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
原始问题
用户bta对演员阵容给出了正确的解释 - 并评论了演员阵容
系统的不恰当.
我要补充一点:
extern
行充其量是奇怪的。在严格的C99下是错误的,因为没有类型,这使得它无效;在 C89 下,类型将被假定为int
。该行显示“有一个外部定义的整数称为 system,另一个称为 put”,这是不正确的 - 有一对具有这些名称的函数。该代码实际上可能“工作”,因为链接器可能将函数与假定的整数相关联。但对于 64 位机器来说这是不安全的,因为指针的大小与int
不同。当然,代码应包含正确的标头(
forputs()
和
forsystem()
和exit()
,以及
用于strcpy()
)。exit(1);
在两个单独的方面都是不好的。它表示失败 - 无条件。您可以使用 0 或
EXIT_SUCCESS
退出来表示成功。在我看来,在
main()
末尾使用return
比使用exit()
更好。不一定每个人都同意我的观点,但我不喜欢将exit()
视为main()
的最后一行。唯一的借口是避免其他不良实践带来的问题,例如使用atexit()
注册的函数依赖于main()
中定义的局部变量的持续存在>.不好的代码!我担心包含此类代码的信息源并不能解释所有的可怕之处(因为显示如此混乱的代码的唯一借口是剖析它并纠正它)。
代码中还有另一个奇怪的地方:
这是“正确的”(它会起作用),但 100% 是非传统的。正常的声明是:
这些名称是“参数计数”和“参数向量”的缩写,并且使用 argc 作为字符串向量(数组)的名称是不正常的并且完全令人困惑。
访问所引用的站点后,您可以看到它正在经历一组分级示例。我不确定作者是否只是在 argc/argv 问题上有盲点或者故意搞乱('abo1' 表明他正在玩,但在我看来这没有帮助)。这些示例应该可以满足您的需求,但对它们的作用没有太多解释。我不认为我可以推荐该网站。
延伸问题
它获取字符串“shellcode”的地址,并将其视为指向一个函数的指针,该函数接受一组不确定的参数,不返回任何值,并且不带参数执行它。该字符串包含某些漏洞利用的二进制汇编代码(通常运行 shell),入侵者的目标是获得 root 权限的程序来执行其 shellcode,并为他们提供具有 root 权限的命令提示符。从那时起,该系统就属于他们自己了。当然,为了练习,第一步是让一个非 root 程序来执行 shellcode。
回顾分析
Mishou 中的分析的网站并不像我希望的那样权威:
剖析评论:
system
和puts
在其他地方定义(作为整数)。当代码被链接时,puts()
函数的地址是已知的;代码会将其视为整型变量,但整型变量的地址实际上是函数的地址 - 因此强制转换强制编译器将其视为函数指针。system()
和puts()
解析外部“变量”的地址。必须说,这并没有促使我阅读整篇文章。尽职调查迫使我前进;后来的解释更好了,尽管仍然没有我想象的那么清楚。但使用过长但精心设计的参数字符串来溢出缓冲区的操作是该操作的核心。该代码提到了
puts()
和system()
,因此当在非利用模式下运行时,puts()
函数是已知的符号(否则,您必须使用dlopen()
来查找其地址),因此当在漏洞利用模式下运行时,代码具有符号system()
可以直接使用。未使用的外部引用在可执行文件中不可用 - 当您意识到典型的系统标头中有多少个符号与包含标头的程序使用的符号数量相比,这是一件好事。显示了一些巧妙的技巧 - 尽管这些技巧的实现没有显示在特定页面上;我假设(未经验证)
getenvaddr
程序的信息可用。abo3.c 代码可以写为:
现在,它使用我最初使用的繁琐编译选项仅发出一个警告 - 这就是未使用“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 beint
. 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 asint
. Of course, the code should include the correct headers (<stdio.h>
forputs()
and<stdlib.h>
forsystem()
andexit()
, and<string.h>
forstrcpy()
).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 ofmain()
thanexit()
. Not everyone necessarily agrees with me, but I do not like to seeexit()
as the last line ofmain()
. About the only excuse for it is to avoid problems from other bad practices, such as functions registered withatexit()
that depend on the continued existence of local variables defined inmain()
.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:
That is 'correct' (it will work) but 100% aconventional. The normal declaration is:
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
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:
Dissecting the commentary:
system
andputs
are defined (as integers) somewhere else. When the code is linked, the address ofputs()
-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.system()
andputs()
in the C library.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()
andsystem()
so that when run in non-exploit mode, theputs()
function is a known symbol (otherwise, you'd have to usedlopen()
to find its address), and so that when run in exploit mode, the code has the symbolsystem()
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:
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.
system
和puts
通常都返回int
。代码将它们转换为返回void
的指针,大概是因为它们想要忽略返回的任何值。如果转换没有更改返回类型,这应该相当于使用(void)fn(argc[2]);
作为倒数第二行。有时,回调函数会放弃返回类型,此代码片段似乎是回调的简单示例。如果从未使用过
system
,为什么要进行强制转换,这超出了我的理解。我假设还有更多代码未在此处显示。Both
system
andputs
normally returnint
. The code is casting them to a pointer that returnsvoid
, 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.