GDB常用命令:
GDB调试常用命令-CSDN博客
原理:
编译与反汇编过程-CSDN博客
Bomb实验实现
阶段一:
分析
- 分配空间:
sub $0x8,%rsp
为局部变量分配栈空间。 - 设置参数:
mov $0x402400,%esi
将字符串地址加载到%esi
。 - 比较字符串:
call 401338 <strings_not_equal>
调用函数比较字符串。 - 判断结果:
test %eax,%eax
检查返回值,决定是否触发“炸弹”。 - 触发或返回:不相等则
call 40143a <explode_bomb>
,相等则恢复栈指针并返回。
答案: "Border relations with Canada have never been better."
结构化汇编代码:
esi = $0x402400 // 字符串地址
strings_not_equal()
if eax == 0 {
release_sp()
}else{
explode_bomb()
}
C_Like代码:
// phase_1
int main(){
std::string string_ = read();
bool check = strings_not_equal(string_,"对应参数");
if (!check){
explode_bomb();
}
return 0;
}
阶段二(数组:
注意:
(1)区分清楚赋值是地址(lea),还是数据(mov)
(2)栈分配40个字节,剩余的字节可能用于对齐或存储其他局部变量(如寄存器的保存)
(3)寄存器地址偏移通过逐步调整通用寄存器(如 rbx
)的值,遍历数组中的元素。例如,int
类型增加 4 字节,char
类型增加 1 字节。
(4)答案: 1 2 4 8 16 32
结构化汇编代码:
阶段二:
rsp -= 0x28
rsi = rsp
read_six_numbers()
if *rsp != 1{
explode_bomb()
}
rbx = rsp + 4 // 栈顶第2个元素地址
rbp = rsp + 0x18 // 栈顶第6个元素地址
do{
eax = *(rbx - 4) // rbx前一个元素
if *rbx - eax*2 !=0 {
explode_bomb()
}
rbx += 4 // rbx指向下一个元素
}while (rbx - rbp <= 0) // // rbx为栈顶第7个元素地址(实际不存在)
...释放栈帧
rsp += 0x28
C_Like代码:
int main(){
int array[6];
read_six_numbers(array);
// 检查第一个数字是否为1
if(array[0] == 1){
explode_bomb();
}
// 检查剩余数字是否满足条件
for(int i = 1; i < 6; i++){
if(array[i] != array[i-1] * 2){
explode_bomb();
}
}
return 0;
}
阶段三(switch:
注意:
(1)cmpl $0x7, 0x8(%rsp)
这条指令是将 rsp + 0x08
处的内存内容(值)与 0x07
进行比较,而不是比较地址本身。
(2)调用函数前,rdi第一个参数,rsi第二个参数,rdx第三个参数,rcx第四个参数
sscanf 函数的核心参数如下:
输入字符串(RDI):char* output
格式字符串(RSI):定义解析规则,例如 "%d %d"。
变量地址(RDX 和 RCX):用于存储解析后的数据,即 x 和 y 的地址。
(3)0x402470
是跳转表的基地址(起始地址)
400f75: ff 24 c5 70 24 40 00 jmp *0x402470(,%rax,8)
(4) 例如:
0 0xcf
0 207
1 311
2 707
3 256
4 389
5 206
6 682
7 327
结构化汇编代码:
// Phase 3
rsp = rsp - 0x18
// 调用函数前参数设置 (output为rdi num_1)
rcx = rsp + 0xc // num_4
rdx = rsp + 8 // num_3
esi = $0x4025cf // num_2
eax = 0 // 获取函数返回值
__isoc99_sscanf@plt()
if eax <= 1 { // 有符号比较
explode_bomb()
}
if *(rsp + 8) - 7 > 0 { // 无符号比较
explode_bomb()
}
eax = *(rsp + 8)
jmp 0x402470 + 8 *eax
switch(x) {
case 0x400f7c:
eax = 0xcf;
break;
case 0x400f83:
eax = 0x2c3;
break;
case 0x400f8a:
eax = 0x100;
break;
case 0x400f91:
eax = 0x185;
break;
case 0x400f98:
eax = 0xce;
break;
case 0x400f9f:
eax = 0x2aa;
break;
case 0x400fa6:
eax = 0x147;
break;
case 0x400fad:
call explode_bomb();
break;
case 0x400fb2:
eax = 0x0;
break;
case 0x400fb9:
eax = 0x137;
}
if eax != *(rsp + 0xc){
explode_bomb()
}
ret
C_Like代码:
// phase_3
int main(char * output){
int x,y;
int check = sscanf(output,"%d %d",&x,&y); // 四个元素
if (check <= 1){
explode_bomb();
}
if(x > 7){
explode_bomb();
}
int ret;
switch (x) {
case 0:
ret = 0xcf; // corresponding to 0x400f7c
break;
case 1:
ret = 0x2c3; // corresponding to 0x400f83
break;
case 2:
ret = 0x100; // corresponding to 0x400f8a
break;
case 3:
ret = 0x185; // corresponding to 0x400f91
break;
case 4:
ret = 0xce; // corresponding to 0x400f98
break;
case 5:
ret = 0x2aa; // corresponding to 0x400f9f
break;
case 6:
ret = 0x147; // corresponding to 0x400fa6
break;
case 7:
explode_bomb(); // corresponding to 0x400fad
break;
case 8:
ret = 0x0; // corresponding to 0x400fb2
break;
case 9:
ret = 0x137; // corresponding to 0x400fb9
break;
}
if (ret != y){
explode_bomb();
}
return 0;
}
阶段四(递归:
分析
(1)sar %eax相当于 sar $1,eax (eax右移1位
(2)func_4功能:
- 找到目标值
x
:返回0
。 - 在左半部分找到
x
:返回偶数。 - 在右半部分找到
x
:返回奇数。
总体结果:
- 始终在左半部分查找,返回
0
有一次出现在右半部分,返回非0
(3)要求: 返回值必须为0,y也必须为0 (eg. x: 1 y:0
结构化汇编代码:
void phase_4() {
// 减小栈指针,准备局部变量空间
rsp -= 18;
// 函数参数设置
int* num_4 = (int*)(rsp + 0xc); // num_4 (y)
int* num_3 = (int*)(rsp + 8); // num_3 (x)
// 读取用户输入
esi = 0x4025cf; // 指向输入格式字符串
eax = 0;
int result = __isoc99_sscanf(esi, "%d %d", num_4, num_3);
// 判断输入是否合法
if (result != 2) {
explode_bomb();
}
// 判断 num_3 是否超出范围
if (*num_3 > 0xe) {
explode_bomb();
}
// 设置参数,准备调用 func_4
int num_2 = 0; // num_2
int num_1 = *num_3; // num_1
int max_value = 0xe; // 最大值 (edx)
// 调用 func_4
eax = func_4(num_1, num_2, max_value);
// 判断 func_4 的返回值是否合法
if (eax <= 0) {
explode_bomb();
} else {
// 判断 num_4 是否为0
if (*num_4 != 0) {
explode_bomb();
}
}
// 恢复栈指针并返回
rsp += 24;
return;
}
int func_4(int num_1, int num_2, int max_value) {
// 减小栈指针,准备局部变量空间
rsp -= 8;
// 计算中间值
int mid = (max_value - num_2) / 2 + num_2;
// 递归处理
if (mid > num_1) {
max_value = mid - 1;
eax = func_4(num_1, num_2, max_value);
eax *= 2;
} else if (mid < num_1) {
num_2 = mid + 1;
eax = func_4(num_1, num_2, max_value);
eax = eax * 2 + 1;
} else {
eax = 0;
}
// 恢复栈指针并返回
rsp += 8;
return eax;
}
C_Like代码:
int func_4(int target, int low, int high) {
int range_size = high - low; // 当前搜索范围的大小
range_size = (range_size + (range_size >> 31)) >> 1; // 处理符号位并计算中点偏移量
int mid = range_size + low; // 计算当前中间点的值
if(mid > target) {
return 2 * func_4(target, low, mid - 1); // 在左半部分继续搜索
} else if(mid < target) {
return 2 * func_4(target, mid + 1, high) + 1; // 在右半部分继续搜索
} else {
return 0; // 找到目标值,返回0
}
}
int main(char *output) {
int x, y;
int check = sscanf(output, "%d %d", &x, &y); // 解析用户输入
if (check != 2) {
explode_bomb(); // 如果输入格式不对,触发炸弹
}
if (x > 14) {
explode_bomb(); // 如果 x 超过 14,触发炸弹
}
// x 小于等于 14
check = func_4(x, 0, 14); // 调用 func_4 进行计算
if (check != 0 || y != 0) {
explode_bomb(); // 如果返回值不正确,触发炸弹
}
return 0;
}
阶段五:
分析
(1)nopl (%rax)
用于指令对齐和填充特定字节
(2)repz ret
提供防攻击优化,提升特定处理器性能;ret
为标准返回指令。
(3)在字符串处理中,每个字符占 1 字节,add $0x1, %rdx
将 rdx
指向下一个字符的地址。
(4)__stack_chk_fail@plt() 防止攻击,检查金丝雀值
(5)flyers
六个字母对应maduiersnfotvbyl
的下标分别为 9 15 14 5 6 7 ,对应ASCIL表编码进行&0xf操作后低四位
答案:
- IONEFG
- ionefg
结构化汇编代码:
// phase_5
int string_length(string * str){
if(str == 0){
return 0;
}
string * str_offset_addr = str
do{
str_offset_addr += 1
str_len = str_offset_addr - str
}
while(*str_offset_addr != 0);
return str_len;
}
void return_(){
eax = edx
pop rbx
pop rbp
pop r12
ret
};
int strings_not_equal(){
push r12
push rbp
push rbx
rbx = rdi
rbp = rsi
string_length()
r12 = eax
rdi = rbp
string_length()
edx = 1
if(r12 != eax){
return_();
}
eax = *rbx
if(al == 0){
edx = 0
return_();
}
if(al == *rbp){
while(true){
rbx += 1
rbp += 1
eax = *(rbx)
if(al == 0){
edx = 0
return_();
}
if(al != *rbp){
edx = 1
return_();
}
}
}else{
edx = 1
return_();
}
}
void phase_5(){
push rbx
rsp -= 0x20
rbx = rdi
rax = *(fs + 0x28)
rax = *(rsp + 0x18)
int check = string_length();
if(check != 6){
explode_bomb()
}
eax = 0
do{
ecx = *(rbx + rax)
*(rsp) = cl
rdx = *(rsp)
edx = edx & 0xf
edx = *(rdx + 0x4024b0)
*(rsp + rax + 16) = dl
rax += 1
}while(rax != 6)
*(rsp + 0x16) = 0
esi = 0x40245e
rdi = rsp + 16
int check_ = strings_not_equal()
if(check_ != 0){
explode_bomb()
}
rax = *(rsp + 0x18)
rax ^= *(fs + 0x28)
if(rax != 0){
__stack_chk_fail@plt() // 检测金丝雀值
}
rsp += 0x20
pop rbx
ret
}
C_Like代码:
// phase_5
int string_length(string * str){
if(!(*str)[0]){
return 0;
}
string * str_offset_addr = str;
int str_len = 0;
do{
str_offset_addr += 1;
str_len = str_offset_addr - str;
}
while((*str_offset_addr)[0]);
return str_len;
}
bool strings_not_equal(string * str1, string * str2){
if(string_length(str1) != string_length(str2)){
return true;
}
if(!(*str1)[0]){
return false;
}
int i = 0;
while((*str1)[i] == (*str2)[i]){
i++;
if(!(*str1)[i]){
return false;
}
if((*str1)[i] != (*str2)[i]){
return true;
}
}
return true;
}
void phase_5(){
string output = "maduiersnfotvbylSo";
int len = string_length(&output);
if(len != 6){
explode_bomb();
}
string *other;
for(int i = 0;i < 6;i++){
char c = (output)[i];
c = c & 0xf;
(*other)[i] = *(char*)(0x4024b0 + c);
}
string target = "flyers";
bool check = strings_not_equal(other, &target);
if(check){
explode_bomb();
}
}
阶段六:
分析
(1)链表结构: 嵌套解指针的结构
for(eax = 1;eax == ecx;eax += 1){
rdx = rdx + 8
}
(2)32位系统中,指针为4位,故这个结构体大小为8位
typedef struct {
int val;
Node* node_next;
}Node;
(3)GDB命令
1.查看寄存器的值
info reg rsp
2.查看内存的值
x/gx 0x7fffffffe000 + 0x20
x: 表示 examine(查看),用于查看内存。
/gx: 指定查看格式。
g: 表示以 8 字节(64 位)长的整数格式查看。
x: 表示以十六进制格式显示值。
答案: "Border relations with Canada have never been better."
友好的汇编代码(重点,作为汇编阅读方式):
00000000004010f4 <phase_6>:
4010fc: 48 83 ec 50 sub $0x50,%rsp
401100: 49 89 e5 mov %rsp,%r13
401103: 48 89 e6 mov %rsp,%rsi
401106: e8 51 03 00 00 call 40145c <read_six_numbers>
40110b: 49 89 e6 mov %rsp,%r14
40110e: 41 bc 00 00 00 00 mov $0x0,%r12d # i = 0
# num_1: 外层循环
-------------------------------------------------
401114: 4c 89 ed mov %r13,%rbp
401117: 41 8b 45 00 mov 0x0(%r13),%eax
40111b: 83 e8 01 sub $0x1,%eax
# 爆炸(eax > 6)
---------------------
40111e: 83 f8 05 cmp $0x5,%eax
401121: 76 05 jbe 401128 <phase_6+0x34>
401123: e8 12 03 00 00 call 40143a <explode_bomb>
---------------------
401128: 41 83 c4 01 add $0x1,%r12d # i += 1
40112c: 41 83 fc 06 cmp $0x6,%r12d # i == 6
401130: 74 21 je 401153 <phase_6+0x5f> # 退出整个循环
401132: 44 89 e3 mov %r12d,%ebx
# 内层循环
-------------------------------------------------
401135: 48 63 c3 movslq %ebx,%rax
401138: 8b 04 84 mov (%rsp,%rax,4),%eax
# 爆炸(eax == *rbp)
---------------------
40113b: 39 45 00 cmp %eax,0x0(%rbp)
40113e: 75 05 jne 401145 <phase_6+0x51>
401140: e8 f5 02 00 00 call 40143a <explode_bomb>
---------------------
401145: 83 c3 01 add $0x1,%ebx # j += 1
401148: 83 fb 05 cmp $0x5,%ebx # j > 5
40114b: 7e e8 jle 401135 <phase_6+0x41> # 退出内层循环
-------------------------------------------------
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114 <phase_6+0x20>
-------------------------------------------------------------
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi
401158: 4c 89 f0 mov %r14,%rax # addr = rsp
40115b: b9 07 00 00 00 mov $0x7,%ecx
# num_2 循环
-------------------------------------------------------------
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx
401164: 89 10 mov %edx,(%rax)
401166: 48 83 c0 04 add $0x4,%rax # addr += 4
40116a: 48 39 f0 cmp %rsi,%rax # addr == rsp + 0x18
40116d: 75 f1 jne 401160 <phase_6+0x6c> # 退出循环
-------------------------------------------------------------
40116f: be 00 00 00 00 mov $0x0,%esi
401174: eb 21 jmp 401197 <phase_6+0xa3>
# num_3 外层循环
-------------------------------------------------------------
# num_3 内层循环1
-------------------------------------------------------------
401176: 48 8b 52 08 mov 0x8(%rdx),%rdx
40117a: 83 c0 01 add $0x1,%eax
40117d: 39 c8 cmp %ecx,%eax
40117f: 75 f5 jne 401176 <phase_6+0x82>
-------------------------------------------------------------
401181: eb 05 jmp 401188 <phase_6+0x94>
# num_3 循环开始
# num_3 内层循环2
-------------------------------------------------------------
401183: ba d0 32 60 00 mov $0x6032d0,%edx
401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2)
40118d: 48 83 c6 04 add $0x4,%rsi
401191: 48 83 fe 18 cmp $0x18,%rsi
401195: 74 14 je 4011ab <phase_6+0xb7>
num_3 start:
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
40119a: 83 f9 01 cmp $0x1,%ecx
40119d: 7e e4 jle 401183 <phase_6+0x8f>
-------------------------------------------------------------
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
4011a9: eb cb jmp 401176 <phase_6+0x82>
-------------------------------------------------------------
4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax
4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi
4011ba: 48 89 d9 mov %rbx,%rcx
# num_4 循环
-------------------------------------------------------------
4011bd: 48 8b 10 mov (%rax),%rdx
4011c0: 48 89 51 08 mov %rdx,0x8(%rcx)
4011c4: 48 83 c0 08 add $0x8,%rax
// break;
4011c8: 48 39 f0 cmp %rsi,%rax
4011cb: 74 05 je 4011d2 <phase_6+0xde>
4011cd: 48 89 d1 mov %rdx,%rcx
4011d0: eb eb jmp 4011bd <phase_6+0xc9>
-------------------------------------------------------------
4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
4011d9: 00
4011da: bd 05 00 00 00 mov $0x5,%ebp
# num_5 循环
-------------------------------------------------------------
4011df: 48 8b 43 08 mov 0x8(%rbx),%rax
4011e3: 8b 00 mov (%rax),%eax
4011e5: 39 03 cmp %eax,(%rbx)
4011e7: 7d 05 jge 4011ee <phase_6+0xfa>
4011e9: e8 4c 02 00 00 call 40143a <explode_bomb>
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df <phase_6+0xeb>
-------------------------------------------------------------
4011f7: 48 83 c4 50 add $0x50,%rsp
401203: c3 ret
结构化汇编代码:
void phase_6(){
r13 = rsp
rsi = rsp
read_six_numbers()
r14 = rsp
r12 = 0 # i = 0
# num_1 外层循环
-------------------------------------------------
rbp = r13 (rsp 副本)
eax = *rsp - 1
if(eax <= 5){
explode_bomb()
}
r12 += 1 # i += 1
# 退出整个循环(i == 6)
if(r12 == 6){
break;
}
ebx = r12 # j = r12
# 内层循环
-------------------------------------------------
rax = ebx
eax = *(rsp + rax + 4)
if(rbp == eax){
explode_bomb()
}
ebx += 1 # j += 1
# 退出内层循环(j > 5)
if(ebx > 5){
break;
}
-------------------------------------------------
r13 += 4
-------------------------------------------------
rax = r14 # addr = rsp
# num_2 循环
-------------------------------------------------------------
for(rax = rsp;rax != rsp + 0x18;rax += 4){
edx = 7
edx -= *rax
*rax = edx
}
-------------------------------------------------------------
# num_3 循环
-------------------------------------------------------------
for(rsi = 0;rsi != 0x18;rsi += 4){
ecx = *(rsp + rsi)
if(ecx > 1){
edx = 0x6032d0
}else{
edx = 0x6032d0
for(eax = 1;eax == ecx;eax += 1){
rdx = rdx + 8
}
}
*(rsp + rsi*2 + 0x20) = rdx
}
-------------------------------------------------------------
rcx = *(rsp + 0x20)
# num_4 循环(关于rax的操作只有 rax+=8
# => 可以预见不可能中途退出,故可以直接判断为for循环)
-------------------------------------------------------------
for(rax = rsp + 0x28;rax != rsp + 0x50;){
rdx = *rax
*(rcx + 8) = rdx
rax += 8
if(rax == rsp + 0x50){
break;
}
rcx = rdx
}
-------------------------------------------------------------
*(rdx + 8) = 0
# num_5 循环
-------------------------------------------------------------
for(ebp = 5;ebp != 0;ebp -= 1){
rax = *(rbx + 8)
eax = *rax
if(*rbx < eax){
explode_bomb()
}
rbx = *(rbx + 8)
}
-------------------------------------------------------------
return 0;
}