了解 gcc x86 反汇编中的非连续堆栈寻址
Understanding non-contiguous stack addressing in gcc x86 disassembly
我正在使用这个C源代码用gcc编译(Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0.
/////////////////////////////////////////////////////////////////////////////////////////////
// Name: megabeets_0x1.c
// Description: Simple crackme intended to teach radare2 framework capabilities.
// Compilation: $ gcc megabeets_0x1.c -o megabeets_0x1 -fno-stack-protector -m32 -z execstac
//
// Author: Itay Cohen (@megabeets)
// Website: https://www.megabeets.net
/////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <string.h>
void rot13 (char *s) {
if (s == NULL)
return;
int i;
for (i = 0; s[i]; i++) {
if (s[i] >= 'a' && s[i] <= 'm') { s[i] += 13; continue; }
if (s[i] >= 'A' && s[i] <= 'M') { s[i] += 13; continue; }
if (s[i] >= 'n' && s[i] <= 'z') { s[i] -= 13; continue; }
if (s[i] >= 'N' && s[i] <= 'Z') { s[i] -= 13; continue; }
}
}
int beet(char *name)
{
char buf[128];
strcpy(buf, name);
char string[] = "Megabeets";
rot13(string);
return !strcmp(buf, string);
}
int main(int argc, char *argv[])
{
printf("\n .:: Megabeets ::.\n");
printf("Think you can make it?\n");
if (argc >= 2 && beet(argv[1]))
{
printf("Success!\n\n");
}
else
printf("Nop, Wrong argument.\n\n");
return 0;
}
使用了 gcc 命令
gcc megabeets_0x1.c -o test32 -fno-stack-protector -z execstack -m32 -no-pie -fno-pic
使用 objdump 生成的函数 beet
的反汇编如下所示:
080485a8 <beet>:
80485a8: 55 push ebp
80485a9: 89 e5 mov ebp,esp
80485ab: 81 ec 98 00 00 00 sub esp,0x98
80485b1: 83 ec 08 sub esp,0x8
80485b4: ff 75 08 push DWORD PTR [ebp+0x8]
80485b7: 8d 85 78 ff ff ff lea eax,[ebp-0x88]
80485bd: 50 push eax
80485be: e8 6d fd ff ff call 8048330 <strcpy@plt>
80485c3: 83 c4 10 add esp,0x10
80485c6: c7 85 6e ff ff ff 4d mov DWORD PTR [ebp-0x92],0x6167654d
80485cd: 65 67 61
80485d0: c7 85 72 ff ff ff 62 mov DWORD PTR [ebp-0x8e],0x74656562
80485d7: 65 65 74
80485da: 66 c7 85 76 ff ff ff mov WORD PTR [ebp-0x8a],0x73
80485e1: 73 00
80485e3: 83 ec 0c sub esp,0xc
80485e6: 8d 85 6e ff ff ff lea eax,[ebp-0x92]
80485ec: 50 push eax
80485ed: e8 94 fe ff ff call 8048486 <rot13>
80485f2: 83 c4 10 add esp,0x10
80485f5: 83 ec 08 sub esp,0x8
80485f8: 8d 85 6e ff ff ff lea eax,[ebp-0x92]
80485fe: 50 push eax
80485ff: 8d 85 78 ff ff ff lea eax,[ebp-0x88]
8048605: 50 push eax
8048606: e8 15 fd ff ff call 8048320 <strcmp@plt>
804860b: 83 c4 10 add esp,0x10
804860e: 85 c0 test eax,eax
8048610: 0f 94 c0 sete al
8048613: 0f b6 c0 movzx eax,al
8048616: c9 leave
8048617: c3 ret
我对这个反汇编没有什么怀疑,
- 压入
ebp
并将esp
移动到ebp
后,堆栈指针第一次减少0x98
,然后减少0x8
,总计0xA0
这导致堆栈帧对齐到 16 字节。为什么编译器不直接从 esp 中减去 0xA0
而不是 2 个后续减法?
- 从C代码可以看出,函数beet中的变量
buf
是128字节。但是在这个反汇编中 buf
是由 ebp-0x88
指向的,这意味着缓冲区有 136 个字节。为什么分配 136 字节而不是 128 字节?
- 在调用像
strcpy
或rot13
这样的函数之前,随机字节数在调用这些函数之前和执行完成之后首先从esp中减去另一个随机字节数被添加到esp(我猜这是为了清除发送给那些函数的参数)。
示例- 在调用 rot13
之前,从 esp 中减去 0xc
,完成后添加 0x10
而不是 0xc
。
因此,这些 esp 的随机移位和推送数据会导致数据不连续,从而导致堆栈内存的利用率较低。这种行为背后有什么特别的原因吗?
在 google 或 Whosebug 上搜索后,我找不到这些疑惑的任何答案。
谢谢
注意:
GCC 代码优化结果几乎相同的反汇编。
从堆栈中减去 0x98 使其保持 16 字节对齐。额外的8个字节是为给strcpy压入参数做准备的,这样调用前栈又是16字节对齐的。
确实为buf分配了128字节。 buf 和 ebp 之间的附加字节用于对齐或用于编译器临时文件或编译器的某些其他目的。也许这里的 return 值是 space。在任何情况下,编译器最终都不需要使用 space。如果启用优化,它可能不会存在。
与#1 一样,在为每个调用推送参数之前调整堆栈指针,以便堆栈在调用之前对齐 16 字节。
我正在使用这个C源代码用gcc编译(Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0.
/////////////////////////////////////////////////////////////////////////////////////////////
// Name: megabeets_0x1.c
// Description: Simple crackme intended to teach radare2 framework capabilities.
// Compilation: $ gcc megabeets_0x1.c -o megabeets_0x1 -fno-stack-protector -m32 -z execstac
//
// Author: Itay Cohen (@megabeets)
// Website: https://www.megabeets.net
/////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <string.h>
void rot13 (char *s) {
if (s == NULL)
return;
int i;
for (i = 0; s[i]; i++) {
if (s[i] >= 'a' && s[i] <= 'm') { s[i] += 13; continue; }
if (s[i] >= 'A' && s[i] <= 'M') { s[i] += 13; continue; }
if (s[i] >= 'n' && s[i] <= 'z') { s[i] -= 13; continue; }
if (s[i] >= 'N' && s[i] <= 'Z') { s[i] -= 13; continue; }
}
}
int beet(char *name)
{
char buf[128];
strcpy(buf, name);
char string[] = "Megabeets";
rot13(string);
return !strcmp(buf, string);
}
int main(int argc, char *argv[])
{
printf("\n .:: Megabeets ::.\n");
printf("Think you can make it?\n");
if (argc >= 2 && beet(argv[1]))
{
printf("Success!\n\n");
}
else
printf("Nop, Wrong argument.\n\n");
return 0;
}
使用了 gcc 命令
gcc megabeets_0x1.c -o test32 -fno-stack-protector -z execstack -m32 -no-pie -fno-pic
使用 objdump 生成的函数 beet
的反汇编如下所示:
080485a8 <beet>:
80485a8: 55 push ebp
80485a9: 89 e5 mov ebp,esp
80485ab: 81 ec 98 00 00 00 sub esp,0x98
80485b1: 83 ec 08 sub esp,0x8
80485b4: ff 75 08 push DWORD PTR [ebp+0x8]
80485b7: 8d 85 78 ff ff ff lea eax,[ebp-0x88]
80485bd: 50 push eax
80485be: e8 6d fd ff ff call 8048330 <strcpy@plt>
80485c3: 83 c4 10 add esp,0x10
80485c6: c7 85 6e ff ff ff 4d mov DWORD PTR [ebp-0x92],0x6167654d
80485cd: 65 67 61
80485d0: c7 85 72 ff ff ff 62 mov DWORD PTR [ebp-0x8e],0x74656562
80485d7: 65 65 74
80485da: 66 c7 85 76 ff ff ff mov WORD PTR [ebp-0x8a],0x73
80485e1: 73 00
80485e3: 83 ec 0c sub esp,0xc
80485e6: 8d 85 6e ff ff ff lea eax,[ebp-0x92]
80485ec: 50 push eax
80485ed: e8 94 fe ff ff call 8048486 <rot13>
80485f2: 83 c4 10 add esp,0x10
80485f5: 83 ec 08 sub esp,0x8
80485f8: 8d 85 6e ff ff ff lea eax,[ebp-0x92]
80485fe: 50 push eax
80485ff: 8d 85 78 ff ff ff lea eax,[ebp-0x88]
8048605: 50 push eax
8048606: e8 15 fd ff ff call 8048320 <strcmp@plt>
804860b: 83 c4 10 add esp,0x10
804860e: 85 c0 test eax,eax
8048610: 0f 94 c0 sete al
8048613: 0f b6 c0 movzx eax,al
8048616: c9 leave
8048617: c3 ret
我对这个反汇编没有什么怀疑,
- 压入
ebp
并将esp
移动到ebp
后,堆栈指针第一次减少0x98
,然后减少0x8
,总计0xA0
这导致堆栈帧对齐到 16 字节。为什么编译器不直接从 esp 中减去0xA0
而不是 2 个后续减法? - 从C代码可以看出,函数beet中的变量
buf
是128字节。但是在这个反汇编中buf
是由ebp-0x88
指向的,这意味着缓冲区有 136 个字节。为什么分配 136 字节而不是 128 字节? - 在调用像
strcpy
或rot13
这样的函数之前,随机字节数在调用这些函数之前和执行完成之后首先从esp中减去另一个随机字节数被添加到esp(我猜这是为了清除发送给那些函数的参数)。 示例- 在调用rot13
之前,从 esp 中减去0xc
,完成后添加0x10
而不是0xc
。 因此,这些 esp 的随机移位和推送数据会导致数据不连续,从而导致堆栈内存的利用率较低。这种行为背后有什么特别的原因吗?
在 google 或 Whosebug 上搜索后,我找不到这些疑惑的任何答案。
谢谢
注意: GCC 代码优化结果几乎相同的反汇编。
从堆栈中减去 0x98 使其保持 16 字节对齐。额外的8个字节是为给strcpy压入参数做准备的,这样调用前栈又是16字节对齐的。
确实为buf分配了128字节。 buf 和 ebp 之间的附加字节用于对齐或用于编译器临时文件或编译器的某些其他目的。也许这里的 return 值是 space。在任何情况下,编译器最终都不需要使用 space。如果启用优化,它可能不会存在。
与#1 一样,在为每个调用推送参数之前调整堆栈指针,以便堆栈在调用之前对齐 16 字节。