原始问题
用户bta对演员表给出了正确的解释 - 并评论了演员表的不合理之处system
。
我要补充:
这extern
条线充其量是奇怪的。在strict C99下是错误的,因为没有类型,导致无效;在 C89 下,类型将被假定为int
. 该行说“有一个外部定义的整数称为 system,另一个称为 puts”,这是不正确的 - 有一对具有这些名称的函数。代码实际上可能“工作”,因为链接器可能会将函数与假定的整数相关联。但是对于指针大小与int
. 当然,代码应该包含正确的标题(<stdio.h>
forputs()
和<stdlib.h>
for system()
andexit()
和<string.h>
for strcpy()
)。
这exit(1);
在两个不同的方面都很糟糕。
/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)
这些名称是“argument count”和“argument vector”的缩写,并且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关键字使系统和puts函数可用。这样做(我认为)基本上是直接引用(隐含的)头文件中定义的函数的位置……我的印象是 GDB 自动神奇地包括头文件 stdlib.h 用于系统和 stdio.h 用于 puts . 尚不清楚的一件事是系统和放置地址都写入同一个位置,我认为这可能是gera 所说的“所以链接器不会删除它”。
剖析评论:
- 第一句话不是很准确;它告诉编译器符号
system
和puts
在其他地方定义(作为整数)。代码链接时,puts()
-the-function的地址是已知的;代码会将其视为整数变量,但整数变量的地址实际上是函数的地址 - 所以强制转换毕竟强制编译器将其视为函数指针。
- 第二句话并不完全准确;
system()
链接器通过函数符号和puts()
C 库解析外部“变量”的地址。
- GDB 对编译或链接过程没有任何作用。
- 最后一句话完全没有任何意义。这些地址只会被写入相同的位置,因为您对同一变量进行了初始化和赋值。
这并没有促使我阅读整篇文章,必须说。尽职调查迫使我前进;之后的解释更好,尽管仍然没有我想的那么清楚。但是用一个过长但精心设计的参数字符串溢出缓冲区的操作是该操作的核心。代码提到了两者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”的准确警告。它与原始版本一样可利用;它是“更好”的代码,因为它可以干净地编译。间接是不必要的神秘,不是使代码可利用的关键部分。