本文主要是介绍bomb 实验,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
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 = rspread_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);// 检查第一个数字是否为1if(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 3rsp = rsp - 0x18// 调用函数前参数设置 (output为rdi num_1)
rcx = rsp + 0xc // num_4
rdx = rsp + 8 // num_3
esi = $0x4025cf // num_2eax = 0 // 获取函数返回值
__isoc99_sscanf@plt()if eax <= 1 { // 有符号比较explode_bomb()
}if *(rsp + 8) - 7 > 0 { // 无符号比较explode_bomb()
}eax = *(rsp + 8)
jmp 0x402470 + 8 *eaxswitch(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 0x400f7cbreak;case 1:ret = 0x2c3; // corresponding to 0x400f83break;case 2:ret = 0x100; // corresponding to 0x400f8abreak;case 3:ret = 0x185; // corresponding to 0x400f91break;case 4:ret = 0xce; // corresponding to 0x400f98break;case 5:ret = 0x2aa; // corresponding to 0x400f9fbreak;case 6:ret = 0x147; // corresponding to 0x400fa6break;case 7:explode_bomb(); // corresponding to 0x400fadbreak;case 8:ret = 0x0; // corresponding to 0x400fb2break;case 9:ret = 0x137; // corresponding to 0x400fb9break;}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_4int num_2 = 0; // num_2int num_1 = *num_3; // num_1int max_value = 0xe; // 最大值 (edx)// 调用 func_4eax = func_4(num_1, num_2, max_value);// 判断 func_4 的返回值是否合法if (eax <= 0) {explode_bomb();} else {// 判断 num_4 是否为0if (*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 小于等于 14check = 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_5int string_length(string * str){if(str == 0){return 0;}string * str_offset_addr = strdo{str_offset_addr += 1str_len = str_offset_addr - str}while(*str_offset_addr != 0);return str_len;
}void return_(){eax = edx pop rbxpop rbppop r12ret
};int strings_not_equal(){push r12push rbppush rbxrbx = rdirbp = rsistring_length()r12 = eaxrdi = rbpstring_length()edx = 1if(r12 != eax){return_();}eax = *rbxif(al == 0){edx = 0return_();}if(al == *rbp){ while(true){rbx += 1rbp += 1eax = *(rbx)if(al == 0){edx = 0return_();}if(al != *rbp){edx = 1return_();}}}else{edx = 1return_();}}void phase_5(){push rbxrsp -= 0x20rbx = rdi rax = *(fs + 0x28)rax = *(rsp + 0x18) int check = string_length();if(check != 6){explode_bomb()}eax = 0do{ecx = *(rbx + rax)*(rsp) = clrdx = *(rsp)edx = edx & 0xfedx = *(rdx + 0x4024b0)*(rsp + rax + 16) = dlrax += 1 }while(rax != 6)*(rsp + 0x16) = 0esi = 0x40245erdi = rsp + 16int check_ = strings_not_equal()if(check_ != 0){explode_bomb()}rax = *(rsp + 0x18)rax ^= *(fs + 0x28)if(rax != 0){__stack_chk_fail@plt() // 检测金丝雀值} rsp += 0x20pop rbxret
}
C_Like代码:
// phase_5int 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 + 0x20x: 表示 examine(查看),用于查看内存。
/gx: 指定查看格式。
g: 表示以 8 字节(64 位)长的整数格式查看。
x: 表示以十六进制格式显示值。
x/gx 0x00000000006032d0 返回 0x000000010000014c:下一个节点的地址:0x00000001
节点的值:0x0000014c (即 332)
可得链表:
332 168 924 691 477 443
0x6032d0->0x6032e0->0x6032f0->0x603300->0x603310->0x603320
(4)链表地址数组
0x6032F0 链表地址数组第一个
0x6032F0 + 8 指向 next_node地址
mov 0x20(%rsp),%rbx -> rbx = 0x6032F0
mov %rbx,%rcx -> rcx = 0x6032F0mov (%rax),%rdx -> rdx = 0x6032F0
mov %rdx,0x8(%rcx) -> *(0x6032F0 + 8) = 0x603300
(*(0x6032F0 + 8) 指向第一个元素的next 地址)
答案: "Border relations with Canada have never been better."
友好的汇编代码(重点,作为汇编阅读方式):
00000000004010f4 <phase_6>:4010fc: 48 83 ec 50 sub $0x50,%rsp401100: 49 89 e5 mov %rsp,%r13401103: 48 89 e6 mov %rsp,%rsi401106: e8 51 03 00 00 call 40145c <read_six_numbers>40110b: 49 89 e6 mov %rsp,%r1440110e: 41 bc 00 00 00 00 mov $0x0,%r12d # i = 0
# num_1: 外层循环
-------------------------------------------------401114: 4c 89 ed mov %r13,%rbp401117: 41 8b 45 00 mov 0x0(%r13),%eax40111b: 83 e8 01 sub $0x1,%eax# 爆炸(eax > 6)---------------------40111e: 83 f8 05 cmp $0x5,%eax401121: 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 += 140112c: 41 83 fc 06 cmp $0x6,%r12d # i == 6401130: 74 21 je 401153 <phase_6+0x5f> # 退出整个循环401132: 44 89 e3 mov %r12d,%ebx# 内层循环------------------------------------------------- 401135: 48 63 c3 movslq %ebx,%rax401138: 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 += 1401148: 83 fb 05 cmp $0x5,%ebx # j > 540114b: 7e e8 jle 401135 <phase_6+0x41> # 退出内层循环------------------------------------------------- 40114d: 49 83 c5 04 add $0x4,%r13401151: eb c1 jmp 401114 <phase_6+0x20>-------------------------------------------------------------401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi401158: 4c 89 f0 mov %r14,%rax # addr = rsp40115b: b9 07 00 00 00 mov $0x7,%ecx
# num_2 循环
-------------------------------------------------------------401160: 89 ca mov %ecx,%edx401162: 2b 10 sub (%rax),%edx401164: 89 10 mov %edx,(%rax)401166: 48 83 c0 04 add $0x4,%rax # addr += 440116a: 48 39 f0 cmp %rsi,%rax # addr == rsp + 0x1840116d: 75 f1 jne 401160 <phase_6+0x6c> # 退出循环
-------------------------------------------------------------40116f: be 00 00 00 00 mov $0x0,%esi401174: eb 21 jmp 401197 <phase_6+0xa3># num_3 外层循环
-------------------------------------------------------------# num_3 内层循环1-------------------------------------------------------------401176: 48 8b 52 08 mov 0x8(%rdx),%rdx40117a: 83 c0 01 add $0x1,%eax40117d: 39 c8 cmp %ecx,%eax40117f: 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,%edx401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2)40118d: 48 83 c6 04 add $0x4,%rsi401191: 48 83 fe 18 cmp $0x18,%rsi401195: 74 14 je 4011ab <phase_6+0xb7>num_3 start: 401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx40119a: 83 f9 01 cmp $0x1,%ecx40119d: 7e e4 jle 401183 <phase_6+0x8f>-------------------------------------------------------------40119f: b8 01 00 00 00 mov $0x1,%eax4011a4: ba d0 32 60 00 mov $0x6032d0,%edx4011a9: eb cb jmp 401176 <phase_6+0x82>
-------------------------------------------------------------4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi4011ba: 48 89 d9 mov %rbx,%rcx# num_4 循环
-------------------------------------------------------------4011bd: 48 8b 10 mov (%rax),%rdx4011c0: 48 89 51 08 mov %rdx,0x8(%rcx)4011c4: 48 83 c0 08 add $0x8,%rax// break; 4011c8: 48 39 f0 cmp %rsi,%rax4011cb: 74 05 je 4011d2 <phase_6+0xde>4011cd: 48 89 d1 mov %rdx,%rcx4011d0: 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),%rax4011e3: 8b 00 mov (%rax),%eax4011e5: 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),%rbx4011f2: 83 ed 01 sub $0x1,%ebp4011f5: 75 e8 jne 4011df <phase_6+0xeb>
-------------------------------------------------------------4011f7: 48 83 c4 50 add $0x50,%rsp401203: c3 ret
结构化汇编代码:
void phase_6(){r13 = rsprsi = rspread_six_numbers()r14 = rspr12 = 0 # i = 0
# num_1 外层循环
------------------------------------------------- rbp = r13 (rsp 副本)eax = *rsp - 1if(eax <= 5){explode_bomb()}r12 += 1 # i += 1# 退出整个循环(i == 6)if(r12 == 6){ break;}ebx = r12 # j = r12# 内层循环------------------------------------------------- rax = ebxeax = *(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 = 7edx -= *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) = rdxrax += 8if(rax == rsp + 0x50){break;}rcx = rdx }-------------------------------------------------------------*(rdx + 8) = 0# num_5 循环
-------------------------------------------------------------for(ebp = 5;ebp != 0;ebp -= 1){rax = *(rbx + 8)eax = *raxif(*rbx < eax){explode_bomb() }rbx = *(rbx + 8)}
-------------------------------------------------------------return 0;}
这篇关于bomb 实验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!