mvc 网站建设,百度商标查询,关键词优化策略有哪些,百度热搜 百度指数说明
6关卡#xff0c;每个关卡需要输入相应的内容#xff0c;通过逆向工程来获取对应关卡的通过条件
准备工作
环境
需要用到gdb调试器
apt-get install gdb系统: Ubuntu 22.04
本实验会用到的gdb调试器的指令如下 r或者 run或者run filename 运行程序,run filename就…说明
6关卡每个关卡需要输入相应的内容通过逆向工程来获取对应关卡的通过条件
准备工作
环境
需要用到gdb调试器
apt-get install gdb系统: Ubuntu 22.04
本实验会用到的gdb调试器的指令如下 r或者 run或者run filename 运行程序,run filename就是用filename中的内容作为输入 b *address 在某个地址设置断点 d或delete 删除所有断点 d 断点号 删除指定断点 info b 查看所有断点信息 x/参数 地址 查看指针解引用后的值参数可以是s(字符串),d(十进制),x(十六进制),地址若是寄存器需要加上$ info register 或info r 查看所有寄存器的值 disas functionName 生成functionName的汇编代码 stepi 执行一个汇编指令 layout asm 窗口分为两部分上面是将要执行的汇编代码下面输入gdb调试命令 前置知识
寄存器的东西
这里面有一些寄存器的知识了解即可 %rsp (register stack pointer) 栈指针 %esi:通用寄存器长应用于指针或索引 %rax:存储函数的返回值,存储临时数据,系统调用号 %r12,%rbx:通用寄存器,参数传递的 %rbp(register base pointer):通常是当作基址指针来用
指令相关
test destination,source
destination和source可以是寄存器,内存地址,立即数.对两个操作数进行按位逻辑与操作,会更新下面寄存器的状态
Zero flag(ZF):若结果为0则设置为1,反之为0这个若为1表明两个操作数相等或者某个操作数等于0Sign flag (SF):若结果最高位为1,则为1,反之为0Overflow flag(OF):有符号数溢出则为1,反之为0Carry flag(CF):无符号数溢出则为1,反之为0Parity flag(PF):结果的低8位包含奇数个1则为1反之为0
je destination
若ZF为1则跳转到destination处
开干
Phase 1 字符串比较
终端输入gdb bomb 在输入disas phase_1,结果如下 0x0000000000400ee0 0: sub $0x8,%rsp // 把栈指针减少8给局部变量提供空间0x0000000000400ee4 4: mov $0x402400,%esi // 将0x402400存储到%esi中这个有可能是存放我们输入的值或者存放内置字符串的0x0000000000400ee9 9: call 0x401338 strings_not_equal//调用了函数strings_not_equal,估计是判断输入的字符串和内置字符串是否相同相同返回00x0000000000400eee 14: test %eax,%eax //判断%eax(函数strings_not_equal的返回值)是否为0若为0则ZF10x0000000000400ef0 16: je 0x400ef7 phase_123 // ZF为1就跳转反之顺序执行0x0000000000400ef2 18: call 0x40143a explode_bomb//拆弹失败炸弹爆炸0x0000000000400ef7 23: add $0x8,%rsp //回收栈指针0x0000000000400efb 27: ret 把断点打到0x0000000000400ee9的位置,开始run,随便输点东西
b *0x400ee9
run
x/s 0x402400 结果发现0x402400就是我们想要的东西:Border relations with Canada have never been better. 那么我们输入的东西到哪里去了?断点打到strings_not_equal里面,查看strings_not_equal汇编代码在gdb中输入disas strings_not_equal
Dump of assembler code for function strings_not_equal:0x0000000000401338 0: push %r120x000000000040133a 2: push %rbp 0x000000000040133b 3: push %rbx0x000000000040133c 4: mov %rdi,%rbx0x000000000040133f 7: mov %rsi,%rbp0x0000000000401342 10: call 0x40131b string_length0x0000000000401347 15: mov %eax,%r12d0x000000000040134a 18: mov %rbp,%rdi0x000000000040134d 21: call 0x40131b string_length0x0000000000401352 26: mov $0x1,%edx0x0000000000401357 31: cmp %eax,%r12d0x000000000040135a 34: jne 0x40139b strings_not_equal990x000000000040135c 36: movzbl (%rbx),%eax0x000000000040135f 39: test %al,%al0x0000000000401361 41: je 0x401388 strings_not_equal800x0000000000401363 43: cmp 0x0(%rbp),%al0x0000000000401366 46: je 0x401372 strings_not_equal580x0000000000401368 48: jmp 0x40138f strings_not_equal870x000000000040136a 50: cmp 0x0(%rbp),%al0x000000000040136d 53: nopl (%rax)0x0000000000401370 56: jne 0x401396 strings_not_equal940x0000000000401372 58: add $0x1,%rbx
断点打到0x401338的位置运行程序 不停的stepi知道运行到了第一次调用string_length函数,字符串一般来说是需要一个基址的,所以找能充当基指指针的寄存器,下面是试探过程
x/s $rbp //这个是内置字符串
x/s $rbx //这个是我们输入的字符串答案 Border relations with Canada have never been better. 进入strings_not_equal函数才能看到我们输入的字符串是保存在%rbx这个寄存器当中的
phase_2 循环
disas phase_2得到如下代码(我分成了两部分,这个是前面一部分) 0x0000000000400efc 0: push %rbp 0x0000000000400efd 1: push %rbx 0x0000000000400efe 2: sub $0x28,%rsp0x0000000000400f02 6: mov %rsp,%rsi0x0000000000400f05 9: call 0x40145c read_six_numbers //这里从名字可以知道要输入6个数字那么是整形还是浮点数呢?这里先输入6个整形试试输入stepi进入到read_six_numbers函数中disas read_six_numbers得到下面的代码
Dump of assembler code for function read_six_numbers:0x000000000040145c 0: sub $0x18,%rsp // 这个不用管0x0000000000401460 4: mov %rsi,%rdx0x0000000000401463 7: lea 0x4(%rsi),%rcx0x0000000000401467 11: lea 0x14(%rsi),%rax0x000000000040146b 15: mov %rax,0x8(%rsp)0x0000000000401470 20: lea 0x10(%rsi),%rax0x0000000000401474 24: mov %rax,(%rsp)0x0000000000401478 28: lea 0xc(%rsi),%r90x000000000040147c 32: lea 0x8(%rsi),%r80x0000000000401480 36: mov $0x4025c3,%esi //看看%esi寄存器的内容0x0000000000401485 41: mov $0x0,%eax0x000000000040148a 46: call 0x400bf0 __isoc99_sscanfplt0x000000000040148f 51: cmp $0x5,%eax0x0000000000401492 54: jg 0x401499 read_six_numbers610x0000000000401494 56: call 0x40143a explode_bomb0x0000000000401499 61: add $0x18,%rsp0x000000000040149d 65: ret 注意这一行mov $0x4025c3,%esi,因为字符串是不可变的地址固定死了所以找立即数 esi寄存器的内容为 六个整形数据猜测是对的我们输入1 2 3 4 5 6试试 继续阅读phase_2后部分的代码
0x0000000000400f05 9: call 0x40145c read_six_numbers0x0000000000400f0a 14: cmpl $0x1,(%rsp) // (%rsp)*rsp 就是我们输入的第一个数字10x0000000000400f0e 18: je 0x400f30 phase_252 // 判断是否相等相等就跳转0x0000000000400f10 20: call 0x40143a explode_bomb //否则就炸了0x0000000000400f15 25: jmp 0x400f30 phase_2520x0000000000400f17 27: mov -0x4(%rbx),%eax // eax保存的是我们输入的第一个数把rbx存放的值减40x0000000000400f1a 30: add %eax,%eax // eax*20x0000000000400f1c 32: cmp %eax,(%rbx) // 比较eax的值和rbx(就是第二个值)是否相等0x0000000000400f1e 34: je 0x400f25 phase_241 // 相等就跳转0x0000000000400f20 36: call 0x40143a explode_bomb //反之爆炸0x0000000000400f25 41: add $0x4,%rbx // rbx 保存的是第三个值0x0000000000400f29 45: cmp %rbp,%rbx // 看看是否遍历完了0x0000000000400f2c 48: jne 0x400f17 phase_2270x0000000000400f2e 50: jmp 0x400f3c phase_2640x0000000000400f30 52: lea 0x4(%rsp),%rbx // 0x4(%rsp)我们输入的第二个数,加4的原因是因为int类型是4个字节0x0000000000400f35 57: lea 0x18(%rsp),%rbp// 0x18转换为十进制数为24,也就是第6个数字后面的第一个存储单元0x0000000000400f3a 62: jmp 0x400f17 phase_2270x0000000000400f3c 64: add $0x28,%rsp意思如下 程序开始时,将 (%rsp) 的值与立即数$0x1进行比较所以第一个输入数必须为1跳转至400f30用lea指令分别加载%rsp4和%rsp24对应的地址到%rbx和%rbp因为int型数据占4个字节所以%rbx和%rbp分别存放第2个输入数的地址和第6个输入数的后一块的地址 后跳转至400f17此时(%rbx-4)对应的值即(%rsp)对应的值将其存放值%eax中将该值*2后与(%rbx)对应的值即第二个输入值进行比较即后一个数是前一个数的2倍所以第二个输入值必须为2后跳转至400f25得到%rbx%rbx4与%rbp进行比较%rbx对应的值(地址)是否为%rbp对应的值(地址)若不相等则又跳转至400f17重复操作若相等则跳转至400f3c结束循环可知这是一个循环操作看是否比较完6个数。
循环中寄存器对应的值为:
%rbx%rbp%eax%rsp4%rsp24(%rsp)*22%rsp8(%rsp)*24%rsp12(%rsp)*28%rsp16(%rsp)*216%rsp20(%rsp)*232%rsp24
c代码如下
int main(){int[] array new int[6];for(int i1;i6;i)array[i]array[i-1]*2;return 0;
}答案 1 2 4 8 16 32 输入的第一个数时保存在(%rsp)中
phase_3 分支语句
对汇编代码进行分析
Dump of assembler code for function phase_3:0x0000000000400f43 0: sub $0x18,%rsp0x0000000000400f47 4: lea 0xc(%rsp),%rcx //这个可能是存储第二个数的0x0000000000400f4c 9: lea 0x8(%rsp),%rdx//这个可能是存储第一个数的0x0000000000400f51 14: mov $0x4025cf,%esi //出现立即数了,后面调用了scanf这里应该是初始化的在gdb 中用x/s $esi 可以得到 %d %d0x0000000000400f56 19: mov $0x0,%eax0x0000000000400f5b 24: call 0x400bf0 __isoc99_sscanfplt0x0000000000400f60 29: cmp $0x1,%eax//%eax存储函数的返回值的scanf的函数返回值就是输入数据的个数0x0000000000400f63 32: jg 0x400f6a phase_339//若大于1就跳转0x0000000000400f65 34: call 0x40143a explode_bomb//否则就爆炸0x0000000000400f6a 39: cmpl $0x7,0x8(%rsp)0x0000000000400f6f 44: ja 0x400fad phase_3106//无符号大于则跳转跳转就爆炸了所以第一个数必须小于7可以等于,但是不能是负数0x0000000000400f71 46: mov 0x8(%rsp),%eax//%eax存储的是输入的第一个数0x0000000000400f75 50: jmp *0x402470(,%rax,8) //*0x402470 124,通过x/d 0x402470得到, rax是64位的eax是32位的就是说eax是rax的低32位这里应该是124%rax*8(%rax就是我们输入的第一个数)来实现跳转。0x0000000000400f7c 57: mov $0xcf,%eax0x0000000000400f81 62: jmp 0x400fbe phase_31230x0000000000400f83 64: mov $0x2c3,%eax0x0000000000400f88 69: jmp 0x400fbe phase_31230x0000000000400f8a 71: mov $0x100,%eax0x0000000000400f8f 76: jmp 0x400fbe phase_31230x0000000000400f91 78: mov $0x185,%eax0x0000000000400f96 83: jmp 0x400fbe phase_31230x0000000000400f98 85: mov $0xce,%eax
--Type RET for more, q to quit, c to continue without paging--0x0000000000400f9d 90: jmp 0x400fbe phase_31230x0000000000400f9f 92: mov $0x2aa,%eax0x0000000000400fa4 97: jmp 0x400fbe phase_31230x0000000000400fa6 99: mov $0x147,%eax0x0000000000400fab 104: jmp 0x400fbe phase_31230x0000000000400fad 106: call 0x40143a explode_bomb0x0000000000400fb2 111: mov $0x0,%eax0x0000000000400fb7 116: jmp 0x400fbe phase_31230x0000000000400fb9 118: mov $0x137,%eax0x0000000000400fbe 123: cmp 0xc(%rsp),%eax0x0000000000400fc2 127: je 0x400fc9 phase_31340x0000000000400fc4 129: call 0x40143a explode_bomb0x0000000000400fc9 134: add $0x18,%rsp0x0000000000400fcd 138: ret
End of assembler dump.第一个数的范围是在[0,7]之间开始试探 n10,跳转到0x400f7c,若n2!0xcf,则爆炸 n11,跳转到0x400fb9,若n2!0x137,则爆炸 n12,跳转到0x400f83,若n2!0x2c3,则爆炸 n13,跳转到0x400f8a,若n2!0x100,则爆炸 n14,跳转到0x400f91,若n2!0x185,则爆炸 n15,跳转到0x400f98,若n2!0xce,则爆炸 n16,跳转到0x400f9f,若n2!0x2aa,则爆炸 n17,跳转到0x400fa6,若n2!0x147,则爆炸
c代码
void phase_3(char* input){//0x8(%rsp) 0xc(%rsp)int n1,n2;//res存放返回输入数据的个数int res sscanf(input,%d %d,n1,n2);if(res1)explode_bomb();switch(n1){case 0:if(n2!0xcf)explode_bomb();break;case 1:if(n2!0x137)explode_bomb();break;case 2:if(n2!0x2c3)explode_bomb();break;case 3:if(n2!0x100)explode_bomb();break;case 4:if(n2!0x185)explode_bomb();break;case 5:if(n2!0xce)explode_bomb();break;case 6:if(n2!0x2aa)explode_bomb();break;case 7:if(n2!0x147)explode_bomb();break;}
}答案
0 2071 3112 7073 2564 3895 2066 6827 327
phase_4 递归
分析汇编代码
Dump of assembler code for function phase_4:0x000000000040100c 0: sub $0x18,%rsp # 给局部变量腾出空间0x0000000000401010 4: lea 0xc(%rsp),%rcx # 我们输入的第二个数0x0000000000401015 9: lea 0x8(%rsp),%rdx # 我们输入的第一个数0x000000000040101a 14: mov $0x4025cf,%esi # 通过x/s 0x4025cf 可以得到是格式化字符串%d %d0x000000000040101f 19: mov $0x0,%eax # 这个就不说了0x0000000000401024 24: call 0x400bf0 __isoc99_sscanfplt # 调用了scanf函数0x0000000000401029 29: cmp $0x2,%eax # 如果输入的数字个数不等于2就爆炸了0x000000000040102c 32: jne 0x401035 phase_4410x000000000040102e 34: cmpl $0xe,0x8(%rsp) # 比较第一个数与14的大小若小于就跳转若大于就爆炸第一个数的范围[0,14]0x0000000000401033 39: jbe 0x40103a phase_4460x0000000000401035 41: call 0x40143a explode_bomb0x000000000040103a 46: mov $0xe,%edx 0x000000000040103f 51: mov $0x0,%esi0x0000000000401044 56: mov 0x8(%rsp),%edi0x0000000000401048 60: call 0x400fce func4 #调用func4 应该是需要%edx %esi %edi这几个参数0x000000000040104d 65: test %eax,%eax # 按位逻辑与操作0x000000000040104f 67: jne 0x401058 phase_476 # 如果%eax不等于0,则跳转至爆炸0x0000000000401051 69: cmpl $0x0,0xc(%rsp) #比较第二个数与0的大小关系0x0000000000401056 74: je 0x40105d phase_481 #相等就跳转结尾0x0000000000401058 76: call 0x40143a explode_bomb #不相等就爆炸0x000000000040105d 81: add $0x18,%rsp0x0000000000401061 85: ret 接下来看看func4干了什么
Dump of assembler code for function func4:0x0000000000400fce 0: sub $0x8,%rsp # 为局部变量腾出空间0x0000000000400fd2 4: mov %edx,%eax # 0xe0x0000000000400fd4 6: sub %esi,%eax # 0xe-0x00x0000000000400fd6 8: mov %eax,%ecx # ecx 0xe0x0000000000400fd8 10: shr $0x1f,%ecx # %ecx的值逻辑右移31位11103114/2^31 00x0000000000400fdb 13: add %ecx,%eax # ecx ecx eax 0 e0x0000000000400fdd 15: sar %eax # 算数右移1位 %eax %eax /2 0xe/2 7 0x0000000000400fdf 17: lea (%rax,%rsi,1),%ecx # %eax是%rax的低32位%esi是%rsi的低32位 %rax %rsi * 1 7 07 %ecx70x0000000000400fe2 20: cmp %edi,%ecx # %edi保存的是输入的第一个数字 %ecx 7 0x0000000000400fe4 22: jle 0x400ff2 func4360x0000000000400fe6 24: lea -0x1(%rcx),%edx # 7 -1 edx60x0000000000400fe9 27: call 0x400fce func4 # 递归调用0x0000000000400fee 32: add %eax,%eax # eax*20x0000000000400ff0 34: jmp 0x401007 func457 # 出口0x0000000000400ff2 36: mov $0x0,%eax # %eax 00x0000000000400ff7 41: cmp %edi,%ecx # n1, 7 0x0000000000400ff9 43: jge 0x401007 func457 # n17跳转到func7 出口0x0000000000400ffb 45: lea 0x1(%rcx),%esi # ecx是rcx的低32位 0x17esi8 0x0000000000400ffe 48: call 0x400fce func4 # 跳转0x0000000000401003 53: lea 0x1(%rax,%rax,1),%eax # 0x1 func4func4eax0x0000000000401007 57: add $0x8,%rsp0x000000000040100b 61: ret
看看这个 0x0000000000401051 69: cmpl $0x0,0xc(%rsp) #比较第二个数与0的大小关系0x0000000000401056 74: je 0x40105d phase_481 #相等就跳转结尾0x000000000040104d 65: test %eax,%eax # 按位逻辑与操作0x000000000040104f 67: jne 0x401058 phase_476 # 如果%eax不等于0,则跳转至爆炸所以n2必须等于0n1的值要让函数func4的返回值为4且n1∈[0,14]
//a: %edi b:%esi c:%edx d: %ecx e:%eax
int func4(int a,int b,int c)
{//a in %rdi,b in %rsi,c in %rdx,e in %rax,d in %ecx//y的初始值为0z的初始值为14int tc-b;int de31;e(ed)1;deb;if(ka){cd-1;func4(a,b,c);e*2;return e;}else{e0;if(da){bda1;func4(a,b,c);e2*e1;return e;}elsereturn e; //要使返回值e%eax为0其中一个答案为ad7}
}分析可得一个答案 7 0 其余的挨个带进去试
答案 7 0 phase_5 指针 字符串比较
phase_5的汇编代码
Dump of assembler code for function phase_5:0x0000000000401062 0: push %rbx0x0000000000401063 1: sub $0x20,%rsp0x0000000000401067 5: mov %rdi,%rbx #%rbx存放我们输入的字符串地址0x000000000040106a 8: mov %fs:0x28,%rax # 栈破坏检测,csapp P199页(金丝雀值),即在栈帧的任何局部缓冲区与栈状态之间存储一个值,在程序返回前检查该值若该值发生变化程序提前终止0x0000000000401073 17: mov %rax,0x18(%rsp) 0x0000000000401078 22: xor %eax,%eax # 异或清零0x000000000040107a 24: call 0x40131b string_length 0x000000000040107f 29: cmp $0x6,%eax0x0000000000401082 32: je 0x4010d2 phase_5112 # 输入的字符个数必须等于6否则爆炸0x0000000000401084 34: call 0x40143a explode_bomb0x0000000000401089 39: jmp 0x4010d2 phase_5112 0x000000000040108b 41: movzbl (%rbx,%rax,1),%ecx # rbx就是我们输入的字符串地址 翻译成语句:%ecx %rbx %rax*1 %rbx0*1%rbx我们输入的第一个字符0x000000000040108f 45: mov %cl,(%rsp) # %cl是%ecx的低8位 0x0000000000401092 48: mov (%rsp),%rdx # %rdx保存我们输入的一位字符0x0000000000401096 52: and $0xf,%edx # edx是rdx的低32位取出低4位0x0000000000401099 55: movzbl 0x4024b0(%rdx),%edx # 0x40240b0是字符串maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?的基址设该字符串为str# edx str[rdx]0x00000000004010a0 62: mov %dl,0x10(%rsp,%rax,1) # %dl是rdx的低4位0x00000000004010a4 66: add $0x1,%rax # 加一0x00000000004010a8 70: cmp $0x6,%rax # 和6相比0x00000000004010ac 74: jne 0x40108b phase_541 # 不等于6跳转0x00000000004010ae 76: movb $0x0,0x16(%rsp)0x00000000004010b3 81: mov $0x40245e,%esi #0x40245e是字符串flyers的地址用立即数来表示的应该是内置的0x00000000004010b8 86: lea 0x10(%rsp),%rdi # rdi是aduier的地址 结合下面的strings_not_equal函数来看应该是要把我们输入的东西和flyers比较由于我们输入的是abcdef,a的ASCII码是97低4位是0001所以取得是索引为1的,接下来就是要改变我们输入的东西让其在str中截取得到的字符串为flyers即可0x00000000004010bd 91: call 0x401338 strings_not_equal0x00000000004010c2 96: test %eax,%eax 0x00000000004010c4 98: je 0x4010d9 phase_5119 #相等跳转0x00000000004010c6 100: call 0x40143a explode_bomb #不相等就炸0x00000000004010cb 105: nopl 0x0(%rax,%rax,1)0x00000000004010d0 110: jmp 0x4010d9 phase_51190x00000000004010d2 112: mov $0x0,%eax # %eax清零0x00000000004010d7 117: jmp 0x40108b phase_541 0x00000000004010d9 119: mov 0x18(%rsp),%rax0x00000000004010de 124: xor %fs:0x28,%rax0x00000000004010e7 133: je 0x4010ee phase_51400x00000000004010e9 135: call 0x400b30 __stack_chk_failplt0x00000000004010ee 140: add $0x20,%rsp0x00000000004010f2 144: pop %rbx0x00000000004010f3 145: ret
所以过关条件就是通过我们输入的六个字符的ASCII码的低4位作为索引,取切maduier那一大串的东西且返回的结果必须是flyers 下面是flyers中各字符在maduier中的位置
字符位置f9I15y14e5r6s7
找到六个字符他们的ASCII码值的低4位要是表格中的(从上到下依次符合即可) 接下来就是找字符
答案
不唯一 9?uvw IONEFG phase6 链表
这个我确实是很蒙我尽力记录清楚 第一部分
Dump of assembler code for function phase_6:0x00000000004010f4 0: push %r140x00000000004010f6 2: push %r130x00000000004010f8 4: push %r120x00000000004010fa 6: push %rbp0x00000000004010fb 7: push %rbx0x00000000004010fc 8: sub $0x50,%rsp0x0000000000401100 12: mov %rsp,%r130x0000000000401103 15: mov %rsp,%rsi0x0000000000401106 18: call 0x40145c read_six_numbers0x000000000040110b 23: mov %rsp,%r14 # 保存我们输入的数的0x000000000040110e 26: mov $0x0,%r12d # 0x0000000000401114 32: mov %r13,%rbp 0x0000000000401117 35: mov 0x0(%r13),%eax # %eax nums[0]0x000000000040111b 39: sub $0x1,%eax # %eax-10x000000000040111e 42: cmp $0x5,%eax # eax-150x0000000000401121 45: jbe 0x401128 phase_6520x0000000000401123 47: call 0x40143a explode_bomb0x0000000000401128 52: add $0x1,%r12d # r12d10x000000000040112c 56: cmp $0x6,%r12d 0x0000000000401130 60: je 0x401153 phase_695 0x0000000000401132 62: mov %r12d,%ebx # %ebx 1 退出循环的条件是遍历完6个数字0x0000000000401135 65: movslq %ebx,%rax # rax 10x0000000000401138 68: mov (%rsp,%rax,4),%eax # eax 2 拿到下一个元素给eax 4是int类型的4个字节0x000000000040113b 71: cmp %eax,0x0(%rbp) # nums[i] !nums[0]0x000000000040113e 74: jne 0x401145 phase_6810x0000000000401140 76: call 0x40143a explode_bomb 0x0000000000401145 81: add $0x1,%ebx # ebx20x0000000000401148 84: cmp $0x5,%ebx # ebx50x000000000040114b 87: jle 0x401135 phase_665 # 这个循环是判断数组运算是否相等0x000000000040114d 89: add $0x4,%r130x0000000000401151 93: jmp 0x401114 phase_6320x0000000000401153 95: lea 0x18(%rsp),%rsi0x0000000000401158 100: mov %r14,%rax0x000000000040115b 103: mov $0x7,%ecx0x0000000000401160 108: mov %ecx,%edx0x0000000000401162 110: sub (%rax),%edx0x0000000000401164 112: mov %edx,(%rax)0x0000000000401166 114: add $0x4,%rax伪代码
r14 rsp nums[0]
rbp r13 nums[0]
eax nums[0]
eax -1
for(r12d 0;r12d6;r12d){
if(eax6)explode_bomb();
r12dif(r12d6)breakebx r12dfor(ebxr12d;ebx5;ebx){rax nums[rbx]if(nums[rbx] ! rbp){ebx;}else explode_bomb();
}
r13; //(这里是加1但是反应在内存上是加上了1*int类型所占的字节)
}
}也就是说每个数字必须6,且6个数字互不相等 第二部分 0x0000000000401153 95: lea 0x18(%rsp),%rsi # rsi 0 ,判定是否遍历完了第六个元素后面的那个存储单元没有值那就是00x1824是因为我们有六个元素每个元素4个字节拿到最后一个元素后要地址(0x14)要4所以是0x180x0000000000401158 100: mov %r14,%rax # *r14 5 nums[0]0x000000000040115b 103: mov $0x7,%ecx # ecx 70x0000000000401160 108: mov %ecx,%edx # edx ecx 70x0000000000401162 110: sub (%rax),%edx # edx edx - *rax 7 - *rax0x0000000000401164 112: mov %edx,(%rax) # *rax edx0x0000000000401166 114: add $0x4,%rax # rax4 获取下一个元素0x000000000040116a 118: cmp %rsi,%rax # rax num[1]7-num[1]0x000000000040116d 121: jne 0x401160 phase_6108rsi 0;//实际上是nums[6]
ecx 7;
for(rax0;rax6;rax) //底层里面nums[6]0,要是不好理解可以把中间的语句换成rax!rsi,应该好理解点nums[rax]7-nums[rax];
第三部分 第三部分中有一条语句 0x0000000000401183 143: mov $0x6032d0,%edx 通过x/30来查看 0x6032d0发现 这是一个链表,但是前面的14c不知道是什么东西结点如下
struct node{int value;int number;node* next;
}0x000000000040116f 123: mov $0x0,%esi # esi 0 0x0000000000401174 128: jmp 0x401197 phase_61630x0000000000401176 130: mov 0x8(%rdx),%rdx # 是一个node类型 rdx 当前结点的next值0x000000000040117a 134: add $0x1,%eax # eax 10x000000000040117d 137: cmp %ecx,%eax 0x000000000040117f 139: jne 0x401176 phase_6130 # 不相等继续遍历rdx最终指向链表的第%eax个结点0x0000000000401181 141: jmp 0x401188 phase_61480x0000000000401183 143: mov $0x6032d0,%edx # 0x6032d0是node1的地址0x0000000000401188 148: mov %rdx,0x20(%rsp,%rsi,2) # (rsp32rsi*2)rdx0x000000000040118d 153: add $0x4,%rsi # rsi4 0x0000000000401191 157: cmp $0x18,%rsi # 240x0000000000401195 161: je 0x4011ab phase_61830x0000000000401197 163: mov (%rsp,%rsi,1),%ecx # ecx *(rsp rsi) 0x000000000040119a 166: cmp $0x1,%ecx # ecx10x000000000040119d 169: jle 0x401183 phase_61430x000000000040119f 171: mov $0x1,%eax # eax 10x00000000004011a4 176: mov $0x6032d0,%edx # 前面的那个立即数是一个node类型的指针,x/30 0x6032d0查看他附近的30个字节0x00000000004011a9 181: jmp 0x401176 phase_6130这部分我很蒙 第四部分
for(int i6;i1;i--)node[i].nextnode[i-1]; 183~2200x00000000004011ab 183: mov 0x20(%rsp),%rbx # rbx (0x20rsp)0x00000000004011b0 188: lea 0x28(%rsp),%rax # rax (0x28rsp)0x00000000004011b5 193: lea 0x50(%rsp),%rsi # rsi (0x50rsp) 0x50是链表的尾端0x00000000004011ba 198: mov %rbx,%rcx 0x00000000004011bd 201: mov (%rax),%rdx # rdx *rax0x00000000004011c0 204: mov %rdx,0x8(%rcx) //栈0x00000000004011c4 208: add $0x8,%rax0x00000000004011c8 212: cmp %rsi,%rax0x00000000004011cb 215: je 0x4011d2 phase_62220x00000000004011cd 217: mov %rdx,%rcx0x00000000004011d0 220: jmp 0x4011bd phase_6201# 这里改一下输入,改成6 5 4 3 2 10x00000000004011d2 222: movq $0x0,0x8(%rdx) # 把最后一个结点的next域赋值为00x00000000004011da 230: mov $0x5,%ebp # ebp 50x00000000004011df 235: mov 0x8(%rbx),%rax # 指向重排后的第i个(1i5)0x00000000004011e3 239: mov (%rax),%eax # 0x00000000004011e5 241: cmp %eax,(%rbx) # rbx指向的是重排后的第i1个 第1个大于等于第二个,要呈现单调递减的顺序而且比较的是类似于权重0x00000000004011e7 243: jge 0x4011ee phase_6250 # 必须要单调递减0x00000000004011e9 245: call 0x40143a explode_bomb0x00000000004011ee 250: mov 0x8(%rbx),%rbx0x00000000004011f2 254: sub $0x1,%ebp0x00000000004011f5 257: jne 0x4011df phase_62350x00000000004011f7 259: add $0x50,%rsp0x00000000004011fb 263: pop %rbx0x00000000004011fc 264: pop %rbp0x00000000004011fd 265: pop %r120x00000000004011ff 267: pop %r130x0000000000401201 269: pop %r140x0000000000401203 271: ret 梳理 1.输入六个数(6且互不相同) 2.nums[i]7-nums[i] 7 -nums[0]nums[0] 7 -nums[1]nums[1] 7 -nums[2]nums[2] 7 -nums[3]nums[3] 7 -nums[4]nums[4] 7 -nums[5]nums[5] 1 2 3 4 5 6 332 168 924 691 477 443 这个是各个结点的权重,
根据nums[i]的内容对node进行重排确保重排后的各个结点的权重呈单调递减 答案: 4 3 2 1 6 5 finish 明天试试隐藏关