7

我正在查看来自Insecure Programming的示例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);  
}

那么 - system 和 puts 的铸造是怎么回事?他们都返回一个int,那么为什么将其设为无效呢?

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

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

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

bta,我从上面的博客文章中收集到,强制转换系统会以某种方式阻止链接器将其删除。

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

当我们讨论函数指针的主题时,我想问一个后续问题,因为语法更清晰了。我正在查看一些使用函数指针的更高级的示例,并偶然发现了这种可憎的东西,取自一个托管 shellcode 的站点。

#include <stdio.h>

char shellcode[] = "一些shellcode";

诠释主要(无效)
{
    fprintf(stdout,"长度: %d\n",strlen(shellcode));
    (*(void(*)()) shellcode)();
}

所以数组被强制转换为返回void、引用和调用的函数?这看起来很讨厌 - 那么上面代码的目的是什么?

[/编辑]

4

2 回答 2

10

原始问题

用户bta对演员表给出了正确的解释 - 并评论了演员表的不合理之处system

我要补充:

extern条线充其量是奇怪的。在strict C99下是错误的,因为没有类型,导致无效;在 C89 下,类型将被假定为int. 该行说“有一个外部定义的整数称为 system,另一个称为 puts”,这是不正确的 - 有一对具有这些名称的函数。代码实际上可能“工作”,因为链接器可能会将函数与假定的整数相关联。但是对于指针大小与int. 当然,代码应该包含正确的标题(<stdio.h>forputs()<stdlib.h>for system()andexit()<string.h>for strcpy())。

exit(1);在两个不同的方面都很糟糕。

  • 它无条件地表示失败。您退出 0 或EXIT_SUCCESS表示成功。

  • 在我看来,最好return在结尾使用main()than 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)

这些名称是“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 所说的“所以链接器不会删除它”。

剖析评论:

  1. 第一句话不是很准确;它告诉编译器符号systemputs在其他地方定义(作为整数)。代码链接时,puts()-the-function的地址是已知的;代码会将其视为整数变量,但整数变量的地址实际上是函数的地址 - 所以强制转换毕竟强制编译器将其视为函数指针。
  2. 第二句话并不完全准确;system()链接器通过函数符号和puts()C 库解析外部“变量”的地址。
  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”的准确警告。它与原始版本一样可利用;它是“更好”的代码,因为它可以干净地编译。间接是不必要的神秘,不是使代码可利用的关键部分。

于 2011-02-19T17:16:20.137 回答
4

两者都system正常puts返回int。代码将它们转换为返回的指针void,大概是因为它们想忽略返回的任何值。(void)fn(argc[2]);如果强制转换没有改变返回类型,这应该等同于用作倒数第二行。有时会为回调函数放弃返回类型,这段代码片段似乎是回调的一个简单示例。

system如果从未使用过,为什么演员表超出了我的范围。我假设这里没有显示更多代码。

于 2011-02-19T16:58:27.560 回答