C程序中的Shellcode

16

揭秘Execve Shellcode中,解释了编写execve shellcode的方法:

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

unsigned char code[] = 
"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

main()
{

    printf("Shellcode Length: %d\n", strlen(code));

    int (*ret)() = (int(*)())code;

    ret();
}

int (*ret)() = (int(*)())code;这行代码是做什么的?


2
cdecl 表示:将代码转换为返回整数的函数指针。 - David Ranieri
我不理解 ret 后面的 () 和代码前面的 int()()。 - user720694
1
为了能够在头脑中解析这样的结构,请按顺时针/螺旋规则进行:http://c-faq.com/decl/spiral.anderson.html - jwaliszko
1
顺便说一下:这种方法在W^X操作系统上默认是禁用的。在现代处理器上运行64位OS X上的nop(0x90)会导致EXC_BAD_ACCESS,因为内核不会运行来自.bss、.text或堆的任何代码,因为这些区域引用了带有第63位设置(NX)的PAE/长模式页表条目。在没有像PAX/ExecShield这样的东西的非PAE/非长模式操作系统上可能会起作用,比如DOS。写入代码区域也不起作用(通过一堆C内联asm nop进行自变异)。最好制作一个程序,为给定的平台生成最小的可执行文件并运行它。 - user246672
4个回答

18
  int (*ret)() = (int(*)())code;
  ~~~~~~~~~~~~   ~~~~~~~~~~~~~~
        1              2

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
               3
  1. 它将ret定义为一个指向无参数()且返回int的函数指针。因此,这些()表示函数参数的定义。

  2. 它用于将code强制转换为一个指向无参数()且返回int的函数指针。

  3. code强制转换为函数并将其赋值给ret。之后可以调用ret();

unsigned char code[] =  "\x31\xc0\x50\x68\x6e\x2f\...

它是以十六进制值表示的机器指令序列。它将被作为函数注入到代码中。


那么,当调用ret()时,代码实际上是如何“运行”的,因为我没有看到C调用“加载”shellcode并运行它的操作? - user720694
@user85030,是的,在ret()中调用了“load”,请搜索“函数指针”。 - David Ranieri
所提供的链接 用11个步骤解释了它。这个机器指令序列做的事情和一个调用进程所需的一样。 - masoud
好的。所以我读了关于函数指针的部分1)并理解了。第2)部分可以写成(int* ())code而不是(int(*)())code吗?重新表达我的问题,如何将变量转换为函数?(因为我们在左边有一个函数指针被赋值给右边的函数。 - user720694
@MM。非常感谢您的帮助。您能否请看一下 https://dev59.com/nXLYa4cB1Zd3GeqPV0g0? - user720694
显示剩余4条评论

2
    (*(void(*)())shellcode)()

==

    p = (void(*)()) shellcode;
    (*p)();

1

这个函数指针部分能不能以更简单的形式重写?

我不知道你是否认为这更简单,但也许可以这样:

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

unsigned char code[] = 
"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

typedef int(*shellcode_t)();

int main(int argc, char ** argv) {
    printf("Shellcode Length: %ld\n", strlen(code));

    shellcode_t ret = (shellcode_t)code;

    ret();
}

0

int行声明了ret()函数,通过指向code[]数组来实现;换句话说,该函数被映射到code[]二进制指令。

\x结构是在字符串中嵌入十六进制字符的安全方式。例如,您可以将“\x31”替换为“1”,因为“1”的字符代码为49,或十六进制31。


我不理解 ret 后面的 () 和代码前面的 int()()。 - user720694
ret是一个函数指针(https://en.wikipedia.org/wiki/Function_pointer),指向一个具有未定义数量参数(`()`)并返回`int`的函数。`(int(*) () )它将code函数地址转换为函数指针地址,该函数指针接受未定义数量的参数,这就是ret`声明的方式。 - Jack
这个函数指针部分能否以更简单的形式重写? - user720694

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接