解压完出现了三个文件bomb、bomb.c、readme,其中bomb.c是实验的源代码,不过他最重要的部分被删除了。我们可以通过将可执行文件bomb反汇编来查看内部情况。通过objdump -d bomb > bomb.asm我们获得了一个新的文件,内容是bomb的汇编代码。
gdb的使用
基本命令 |
|
|
1.进入调试状态 |
gdb bomb |
后面的bomb是我们要调试的程序 |
2.查看源码 |
(gdb) l |
前面的(gdb)代表已经进入调试状态,后面的l,将源码按行号显示 |
3.设置断点 |
(gdb) b 6 |
意思是运行到源码的第六行时停止(第六行的指令没有执行) |
4.查看断点情况 |
(gdb) info b |
将我们设置的断点列举出来 |
5.运行代码 |
(gdb) r |
|
6.显示变量值 |
(gdb) p n |
p(print)变量名 |
7.单步运行 |
(gdb) n |
next |
8.程序继续运行 |
(gdb) c |
continue |
9.退出 |
(gdb) q |
quit |
10.访问内存 |
x/参数 <地址> |
参数s是输出为字符串,d为十进制输出,x为十六进制输出 |
11.跟踪寄存器 |
display<$寄存器> |
碰到断点停下时显示所有跟踪寄存器的值 |
用$前缀表示一个立即数
函数传参:当参数个数小于等于6个时,使用寄存器rdi,rsi,rdx,rcx,r8,r9,从第7个参数开始通过栈传递,顺序为从右往左入栈。csapp 3.7
实验部分
phase_1
这就是第一个炸弹

1 2 3 4 5 6 7 8 9
| 0000000000400ee0 <phase_1>: 400ee0: 48 83 ec 08 sub $0x8,%rsp ;创建栈帧 400ee4: be 00 24 40 00 mov $0x402400,%esi ;将0x402400复制到esi中,前去查看 400ee9: e8 4a 04 00 00 call 401338 <strings_not_equal> ;根据名字来看,调用比较函数 400eee: 85 c0 test %eax,%eax 400ef0: 74 05 je 400ef7 <phase_1+0x17> ;相等则跳转 400ef2: e8 43 05 00 00 call 40143a <explode_bomb> ;爆炸 400ef7: 48 83 c4 08 add $0x8,%rsp ;恢复栈帧 400efb: c3 ret
|
test执行过程与and相似,但他只设置条件码寄存器,不改变目的寄存器中的值。这里的用法是,如果返回值为零,则条件码寄存器置1,je进行跳转。
我们用x/s 0x402400查看以0x402400为首地址的字符串。
先把一个值(字符串的地址)放到了%esi里,而放到%edi里的就是我们输入的字符串地址,记住传参顺序,di、si、dx、cx、r8、r9.
1 2
| (gdb) x/s 0x402400 0x402400: "Border relations with Canada have never been better."
|
phase_2
第二个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 0000000000400efc <phase_2>: 400efc: 55 push rbp ;保存栈帧寄存器 400efd: 53 push rbx 400efe: 48 83 ec 28 sub rsp,0x28 400f02: 48 89 e6 mov rsi,rsp ;-------------------为函数的调用做准备 400f05: e8 52 05 00 00 call 40145c <read_six_numbers> 400f0a: 83 3c 24 01 cmp DWORD PTR [rsp],0x1 400f0e: 74 20 je 400f30 <phase_2+0x34> 400f10: e8 25 05 00 00 call 40143a <explode_bomb> 400f15: eb 19 jmp 400f30 <phase_2+0x34> 400f17: 8b 43 fc mov eax,DWORD PTR [rbx-0x4] 400f1a: 01 c0 add eax,eax 400f1c: 39 03 cmp DWORD PTR [rbx],eax 400f1e: 74 05 je 400f25 <phase_2+0x29> 400f20: e8 15 05 00 00 call 40143a <explode_bomb> 400f25: 48 83 c3 04 add rbx,0x4 400f29: 48 39 eb cmp rbx,rbp 400f2c: 75 e9 jne 400f17 <phase_2+0x1b> 400f2e: eb 0c jmp 400f3c <phase_2+0x40> 400f30: 48 8d 5c 24 04 lea rbx,[rsp+0x4] ;将(rsp)+0x4赋值给rbx 400f35: 48 8d 6c 24 18 lea rbp,[rsp+0x18] 400f3a: eb db jmp 400f17 <phase_2+0x1b> 400f3c: 48 83 c4 28 add rsp,0x28 400f40: 5b pop rbx 400f41: 5d pop rbp 400f42: c3 ret
|
不太熟悉lea指令,先学一下。
lea是“load effective address”的缩写,把一个内存变量的有效地址送给指定的寄存器,简单地说lea指令可以用来将一个内存地址直接赋值给目的操作数。lea eax,[ebx+8]是将ebx+8这个值直接赋值给eax,与之相似的mov指令,mov eax,[ebx+8]是将内存地址为ebx+8处的数据赋值给eax。对于lea指令,lea eax,eax是会报错的,只能lea eax,[eax] 等同于mov eax,eax其实就是【eax】就是eax的值。
理一下逻辑:
1.调用函数,接收六个数字
2.[rsp]和0x1对比,所以(rsp)=1
3.如果相等则进行跳转,不相等则爆炸!!!
4.将rsp+4和rsp+0x18分别赋值给rbx和rbp
5.跳转,将地址为rbx-4处的数据赋值给eax
6.将eax乘2
7.[rbx]和eax进行比较
8.相等则将rbx+4,不相等则爆炸!!!!
9.比较rbx和rbp,如果相等则结束程序(success),如果不相等则跳转到步骤5.
第四行的指令mov rsi,rsp,rsi是传递第二个参数的寄存器,他保存的是什么值?保存的是栈指针,也就是栈顶地址。那么第一个参数呢,我们有理由怀疑我们的输入是第一个参数,正如pgase1中,根据函数名我们可以判断,读入六个数字。

可以看到他将第二个参数赋值给了rdx,然后下面的rsi+4和rsi+0x14,0x14==24,通过lea指令将距离栈顶第二个元素的地址和第七个元素的地址(末尾标志)传入了相关寄存器,注意这里的[rsi],因为前面提到过,rsi里存放的是栈顶地址,所以可以这么解释。这么复杂加上后面的sscanf大概猜测一下,就是将我们输入的6个数字放入首地址为rsp的内存空间中。我么可以查看一下mov esi,0x4025c3这条指令输入的十六个整型。

回到phase2函数,再调用完读取函数之后,进行了一次比较,如果输入的第一个数不是0x1则爆炸。继续往下,将输入的第二个数(【rsp+4】)送入rbp,将[rsp+0x18]送入rbp(栈帧寄存器),下面其实是个循环,通过比较指令和跳转指令来实现,add rbx,0x4这条指令相当于一个计数器。大致实现的逻辑是将第二个如放入rbx,将钱一个数字放入rax,将rax进行乘2操作,进行比较。其实只要看出这一题的循环,很多东西都是水到渠成的顺出来的。
1 2 3
| cmp rbx,rbp 75 e9 jne 400f17 <phase_2+0x1b> eb 0c jmp 400f3c <phase_2+0x40>
|
每比较一次都会对rbx+4,当rbx+4与我们设置的末尾表值相同时就会结束循环。0x4–>0x18,差为24,正好循环六次
1 2 3 4 5 6 7 8 9 10 11
| #include<stdio.h> int main() { int arr[6]={1}; for(int i=0;i<6;i++) { arr[i+1]=arr[i]*2; printf("%d ",arr[i]); } } ;1 2 4 8 16 32
|
phase_3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 0000000000400f43 <phase_3>: 400f43: 48 83 ec 18 sub rsp,0x18 400f47: 48 8d 4c 24 0c lea rcx,[rsp+0xc] ;参数4 0x00 400f4c: 48 8d 54 24 08 lea rdx,[rsp+0x8] ;参数3 400f51: be cf 25 40 00 mov esi,0x4025cf ;参数2查看之后是%d,诡异的是接收两个整形 400f56: b8 00 00 00 00 mov eax,0x0 ;应该是个计数器 400f5b: e8 90 fc ff ff call 400bf0 <__isoc99_sscanf@plt>;读取我们的input作为scand的参数 400f60: 83 f8 01 cmp eax,0x1 ;eax里的值和1比较,eax存放的是函数的返回值 400f63: 7f 05 jg 400f6a <phase_3+0x27> ;如果大于一则跳转 jg是由符号运算大于则跳转 400f65: e8 d0 04 00 00 call 40143a <explode_bomb> ;不大于一直接爆炸 400f6a: 83 7c 24 08 07 cmp DWORD PTR [rsp+0x8],0x7 ;调准到此处,与0x7进行比较 400f6f: 77 3c ja 400fad <phase_3+0x6a> ;无符号大于则跳转,爆炸 400f71: 8b 44 24 08 mov eax,DWORD PTR [rsp+0x8] 400f75: ff 24 c5 70 24 40 00 jmp QWORD PTR [rax*8+0x402470];用x/x查明之后,是机器码0xb9,对应的汇编指令是MOV cx,immed16 400f7c: b8 cf 00 00 00 mov eax,0xcf 400f81: eb 3b jmp 400fbe <phase_3+0x7b> 400f83: b8 c3 02 00 00 mov eax,0x2c3 400f88: eb 34 jmp 400fbe <phase_3+0x7b> 400f8a: b8 00 01 00 00 mov eax,0x100 400f8f: eb 2d jmp 400fbe <phase_3+0x7b> 400f91: b8 85 01 00 00 mov eax,0x185 400f96: eb 26 jmp 400fbe <phase_3+0x7b> 400f98: b8 ce 00 00 00 mov eax,0xce 400f9d: eb 1f jmp 400fbe <phase_3+0x7b> 400f9f: b8 aa 02 00 00 mov eax,0x2aa 400fa4: eb 18 jmp 400fbe <phase_3+0x7b> 400fa6: b8 47 01 00 00 mov eax,0x147 400fab: eb 11 jmp 400fbe <phase_3+0x7b> 400fad: e8 88 04 00 00 call 40143a <explode_bomb> 400fb2: b8 00 00 00 00 mov eax,0x0 400fb7: eb 05 jmp 400fbe <phase_3+0x7b> 400fb9: b8 37 01 00 00 mov eax,0x137 400fbe: 3b 44 24 0c cmp eax,DWORD PTR [rsp+0xc] 400fc2: 74 05 je 400fc9 <phase_3+0x86> 400fc4: e8 71 04 00 00 call 40143a <explode_bomb> 400fc9: 48 83 c4 18 add rsp,0x18 400fcd: c3 ret
|
通过传到esi里的参数我们可以看出,这次接受的输入是两个整形,下面一堆东西是属比较抽象,静态看实在有点头大,于是多下了几个断点一步一步来,输入2 2尝试一下。cmp eax,0x1,eax里保存的是返回值,只要我们的输入大于一个应该就能正常跳转,下面的比较没太看明白,和0x7进行比较,试一下就知道,其实是和我们的第一个输入进行比较,只要不大于7就能正常跳转,我们接着向下,一步一步调试来到了最后的比较,cmp eax,DWORD PTR [rsp+0xc],很容易想到[rsp+0xc]其实就是我们的第二个输入,这是只要我们查看一下寄存器的值,就知道如何进行比较
可以看到rax的值为707,推出之后重新运行,输入2 707果然正确。通过这题目的名字可以看出他考察的是分支,关键的没看懂的地方其实是jmp QWORD PTR [rax*8+0x402470],这一步大概率是根据我们的输入进行不同的跳转的函数。至于我们的输入为什么被放在了[rsp,0xc]和[rsp+0x8]中,我的猜测是这是函数的参数用来指定将接收的输入放置的位置。 __isoc99_sscanf@plt(rdi,rsi,n1,n2,n3…)其中参数rdi保存的是我们input里的输入,rsi保存的是%d之类的,以什么形式接收多少,后面就是放置的位置吧。
看了网上的题解之后,才明白这原来就是个switch分支啊,我们可以通过x命令来查看一下0x402470的内容,这是个跳转表,在《汇编语言》接触过
通过x/8xg
细说一下x命令,很好用。
1 2 3 4 5 6 7 8
| 格式 x/nfu addr n是正数表示要显示的数量 f该表的是输出格式,常用的有字符串s,十六进制x,十进制d,字符c u代表一多少个字节作为一个内存单元来显示,b=1byte,h=2 bytes,w=4 bytes,g=8 bytes addr代表内存单元地址 x/8xg 0x402470 这条指令的意思就是从内存地址0x402470处,以十六进制形式8个字节为一个单位显示8组数 为什么这里要用8呢,因为跳转那里是qword也就是两个字,一个字32为两个字就是8字节
|
phase_4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| 0000000000400fce <func4>: 400fce: 48 83 ec 08 sub rsp,0x8 400fd2: 89 d0 mov eax,edx ;0x0e 400fd4: 29 f0 sub eax,esi ;0x0 400fd6: 89 c1 mov ecx,eax 400fd8: c1 e9 1f shr ecx,0x1f ;右移0x1f 400fdb: 01 c8 add eax,ecx ; 400fdd: d1 f8 sar eax,1 ;算术右移 400fdf: 8d 0c 30 lea ecx,[rax+rsi*1] ; 400fe2: 39 f9 cmp ecx,edi 400fe4: 7e 0c jle 400ff2 <func4+0x24> ;有符号小于等于 400fe6: 8d 51 ff lea edx,[rcx-0x1] 400fe9: e8 e0 ff ff ff call 400fce <func4> ;调用自身 递归 400fee: 01 c0 add eax,eax 400ff0: eb 15 jmp 401007 <func4+0x39> 400ff2: b8 00 00 00 00 mov eax,0x0 400ff7: 39 f9 cmp ecx,edi 400ff9: 7d 0c jge 401007 <func4+0x39>;有符号大于等于 400ffb: 8d 71 01 lea esi,[rcx+0x1] 400ffe: e8 cb ff ff ff call 400fce <func4> 401003: 8d 44 00 01 lea eax,[rax+rax*1+0x1] 401007: 48 83 c4 08 add rsp,0x8 40100b: c3 ret
000000000040100c <phase_4>: 40100c: 48 83 ec 18 sub rsp,0x18 401010: 48 8d 4c 24 0c lea rcx,[rsp+0xc] ;第二个输入 401015: 48 8d 54 24 08 lea rdx,[rsp+0x8] ;第一个输入 40101a: be cf 25 40 00 mov esi,0x4025cf ;接收两个整型 40101f: b8 00 00 00 00 mov eax,0x0 401024: e8 c7 fb ff ff call 400bf0 <__isoc99_sscanf@plt> 401029: 83 f8 02 cmp eax,0x2 ;如果输入不为2就爆炸 40102c: 75 07 jne 401035 <phase_4+0x29> 40102e: 83 7c 24 08 0e cmp DWORD PTR [rsp+0x8],0xe ;第一个输入和0xe进行比较 401033: 76 05 jbe 40103a <phase_4+0x2e> ;无符号小于等于则跳转,所以我们的输入必须小于等于14 401035: e8 00 04 00 00 call 40143a <explode_bomb> 40103a: ba 0e 00 00 00 mov edx,0xe 40103f: be 00 00 00 00 mov esi,0x0 401044: 8b 7c 24 08 mov edi,DWORD PTR [rsp+0x8] ;------------函数传参 401048: e8 81 ff ff ff call 400fce <func4> ;调用func4 40104d: 85 c0 test eax,eax ;and操作不过不改变不敌寄存器的值,为0时标志寄存器置0 40104f: 75 07 jne 401058 <phase_4+0x4c> ;如果上面eax的值为0则不发生跳转(哦按段是否相等是检测其差是否为0) 401051: 83 7c 24 0c 00 cmp DWORD PTR [rsp+0xc],0x0 ;将第二个参数与0进行比较 401056: 74 05 je 40105d <phase_4+0x51> ;相等则成功,否则爆炸!!! 401058: e8 dd 03 00 00 call 40143a <explode_bomb> 40105d: 48 83 c4 18 add rsp,0x18 401061: c3 ret
|
看着汇编用c语言大致实现了一遍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<stdio.h> int func4(int di, int si, int dx) { int ax = dx; ax = ax - si; int cx = ax; cx = cx >> 0x1f; ax = ax + cx; ax = ax >> 1; cx = (ax + si); if (cx <= di) { ax = 0; if (cx >= di) return ax; else si = (cx + 1); ax=func4(di, si, dx); ax = (ax + ax + 1); return ax; } else dx = (cx - 1); ax = func4(di, si, dx); ax = ax+ax; return ax; } int main() { int i = 0; int g = 0; scanf_s("%d", &i); scanf_s("%d", &g); if (i <= 0xe) { printf("yes"); int x=func4(i, 0, 0xe); if (x != 0) printf("bomb!!"); else if (g == 0) printf("success!!!"); else printf("bomb!!!!!!!!"); } else printf("bomb"); return 0;
}
|
本来tmd早就可以结束的,在test那里出了点问题,test eax,eax; jne bomb 意思是如果返回值eax的值为零,那么爆炸,woc简直了,把c语言的判断写成了 if(ex==0)则爆炸。。。这一题的解题思路不同于上两个,是直接通过代码实现的,经过验证(0~14)其中可行的是0,1,3,7第二个输入是固定的0。
phase_5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| 0000000000401062 <phase_5>: 401062: 53 push rbx 401063: 48 83 ec 20 sub rsp,0x20 401067: 48 89 fb mov rbx,rdi 40106a: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28 401071: 00 00 401073: 48 89 44 24 18 mov QWORD PTR [rsp+0x18],rax 401078: 31 c0 xor eax,eax 40107a: e8 9c 02 00 00 call 40131b <string_length> 40107f: 83 f8 06 cmp eax,0x6 ;如果输入的字符串长度不等于6,则爆炸 401082: 74 4e je 4010d2 <phase_5+0x70> 401084: e8 b1 03 00 00 call 40143a <explode_bomb> 401089: eb 47 jmp 4010d2 <phase_5+0x70> 40108b: 0f b6 0c 03 movzx ecx,BYTE PTR [rbx+rax*1] ;将我们的输入逐字节传送到ecx 40108f: 88 0c 24 mov BYTE PTR [rsp],cl ;送入【rsp】 401092: 48 8b 14 24 mov rdx,QWORD PTR [rsp] ;送入rdx 401096: 83 e2 0f and edx,0xf ;将我们的输入and上0xf 401099: 0f b6 92 b0 24 40 00 movzx edx,BYTE PTR [rdx+0x4024b0] ;把该地址处的值存入edx,0扩展并传送(无符号扩展)到edx 4010a0: 88 54 04 10 mov BYTE PTR [rsp+rax*1+0x10],dl ;将修改过的输入传入指定位置 4010a4: 48 83 c0 01 add rax,0x1 ;eax+1 4010a8: 48 83 f8 06 cmp rax,0x6 ;eax的值与6进行比较 4010ac: 75 dd jne 40108b <phase_5+0x29> ;因为在前面eax已经置零,所以这里就是循环六次上面的操作 4010ae: c6 44 24 16 00 mov BYTE PTR [rsp+0x16],0x0 ;向指定位置送个0,像是结尾标志 4010b3: be 5e 24 40 00 mov esi,0x40245e ;将比较字符串“flyers”送入 4010b8: 48 8d 7c 24 10 lea rdi,[rsp+0x10] ; 4010bd: e8 76 02 00 00 call 401338 <strings_not_equal> ;如果不相等则为真返回1否则返回0 4010c2: 85 c0 test eax,eax 4010c4: 74 13 je 4010d9 <phase_5+0x77> ;如果返回值rax为0则进行跳转,否则爆炸 4010c6: e8 6f 03 00 00 call 40143a <explode_bomb> 4010cb: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0] 4010d0: eb 07 jmp 4010d9 <phase_5+0x77> 4010d2: b8 00 00 00 00 mov eax,0x0 4010d7: eb b2 jmp 40108b <phase_5+0x29> 4010d9: 48 8b 44 24 18 mov rax,QWORD PTR [rsp+0x18] ; 4010de: 64 48 33 04 25 28 00 xor rax,QWORD PTR fs:0x28 ;异或操作,实际上是进行比较,其实是进行栈溢出检测 4010e5: 00 00 4010e7: 74 05 je 4010ee <phase_5+0x8c> ;如果相等则异或结果为零跳转,成功。 4010e9: e8 42 fa ff ff call 400b30 <__stack_chk_fail@plt> 4010ee: 48 83 c4 20 add rsp,0x20 4010f2: 5b pop rbx 4010f3: c3 ret
|
这里其实是个多解题目,其实输入的是六个索引值。0x4024b0那里就是字符表(在字符表后发现了隐藏的炸弹好阴险),最后的栈溢出检测浪费了很长时间,应该就是将其和金丝雀值进行对比,通过调试也可以看出,栈是随机化的。
phase_6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| 00000000004010f4 <phase_6>: 4010f4: 41 56 push r14 4010f6: 41 55 push r13 4010f8: 41 54 push r12 4010fa: 55 push rbp 4010fb: 53 push rbx 4010fc: 48 83 ec 50 sub rsp,0x50 401100: 49 89 e5 mov r13,rsp 401103: 48 89 e6 mov rsi,rsp ;第二个参数 存放输入的首地址 401106: e8 51 03 00 00 call 40145c <read_six_numbers> 40110b: 49 89 e6 mov r14,rsp ;将栈顶地址赋值给r14 40110e: 41 bc 00 00 00 00 mov r12d,0x0 ; 401114: 4c 89 ed mov rbp,r13 ;将栈顶地址赋值给rbp 401117: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] ;把输入1送入eax 40111b: 83 e8 01 sub eax,0x1 ;将eax减去1 40111e: 83 f8 05 cmp eax,0x5 ;和5进行比较 401121: 76 05 jbe 401128 <phase_6+0x34> ;无符号小于等于跳转 401123: e8 12 03 00 00 call 40143a <explode_bomb> ;第一个输入不能大于6 401128: 41 83 c4 01 add r12d,0x1 ;给计数器加1 40112c: 41 83 fc 06 cmp r12d,0x6 ;与6进行比较 401130: 74 21 je 401153 <phase_6+0x5f> ;相等则跳转 401132: 44 89 e3 mov ebx,r12d ; 401135: 48 63 c3 movsxd rax,ebx ;符号扩展 401138: 8b 04 84 mov eax,DWORD PTR [rsp+rax*4];将输入送入eax 40113b: 39 45 00 cmp DWORD PTR [rbp+0x0],eax;与输入1进行对比 40113e: 75 05 jne 401145 <phase_6+0x51>;不相等跳转 401140: e8 f5 02 00 00 call 40143a <explode_bomb>;相等爆炸 401145: 83 c3 01 add ebx,0x1 ;2 401148: 83 fb 05 cmp ebx,0x5 ;比较,循环5次知道ebx=6 40114b: 7e e8 jle 401135 <phase_6+0x41> ;有符号小于等于跳转 40114d: 49 83 c5 04 add r13,0x4 ;r13指向第2个输入 401151: eb c1 jmp 401114 <phase_6+0x20> 401153: 48 8d 74 24 18 lea rsi,[rsp+0x18] ;第六个输入的后一个的地址 401158: 4c 89 f0 mov rax,r14 40115b: b9 07 00 00 00 mov ecx,0x7 401160: 89 ca mov edx,ecx 401162: 2b 10 sub edx,DWORD PTR [rax] 401164: 89 10 mov DWORD PTR [rax],edx 401166: 48 83 c0 04 add rax,0x4 40116a: 48 39 f0 cmp rax,rsi 40116d: 75 f1 jne 401160 <phase_6+0x6c> ;又是一个循环 40116f: be 00 00 00 00 mov esi,0x0 401174: eb 21 jmp 401197 <phase_6+0xa3> 401176: 48 8b 52 08 mov rdx,QWORD PTR [rdx+0x8] 40117a: 83 c0 01 add eax,0x1 40117d: 39 c8 cmp eax,ecx 40117f: 75 f5 jne 401176 <phase_6+0x82> 401181: eb 05 jmp 401188 <phase_6+0x94> 401183: ba d0 32 60 00 mov edx,0x6032d0 401188: 48 89 54 74 20 mov QWORD PTR [rsp+rsi*2+0x20],rdx 40118d: 48 83 c6 04 add rsi,0x4 401191: 48 83 fe 18 cmp rsi,0x18 401195: 74 14 je 4011ab <phase_6+0xb7> 401197: 8b 0c 34 mov ecx,DWORD PTR [rsp+rsi*1] 40119a: 83 f9 01 cmp ecx,0x1 40119d: 7e e4 jle 401183 <phase_6+0x8f> 40119f: b8 01 00 00 00 mov eax,0x1 4011a4: ba d0 32 60 00 mov edx,0x6032d0 4011a9: eb cb jmp 401176 <phase_6+0x82> 4011ab: 48 8b 5c 24 20 mov rbx,QWORD PTR [rsp+0x20] 4011b0: 48 8d 44 24 28 lea rax,[rsp+0x28] 4011b5: 48 8d 74 24 50 lea rsi,[rsp+0x50] 4011ba: 48 89 d9 mov rcx,rbx 4011bd: 48 8b 10 mov rdx,QWORD PTR [rax] 4011c0: 48 89 51 08 mov QWORD PTR [rcx+0x8],rdx 4011c4: 48 83 c0 08 add rax,0x8 4011c8: 48 39 f0 cmp rax,rsi 4011cb: 74 05 je 4011d2 <phase_6+0xde> 4011cd: 48 89 d1 mov rcx,rdx 4011d0: eb eb jmp 4011bd <phase_6+0xc9> 4011d2: 48 c7 42 08 00 00 00 mov QWORD PTR [rdx+0x8],0x0 4011d9: 00 4011da: bd 05 00 00 00 mov ebp,0x5 4011df: 48 8b 43 08 mov rax,QWORD PTR [rbx+0x8] 4011e3: 8b 00 mov eax,DWORD PTR [rax] 4011e5: 39 03 cmp DWORD PTR [rbx],eax 4011e7: 7d 05 jge 4011ee <phase_6+0xfa> 4011e9: e8 4c 02 00 00 call 40143a <explode_bomb> 4011ee: 48 8b 5b 08 mov rbx,QWORD PTR [rbx+0x8] 4011f2: 83 ed 01 sub ebp,0x1 4011f5: 75 e8 jne 4011df <phase_6+0xeb> 4011f7: 48 83 c4 50 add rsp,0x50 4011fb: 5b pop rbx 4011fc: 5d pop rbp 4011fd: 41 5c pop r12 4011ff: 41 5d pop r13 401201: 41 5e pop r14 401203: c3 ret
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
| #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { char arr[]="193456"; int rsp=0; int ebp; int ecx = 0; int edx = 0; int esi = 0; char* r14 = arr; char* rsp = arr; int r12d = 0; char* r13 = arr; jump401114:char* rbp = r13; int eax = *(r13 + 0); eax = eax - 1; int ebx = 0; if (eax <= 5) { goto jump4001128; jump4001128: r12d = r12d + 1; if (r12d == 6) { goto jump4001153; jump4001153: int rsi = 0; eax = *r14; ecx = 0x7; jump401160: edx = ecx; edx = edx - eax; eax = edx; eax = eax + 4; if (eax == rsi) { rsi = 0; goto jump401197; jump401197: ecx = *(rsp + rsi); if (ecx <= 1) { edx = 0x6032d0; goto jump401188; } else { eax = 1; edx = 0x6032d0; goto jump401176; jump401176: edx = edx + 0x8; eax = eax + 1; if (eax != ecx) { goto jump401176; } else { goto jump401188; jump401188: rsp = edx; rsi = rsi + 4; if (rsi == 0x18) { goto jump4011ab; jump4011ab: ebx = rsp+0x20; eax = rsp+0x28; rsi = rsp+0x50; ecx = ebx; jump4011bd: edx = eax; ecx = edx; eax = eax + 8; if (eax == rsi) { goto jump4011d2; jump4011d2: edx = 0; ebp = 5; jump4011df: eax=ebx; eax = eax; ebx = eax; if (ebx >= eax) { goto jump4011ee; jump4011ee: ebp = ebp - 1; if (ebp != 1) { goto jump4011df; } else { printf("w1n!!!_y0u_@r3_my_her0!!!!"); return 0; } } else { goto bomb; } } else { goto jump4011bd; } } else { ecx = rsp + 4; if (ecx <= 1) { goto jump401188; } else { eax = 1; edx = 0x6032d0; goto jump401176;
}
} } } } else { goto jump401160; } } else { ebx = r12d; jump401135:eax = ebx; eax = *(rsp + eax); if (*(rbp) == eax) { printf("401140 bomb!!!"); return 0; } else { goto jump401145; jump401145: ebx = ebx + 1; if (ebx <= 5) { goto jump401135; } else { r13 = r13 + 1; goto jump401114; } } } } else printf("bomb!"); return 0; printf("%c", *r13 ); return 0; bomb:printf("bomb!!!!!"); return 0;
}
|
这一题做的非常的。。。。。。。。怎么说呢,做完了看看别人的解题思路才发现这是个链表,我是直接把汇编还原成c语言,不是完全的还原,只是看着像的那种还原,这样做有一个好处是比直接看汇编简单一点,因为那个跳来跳去的,这样跳转到哪里,跳转条件至少清楚一点。第一层:检测六个数是否都小于等于6,第二层:用7减去原数据放在原始位置上,第三层:将0x6032d0处数据每隔8字节分别复制到栈中,这个复制顺序和我们的输入有关,比如说我们第一个输入是6,7-6=1,就是将0x6032d0+0*8处的数据复制到第一个位置,第二个输入是3,7-3=4,那么将0x6032d0+3*8处的数据复制到第二个位置。第四层:将复制到栈中的数据进行比较,大的在前。查看内存

后面是序号,前面是数据,从大到小排列是345612,这个顺序是被7减过后的,所以原来的是432165
这种做法算是比较粗野的一种,说一下这次做题中遇到的问题,首先是【】的问题,现在在知道了,出现[]一般是要访问内存了,其二注意数据的长度,有dw,qw,b啥的。还有,有时候看不懂的时候就带入具体的数据看一看,比如说第三层那里,看不懂它是将哪里的数据复制到哪里,最后带入654321这样的具体数据一眼就看出了它是如何操作的。
