一、汇编源码
阶段 1:字符串比较
08048b80 <phase_1>:
8048b80: 55 push %ebp
8048b81: 89 e5 mov %esp,%ebp
8048b83: 83 ec 08 sub $0x8,%esp
8048b86: c7 44 24 04 18 99 04 movl $0x8049918,0x4(%esp)
8048b8d: 08
8048b8e: 8b 45 08 mov 0x8(%ebp),%eax
8048b91: 89 04 24 mov %eax,(%esp)
8048b94: e8 c6 04 00 00 call 804905f <strings_not_equal>
8048b99: 85 c0 test %eax,%eax
//判断返回值是否为0,如果为0,那么test将eax=0,否则为1
8048b9b: 74 05 je 8048ba2 <phase_1+0x22>
8048b9d: e8 84 0a 00 00 call 8049626 <explode_bomb>
8048ba2: c9 leave
8048ba3: c3 ret
阶段 2:循环
08048ba4 <phase_2>:
8048ba4: 55 push %ebp
8048ba5: 89 e5 mov %esp,%ebp
8048ba7: 83 ec 28 sub $0x28,%esp
8048baa: 8d 45 e4 lea -0x1c(%ebp),%eax
8048bad: 89 44 24 04 mov %eax,0x4(%esp)
8048bb1: 8b 45 08 mov 0x8(%ebp),%eax
8048bb4: 89 04 24 mov %eax,(%esp)
8048bb7: e8 10 04 00 00 call 8048fcc <read_six_numbers>
8048bbc: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%ebp)
8048bc3: eb 1e jmp 8048be3 <phase_2+0x3f>
8048bc5: 8b 45 fc mov -0x4(%ebp),%eax
8048bc8: 8b 54 85 e4 mov -0x1c(%ebp,%eax,4),%edx
8048bcc: 8b 45 fc mov -0x4(%ebp),%eax
8048bcf: 48 dec %eax
//--
8048bd0: 8b 44 85 e4 mov -0x1c(%ebp,%eax,4),%eax
8048bd4: 83 c0 05 add $0x5,%eax
//%eax+5
8048bd7: 39 c2 cmp %eax,%edx
8048bd9: 74 05 je 8048be0 <phase_2+0x3c>
//%edx-%eax==0时跳转,否则爆炸
8048bdb: e8 46 0a 00 00 call 8049626 <explode_bomb>
8048be0: ff 45 fc incl -0x4(%ebp)
//++
8048be3: 83 7d fc 05 cmpl $0x5,-0x4(%ebp)
8048be7: 7e dc jle 8048bc5 <phase_2+0x21>
//-0x4(%ebp) ≤ 5跳转
8048be9: c9 leave
8048bea: c3 ret
初始化:
push %ebp 和 mov %esp,%ebp 是典型的函数入口点,它们保存了旧的基本指针(base pointer)并设置新的基本指针。
sub $0x28,%esp 减小栈指针,为局部变量预留空间。
读取输入:
使用 lea -0x1c(%ebp),%eax 创建一个指向局部变量数组的指针。
读取一个参数(可能是一个文件描述符或类似的东西)并使用 read_six_numbers 函数从该源读取6个数字。
检查数字:
初始化一个循环计数器(在 -0x4(%ebp) 处)。
使用 jmp 8048be3 跳转到循环的末尾检查部分。
在循环内部,代码从数组中取出两个相邻的数字(当前索引和下一个索引),比较它们(下一个数字是否比当前数字大5)。
如果条件不满足(即 je 8048be0 的 je 分支不执行),则调用 explode_bomb 函数,这很可能是一个错误处理函数,用于在检测到错误时“爆炸”或终止程序。
如果条件满足,则递增循环计数器并继续下一个迭代。
循环结束检查:
使用 cmpl $0x5,-0x4(%ebp) 检查是否已检查了5个数字对(即,是否检查了6个数字中的每一对相邻数字)。
如果已经检查完5对数字,则退出循环。
退出函数:
使用 leave 和 ret 指令退出函数。
简而言之,此函数读取6个数字,并检查每对相邻的数字是否满足“下一个数字比当前数字大5”的条件。如果任何一对数字不满足此条件,则程序会调用 explode_bomb 函数。如果所有对都满足条件,则函数正常返回。
阶段 3:条件/分支
08048beb <phase_3>:
8048beb: 55 push %ebp
8048bec: 89 e5 mov %esp,%ebp
8048bee: 83 ec 28 sub $0x28,%esp
8048bf1: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp)
8048bf8: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
8048bff: 8d 45 f0 lea -0x10(%ebp),%eax
8048c02: 89 44 24 0c mov %eax,0xc(%esp)
8048c06: 8d 45 f4 lea -0xc(%ebp),%eax
8048c09: 89 44 24 08 mov %eax,0x8(%esp)
8048c0d: c7 44 24 04 47 99 04 movl $0x8049947,0x4(%esp)
8048c14: 08
8048c15: 8b 45 08 mov 0x8(%ebp),%eax
8048c18: 89 04 24 mov %eax,(%esp)
8048c1b: e8 48 fc ff ff call 8048868 <sscanf@plt>
8048c20: 89 45 fc mov %eax,-0x4(%ebp)
8048c23: 83 7d fc 01 cmpl $0x1,-0x4(%ebp)
8048c27: 7f 05 jg 8048c2e <phase_3+0x43>
//-0x4(%ebp)大于1个跳转,否则爆炸
8048c29: e8 f8 09 00 00 call 8049626 <explode_bomb>
8048c2e: 8b 45 f4 mov -0xc(%ebp),%eax
8048c31: 89 45 ec mov %eax,-0x14(%ebp)
8048c34: 83 7d ec 07 cmpl $0x7,-0x14(%ebp)
8048c38: 77 54 ja 8048c8e <phase_3+0xa3>
//-0x14(%ebp)大于7爆炸
8048c3a: 8b 55 ec mov -0x14(%ebp),%edx
8048c3d: 8b 04 95 50 99 04 08 mov 0x8049950(,%edx,4),%eax
8048c44: ff e0 jmp *%eax
8048c46: c7 45 f8 c7 03 00 00 movl $0x3c7,-0x8(%ebp)
8048c4d: eb 44 jmp 8048c93 <phase_3+0xa8>
8048c4f: c7 45 f8 93 00 00 00 movl $0x93,-0x8(%ebp)
8048c56: eb 3b jmp 8048c93 <phase_3+0xa8>
8048c58: c7 45 f8 96 02 00 00 movl $0x296,-0x8(%ebp)
8048c5f: eb 32 jmp 8048c93 <phase_3+0xa8>
8048c61: c7 45 f8 25 02 00 00 movl $0x225,-0x8(%ebp)
8048c68: eb 29 jmp 8048c93 <phase_3+0xa8>
8048c6a: c7 45 f8 a6 03 00 00 movl $0x3a6,-0x8(%ebp)
8048c71: eb 20 jmp 8048c93 <phase_3+0xa8>
8048c73: c7 45 f8 7a 01 00 00 movl $0x17a,-0x8(%ebp)
8048c7a: eb 17 jmp 8048c93 <phase_3+0xa8>
8048c7c: c7 45 f8 e5 01 00 00 movl $0x1e5,-0x8(%ebp)
8048c83: eb 0e jmp 8048c93 <phase_3+0xa8>
8048c85: c7 45 f8 6b 01 00 00 movl $0x16b,-0x8(%ebp)
8048c8c: eb 05 jmp 8048c93 <phase_3+0xa8>
8048c8e: e8 93 09 00 00 call 8049626 <explode_bomb>
8048c93: 8b 45 f0 mov -0x10(%ebp),%eax
8048c96: 39 45 f8 cmp %eax,-0x8(%ebp)
8048c99: 74 05 je 8048ca0 <phase_3+0xb5>
8048c9b: e8 86 09 00 00 call 8049626 <explode_bomb>
8048ca0: c9 leave
8048ca1: c3 ret
检查输入值:
如果 sscanf 没有成功读取一个输入值(返回值不为1),则调用 explode_bomb。
输入值范围检查:
检查读取到的值(位于 -0x14(%ebp))是否在0到7之间。
如果不是,则调用 explode_bomb。
基于输入值的间接跳转:
根据输入值,通过间接跳转(jmp *%eax)设置局部变量 -0x8(%ebp) 的值。
检查设置的值与另一个局部变量的关系:
将 -0x10(%ebp) 的值与 -0x8(%ebp) 的值进行比较。
如果它们相等,则函数正常返回。
如果不相等,则调用 explode_bomb。
阶段 4:递归调用和栈
08048ca2 <func4>:
8048ca2: 55 push %ebp
8048ca3: 89 e5 mov %esp,%ebp
8048ca5: 83 ec 08 sub $0x8,%esp
8048ca8: 83 7d 08 01 cmpl $0x1,0x8(%ebp)
8048cac: 7f 09 jg 8048cb7 <func4+0x15>
8048cae: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%ebp)
8048cb5: eb 15 jmp 8048ccc <func4+0x2a>
8048cb7: 8b 45 08 mov 0x8(%ebp),%eax
8048cba: 48 dec %eax
8048cbb: 89 04 24 mov %eax,(%esp)
8048cbe: e8 df ff ff ff call 8048ca2 <func4>
8048cc3: 89 c2 mov %eax,%edx
8048cc5: 0f af 55 08 imul 0x8(%ebp),%edx
8048cc9: 89 55 fc mov %edx,-0x4(%ebp)
8048ccc: 8b 45 fc mov -0x4(%ebp),%eax
8048ccf: c9 leave
8048cd0: c3 ret
08048cd1 <phase_4>:
8048cd1: 55 push %ebp
8048cd2: 89 e5 mov %esp,%ebp
8048cd4: 83 ec 28 sub $0x28,%esp
8048cd7: 8d 45 f4 lea -0xc(%ebp),%eax
8048cda: 89 44 24 08 mov %eax,0x8(%esp)
8048cde: c7 44 24 04 70 99 04 movl $0x8049970,0x4(%esp)
8048ce5: 08
8048ce6: 8b 45 08 mov 0x8(%ebp),%eax
8048ce9: 89 04 24 mov %eax,(%esp)
8048cec: e8 77 fb ff ff call 8048868 <sscanf@plt>
8048cf1: 89 45 fc mov %eax,-0x4(%ebp)
8048cf4: 83 7d fc 01 cmpl $0x1,-0x4(%ebp)
8048cf8: 75 07 jne 8048d01 <phase_4+0x30>
8048cfa: 8b 45 f4 mov -0xc(%ebp),%eax
8048cfd: 85 c0 test %eax,%eax
8048cff: 7f 05 jg 8048d06 <phase_4+0x35>
// %eax大于0跳转,否则爆炸
8048d01: e8 20 09 00 00 call 8049626 <explode_bomb>
8048d06: 8b 45 f4 mov -0xc(%ebp),%eax
8048d09: 89 04 24 mov %eax,(%esp)
8048d0c: e8 91 ff ff ff call 8048ca2 <func4>
8048d11: 89 45 f8 mov %eax,-0x8(%ebp)
8048d14: 81 7d f8 00 5f 37 00 cmpl $0x375f00,-0x8(%ebp)
8048d1b: 74 05 je 8048d22 <phase_4+0x51>
8048d1d: e8 04 09 00 00 call 8049626 <explode_bomb>
8048d22: c9 leave
8048d23: c3 ret
func4 函数
参数检查:
检查传入的参数(位于 0x8(%ebp))是否大于1。
递归基:
如果参数小于或等于1,则将局部变量 -0x4(%ebp) 设置为1,并跳转到函数末尾返回。
递归步骤:
如果参数大于1,则执行以下操作:
参数减1,并递归调用 func4。
将递归调用的返回值与原始参数相乘,并将结果存储在 -0x4(%ebp)。即求阶乘。
返回结果:
返回局部变量 -0x4(%ebp) 的值。
读取输入:
lea -0xc(%ebp),%eax 获取栈上分配的一个地址(-0xc(%ebp)),这个地址可能用于存储输入的字符串。
接着,使用sscanf函数(通过call 8048868 <sscanf@plt>)从标准输入读取一个整数,并存储在-0xc(%ebp)指向的地址中。
检查输入:
movl $0x8049970,0x4(%esp) 可能是将某个字符串(可能是输入格式字符串)的地址推送到栈上。
cmpl $0x1,-0x4(%ebp) 检查sscanf的返回值(存储在-0x4(%ebp)中)是否为1,确保正确读取了一个整数。
如果不是,调用explode_bomb函数。
检查整数值:
mov -0xc(%ebp),%eax 获取用户输入的整数值。
test %eax,%eax 检查该值是否为0(如果是0,则jg不会跳转)。
如果输入的值不大于0(无符号比较),则调用explode_bomb函数。
调用func4函数:
调用func4函数,并将用户输入的整数值作为参数传递。
将func4的返回值存储在-0x8(%ebp)中。
检查结果:
cmpl $0x375f00,-0x8(%ebp) 检查func4的返回值是否等于0x375f00。
如果不等于,调用explode_bomb函数。
函数结束:
leave 和 ret 指令用于恢复栈并返回。
总结:
phase_4函数要求用户输入一个整数。这个整数首先被检查是否成功读取(sscanf的返回值是否为1),然后检查其是否大于0。接着,这个整数被传递给func4函数,并且func4的返回值必须等于0x375f00,否则程序会调用explode_bomb函数。
阶段 5:指针
08048d24 <phase_5>:
8048d24: 55 push %ebp
8048d25: 89 e5 mov %esp,%ebp
8048d27: 83 ec 18 sub $0x18,%esp
8048d2a: 8b 45 08 mov 0x8(%ebp),%eax
8048d2d: 89 04 24 mov %eax,(%esp)
8048d30: e8 00 03 00 00 call 8049035 <string_length>
8048d35: 89 45 fc mov %eax,-0x4(%ebp)
8048d38: 83 7d fc 06 cmpl $0x6,-0x4(%ebp)
8048d3c: 74 05 je 8048d43 <phase_5+0x1f>
8048d3e: e8 e3 08 00 00 call 8049626 <explode_bomb>
8048d43: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp)
8048d4a: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
8048d51: eb 1c jmp 8048d6f <phase_5+0x4b>
8048d53: 8b 45 f4 mov -0xc(%ebp),%eax
8048d56: 03 45 08 add 0x8(%ebp),%eax
8048d59: 0f b6 00 movzbl (%eax),%eax
8048d5c: 0f be c0 movsbl %al,%eax
8048d5f: 83 e0 0f and $0xf,%eax
8048d62: 8b 04 85 c0 a5 04 08 mov 0x804a5c0(,%eax,4),%eax
8048d69: 01 45 f8 add %eax,-0x8(%ebp)
8048d6c: ff 45 f4 incl -0xc(%ebp)
8048d6f: 83 7d f4 05 cmpl $0x5,-0xc(%ebp)
8048d73: 7e de jle 8048d53 <phase_5+0x2f>
8048d75: 83 7d f8 2c cmpl $0x2c,-0x8(%ebp)
8048d79: 74 05 je 8048d80 <phase_5+0x5c>
8048d7b: e8 a6 08 00 00 call 8049626 <explode_bomb>
8048d80: c9 leave
8048d81: c3 ret
读取输入字符串的长度:
函数首先通过string_length函数(通过call 8049035 <string_length>)获取输入字符串(存储在%ebp+8中)的长度,并将结果存储在%ebp-4中。
检查字符串长度:
使用cmpl $0x6,-0x4(%ebp)检查字符串长度是否为6(即6个字符)。
如果不是,调用explode_bomb函数导致程序崩溃。
初始化循环变量:
在8048d43到8048d51的代码中,两个局部变量(%ebp-8和%ebp-12)被初始化为0。
处理字符串:
接下来的代码块(从8048d53开始)是一个循环,它遍历输入字符串的每个字符(由于之前检查了长度为6,所以循环会执行6次)。
在每次迭代中,它做以下操作:
使用movzbl (%eax),%eax和movsbl %al,%eax从字符串中读取一个字节,并将其符号扩展到32位。
使用and $0xf,%eax将字节值限制在0到15之间(即取低4位)。
使用mov 0x804a5c0(,%eax,4),%eax从数组0x804a5c0中索引一个值(基于当前字符的低4位)。这个数组可能包含某种映射或转换。
将从数组中检索到的值累加到%ebp-8指向的变量中。
增加%ebp-12的值以跟踪循环的进度。
检查最终累积值:
在循环结束后,使用cmpl $0x2c,-0x8(%ebp)检查累积值(存储在%ebp-8中)是否为0x2c(即44的十六进制)。
如果不是,调用explode_bomb函数。
函数返回:
如果所有检查都通过,函数使用leave和ret指令返回。
总结:
phase_5函数要求用户输入一个长度为6的字符串。这个字符串的每个字符的低4位被用来从一个数组(位于0x804a5c0)中索引一个值,并将这些值累加起来。如果最终累积值等于0x2c(44),则函数成功返回;否则,程序会调用explode_bomb函数并崩溃。
阶段 6:链表/指针/结构
08048d82 <fun6>:
8048d82: 55 push %ebp
8048d83: 89 e5 mov %esp,%ebp
8048d85: 83 ec 10 sub $0x10,%esp
8048d88: 8b 45 08 mov 0x8(%ebp),%eax
8048d8b: 89 45 f0 mov %eax,-0x10(%ebp)
8048d8e: 8b 45 08 mov 0x8(%ebp),%eax
8048d91: 89 45 f0 mov %eax,-0x10(%ebp)
8048d94: 8b 45 08 mov 0x8(%ebp),%eax
8048d97: 8b 40 08 mov 0x8(%eax),%eax
8048d9a: 89 45 f4 mov %eax,-0xc(%ebp)
8048d9d: 8b 45 f0 mov -0x10(%ebp),%eax
8048da0: c7 40 08 00 00 00 00 movl $0x0,0x8(%eax)
8048da7: eb 62 jmp 8048e0b <fun6+0x89>
8048da9: 8b 45 f0 mov -0x10(%ebp),%eax
8048dac: 89 45 fc mov %eax,-0x4(%ebp)
8048daf: 8b 45 f0 mov -0x10(%ebp),%eax
8048db2: 89 45 f8 mov %eax,-0x8(%ebp)
8048db5: eb 0f jmp 8048dc6 <fun6+0x44>
8048db7: 8b 45 fc mov -0x4(%ebp),%eax
8048dba: 89 45 f8 mov %eax,-0x8(%ebp)
8048dbd: 8b 45 fc mov -0x4(%ebp),%eax
8048dc0: 8b 40 08 mov 0x8(%eax),%eax
8048dc3: 89 45 fc mov %eax,-0x4(%ebp)
8048dc6: 83 7d fc 00 cmpl $0x0,-0x4(%ebp)
8048dca: 74 0e je 8048dda <fun6+0x58>
8048dcc: 8b 45 fc mov -0x4(%ebp),%eax
8048dcf: 8b 10 mov (%eax),%edx
8048dd1: 8b 45 f4 mov -0xc(%ebp),%eax
8048dd4: 8b 00 mov (%eax),%eax
8048dd6: 39 c2 cmp %eax,%edx
8048dd8: 7f dd jg 8048db7 <fun6+0x35>
8048dda: 8b 45 f8 mov -0x8(%ebp),%eax
8048ddd: 3b 45 fc cmp -0x4(%ebp),%eax
8048de0: 74 0b je 8048ded <fun6+0x6b>
8048de2: 8b 55 f8 mov -0x8(%ebp),%edx
8048de5: 8b 45 f4 mov -0xc(%ebp),%eax
8048de8: 89 42 08 mov %eax,0x8(%edx)
8048deb: eb 06 jmp 8048df3 <fun6+0x71>
8048ded: 8b 45 f4 mov -0xc(%ebp),%eax
8048df0: 89 45 f0 mov %eax,-0x10(%ebp)
8048df3: 8b 45 f4 mov -0xc(%ebp),%eax
8048df6: 8b 40 08 mov 0x8(%eax),%eax
8048df9: 89 45 f8 mov %eax,-0x8(%ebp)
8048dfc: 8b 55 f4 mov -0xc(%ebp),%edx
8048dff: 8b 45 fc mov -0x4(%ebp),%eax
8048e02: 89 42 08 mov %eax,0x8(%edx)
8048e05: 8b 45 f8 mov -0x8(%ebp),%eax
8048e08: 89 45 f4 mov %eax,-0xc(%ebp)
8048e0b: 83 7d f4 00 cmpl $0x0,-0xc(%ebp)
8048e0f: 75 98 jne 8048da9 <fun6+0x27>
8048e11: 8b 45 f0 mov -0x10(%ebp),%eax
8048e14: c9 leave
8048e15: c3 ret
08048e16 <phase_6>:
8048e16: 55 push %ebp
8048e17: 89 e5 mov %esp,%ebp
8048e19: 83 ec 18 sub $0x18,%esp
8048e1c: c7 45 f8 6c a6 04 08 movl $0x804a66c,-0x8(%ebp)
8048e23: 8b 45 08 mov 0x8(%ebp),%eax
8048e26: 89 04 24 mov %eax,(%esp)
8048e29: e8 2a fa ff ff call 8048858 <atoi@plt>
8048e2e: 89 c2 mov %eax,%edx
8048e30: 8b 45 f8 mov -0x8(%ebp),%eax
8048e33: 89 10 mov %edx,(%eax)
8048e35: 8b 45 f8 mov -0x8(%ebp),%eax
8048e38: 89 04 24 mov %eax,(%esp)
8048e3b: e8 42 ff ff ff call 8048d82 <fun6>
8048e40: 89 45 f8 mov %eax,-0x8(%ebp)
8048e43: 8b 45 f8 mov -0x8(%ebp),%eax
8048e46: 89 45 fc mov %eax,-0x4(%ebp)
8048e49: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%ebp)
8048e50: eb 0c jmp 8048e5e <phase_6+0x48>
8048e52: 8b 45 fc mov -0x4(%ebp),%eax
8048e55: 8b 40 08 mov 0x8(%eax),%eax
8048e58: 89 45 fc mov %eax,-0x4(%ebp)
8048e5b: ff 45 f4 incl -0xc(%ebp)
8048e5e: 83 7d f4 05 cmpl $0x5,-0xc(%ebp)
8048e62: 7e ee jle 8048e52 <phase_6+0x3c>
8048e64: 8b 45 fc mov -0x4(%ebp),%eax
8048e67: 8b 10 mov (%eax),%edx
8048e69: a1 6c a6 04 08 mov 0x804a66c,%eax
8048e6e: 39 c2 cmp %eax,%edx
8048e70: 74 05 je 8048e77 <phase_6+0x61>
8048e72: e8 af 07 00 00 call 8049626 <explode_bomb>
8048e77: c9 leave
8048e78: c3 ret
初始化变量:
movl $0x804a66c, -0x8(%ebp) 将一个地址(可能是一个全局数组或链表的头)存储在局部变量中。
从用户输入读取一个整数:
mov 0x8(%ebp), %eax 获取传递给函数的参数(可能是用户输入)。
call 8048858 <atoi@plt> 调用 atoi 函数将字符串参数转换为整数。
将返回的整数存储在局部变量中,并用于修改全局数组或链表的某个元素。
调用 fun6 函数:
使用之前初始化的全局数组或链表的地址作为参数调用 fun6 函数。
fun6 函数的返回值被存储在局部变量中。
遍历链表或数组:
使用一个循环(jmp 和 cmpl 指令)来遍历链表或数组的某个部分。
在循环内部,从当前元素加载一个值(mov 0x8(%eax), %eax),并将其存储在局部变量中。
递增一个计数器(incl -0xc(%ebp))。
检查链表或数组的结束条件:
当计数器达到 5 时,循环结束。
检查链表或数组的最后一个元素:
加载链表或数组的最后一个元素,并将其与全局数组或链表的头进行比较。
如果它们相等,则函数正常返回。
如果它们不相等,则调用 explode_bomb 函数。
总结:
phase_6 函数首先读取一个用户输入的整数,并使用它来修改全局数组或链表的某个元素。
然后,它调用 fun6 函数对链表或数组进行操作。
接着,它遍历链表或数组的前五个元素。
最后,它检查链表或数组的最后一个元素是否与初始值相同,如果不同,则调用 explode_bomb 函数。
为了成功通过此阶段,用户输入的整数必须导致 fun6 函数修改全局数组或链表的正确元素,使得最后一个元素保持不变。
隐藏阶段:
08048e79 <fun7>:
8048e79: 55 push %ebp
8048e7a: 89 e5 mov %esp,%ebp
8048e7c: 83 ec 0c sub $0xc,%esp
8048e7f: 83 7d 08 00 cmpl $0x0,0x8(%ebp)
8048e83: 75 09 jne 8048e8e <fun7+0x15>
8048e85: c7 45 fc ff ff ff ff movl $0xffffffff,-0x4(%ebp)
8048e8c: eb 54 jmp 8048ee2 <fun7+0x69>
8048e8e: 8b 45 08 mov 0x8(%ebp),%eax
8048e91: 8b 00 mov (%eax),%eax
8048e93: 3b 45 0c cmp 0xc(%ebp),%eax
8048e96: 7e 1c jle 8048eb4 <fun7+0x3b>
8048e98: 8b 45 08 mov 0x8(%ebp),%eax
8048e9b: 8b 50 04 mov 0x4(%eax),%edx
8048e9e: 8b 45 0c mov 0xc(%ebp),%eax
8048ea1: 89 44 24 04 mov %eax,0x4(%esp)
8048ea5: 89 14 24 mov %edx,(%esp)
8048ea8: e8 cc ff ff ff call 8048e79 <fun7>
8048ead: 01 c0 add %eax,%eax
8048eaf: 89 45 fc mov %eax,-0x4(%ebp)
8048eb2: eb 2e jmp 8048ee2 <fun7+0x69>
8048eb4: 8b 45 08 mov 0x8(%ebp),%eax
8048eb7: 8b 00 mov (%eax),%eax
8048eb9: 3b 45 0c cmp 0xc(%ebp),%eax
8048ebc: 75 09 jne 8048ec7 <fun7+0x4e>
8048ebe: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
8048ec5: eb 1b jmp 8048ee2 <fun7+0x69>
8048ec7: 8b 45 08 mov 0x8(%ebp),%eax
8048eca: 8b 50 08 mov 0x8(%eax),%edx
8048ecd: 8b 45 0c mov 0xc(%ebp),%eax
8048ed0: 89 44 24 04 mov %eax,0x4(%esp)
8048ed4: 89 14 24 mov %edx,(%esp)
8048ed7: e8 9d ff ff ff call 8048e79 <fun7>
8048edc: 01 c0 add %eax,%eax
8048ede: 40 inc %eax
8048edf: 89 45 fc mov %eax,-0x4(%ebp)
8048ee2: 8b 45 fc mov -0x4(%ebp),%eax
8048ee5: c9 leave
8048ee6: c3 ret
08048ee7 <secret_phase>:
8048ee7: 55 push %ebp
8048ee8: 89 e5 mov %esp,%ebp
8048eea: 83 ec 18 sub $0x18,%esp
8048eed: e8 a8 03 00 00 call 804929a <read_line>
8048ef2: 89 45 f4 mov %eax,-0xc(%ebp)
8048ef5: 8b 45 f4 mov -0xc(%ebp),%eax
8048ef8: 89 04 24 mov %eax,(%esp)
8048efb: e8 58 f9 ff ff call 8048858 <atoi@plt>
8048f00: 89 45 f8 mov %eax,-0x8(%ebp)
8048f03: 83 7d f8 00 cmpl $0x0,-0x8(%ebp)
8048f07: 7e 09 jle 8048f12 <secret_phase+0x2b>
8048f09: 81 7d f8 e9 03 00 00 cmpl $0x3e9,-0x8(%ebp)
8048f10: 7e 05 jle 8048f17 <secret_phase+0x30>
8048f12: e8 0f 07 00 00 call 8049626 <explode_bomb>
8048f17: 8b 45 f8 mov -0x8(%ebp),%eax
8048f1a: 89 44 24 04 mov %eax,0x4(%esp)
8048f1e: c7 04 24 20 a7 04 08 movl $0x804a720,(%esp)
8048f25: e8 4f ff ff ff call 8048e79 <fun7>
8048f2a: 89 45 fc mov %eax,-0x4(%ebp)
8048f2d: 83 7d fc 05 cmpl $0x5,-0x4(%ebp)
8048f31: 74 05 je 8048f38 <secret_phase+0x51>
8048f33: e8 ee 06 00 00 call 8049626 <explode_bomb>
8048f38: c7 04 24 74 99 04 08 movl $0x8049974,(%esp)
8048f3f: e8 84 f8 ff ff call 80487c8 <puts@plt>
8048f44: e8 07 07 00 00 call 8049650 <phase_defused>
8048f49: c9 leave
8048f4a: c3 ret
8048f4b: 90 nop
08049650 <phase_defused>:
8049650: 55 push %ebp
8049651: 89 e5 mov %esp,%ebp
8049653: 83 ec 78 sub $0x78,%esp
8049656: a1 8c a8 04 08 mov 0x804a88c,%eax
804965b: 83 f8 06 cmp $0x6,%eax
804965e: 75 6e jne 80496ce <phase_defused+0x7e>
8049660: b8 90 a9 04 08 mov $0x804a990,%eax
8049665: 89 c2 mov %eax,%edx
8049667: 8d 45 ac lea -0x54(%ebp),%eax
804966a: 89 44 24 0c mov %eax,0xc(%esp)
804966e: 8d 45 a8 lea -0x58(%ebp),%eax
8049671: 89 44 24 08 mov %eax,0x8(%esp)
8049675: c7 44 24 04 10 9e 04 movl $0x8049e10,0x4(%esp)
804967c: 08
804967d: 89 14 24 mov %edx,(%esp)
8049680: e8 e3 f1 ff ff call 8048868 <sscanf@plt>
8049685: 89 45 fc mov %eax,-0x4(%ebp)
8049688: 83 7d fc 02 cmpl $0x2,-0x4(%ebp)
804968c: 75 34 jne 80496c2 <phase_defused+0x72>
804968e: c7 44 24 04 16 9e 04 movl $0x8049e16,0x4(%esp)
8049695: 08
8049696: 8d 45 ac lea -0x54(%ebp),%eax
8049699: 89 04 24 mov %eax,(%esp)
804969c: e8 be f9 ff ff call 804905f <strings_not_equal>
80496a1: 85 c0 test %eax,%eax
80496a3: 75 1d jne 80496c2 <phase_defused+0x72>
80496a5: c7 04 24 24 9e 04 08 movl $0x8049e24,(%esp)
80496ac: e8 17 f1 ff ff call 80487c8 <puts@plt>
80496b1: c7 04 24 4c 9e 04 08 movl $0x8049e4c,(%esp)
80496b8: e8 0b f1 ff ff call 80487c8 <puts@plt>
80496bd: e8 25 f8 ff ff call 8048ee7 <secret_phase>
80496c2: c7 04 24 84 9e 04 08 movl $0x8049e84,(%esp)
80496c9: e8 fa f0 ff ff call 80487c8 <puts@plt>
80496ce: c9 leave
80496cf: c3 ret
fun7函数接收两个参数,一个在%ebp+8(函数参数通常从右到左压栈,所以这是第一个参数),另一个在%ebp+0xC(第二个参数)。
函数开始:
push %ebp 和 mov %esp, %ebp 是常见的函数开始时的栈帧设置。
sub $0xC, %esp 为局部变量在栈上分配了12字节的空间(三个32位整数)。
检查第一个参数:
cmpl $0x0, 0x8(%ebp) 检查第一个参数是否为0。
如果为0,则跳转到错误处理部分,将局部变量(位于%ebp-4)设置为-1(表示错误)并跳转到函数末尾。
主逻辑:
函数接下来检查第一个参数(可能是链表或某种数据结构的指针)所指向的值(mov (%eax),%eax)。
这个值与第二个参数进行比较(cmp 0xc(%ebp),%eax)。
如果第一个参数所指向的值小于或等于第二个参数,执行一个分支(标签为<fun7+0x3b>)。
否则,执行另一个分支,它首先读取第一个参数所指向的数据结构的下一个字段(偏移为4的字段,可能是链表节点的下一个节点),并递归调用fun7函数自身。
递归调用:
在递归调用中,传递了当前节点的下一个字段(edx)和第二个参数(eax)作为参数。
递归调用的返回值乘以2(add %eax,%eax),并存储在局部变量中(mov %eax,-0x4(%ebp))。
小于或等于的分支:
如果第一个参数所指向的值小于或等于第二个参数,函数检查它们是否相等。
如果相等,将局部变量设置为0(成功的情况)。
如果不相等且之前也没有递归调用(即,这是第一个节点),则跳转到函数末尾。
另一个递归调用:
如果第一个参数所指向的值大于第二个参数,但又不满足上述条件,函数会读取数据结构的另一个字段(偏移为8的字段,可能是链表节点的另一个属性或子节点),并递归调用fun7。
递归调用的返回值乘以2并加1(inc %eax),然后存储在局部变量中。
函数结束:
函数最后返回局部变量(位于%ebp-4)的值作为结果。
读取输入:
调用read_line函数读取一行输入(call 804929a <read_line>)。
将read_line的返回值(可能是一个指向输入字符串的指针)存储在局部变量中(mov %eax,-0xc(%ebp))。
将输入转换为整数:
将这个指针作为参数传递给atoi函数(call 8048858 <atoi@plt>),将字符串转换为整数。
将atoi的返回值(整数)存储在另一个局部变量中(mov %eax,-0x8(%ebp))。
检查输入值:
检查这个整数是否为0或负数(cmpl $0x0,-0x8(%ebp) 和 jle 8048f12 <secret_phase+0x2b>)。
如果不是,进一步检查它是否小于或等于0x3e9(cmpl $0x3e9,-0x8(%ebp) 和 jle 8048f17 <secret_phase+0x30>)。
如果不满足上述任一条件(即输入值不在0到0x3e9之间),则调用explode_bomb函数,可能是使程序崩溃或触发某种错误处理机制(call 8049626 <explode_bomb>)。
调用fun7:
如果输入值满足条件,将其作为参数传递给fun7函数(mov -0x8(%ebp),%eax 和 call 8048e79 <fun7>)。
将fun7的返回值存储在另一个局部变量中(mov %eax,-0x4(%ebp))。
检查fun7的返回值:
检查fun7的返回值是否为5(cmpl $0x5,-0x4(%ebp) 和 je 8048f38 <secret_phase+0x51>)。
如果不是,再次调用explode_bomb函数。
解密成功处理:
如果fun7的返回值是5,则打印一条消息(可能是“解密成功”之类的信息,调用puts函数,地址是0x8049974)。
接着调用phase_defused函数,可能是标记当前阶段已成功解除或进行后续处理(call 8049650 <phase_defused>)。
函数结束:
使用leave指令恢复栈帧(leave)。
返回(ret)。
二、实验过程
阶段 1:字符串比较
阶段 2:循环
随便输入六个数
第二个数应该是-1,即比-6大5
重复,这次需要循环到第二个0x08048bd7
第三个数应该是4 ……
阶段 3:条件/分支
应该输入两个整数
0 967或1 147或2 662 …… 7 363
阶段 4:递归调用和栈
应该输入一个整数
10!= 0x375f00
阶段 5:指针
2 10 6 1
12 16 9 3
0000 0001 0010 0011 0101 0110
80 81 82 83 85 86 PQRSUV
96 97 98 99 101 102 bcdegh
阶段 6:链表/指针/结构
循环5次到0x08048e6e
344或345好像都行?
隐藏阶段:
即在阶段4要输入一个整数一个字符串 austinpowers
int fun7(const int *a, int b)
{
if (a == NULL)
return -1;
int ret = 0;
if (*a - b > 0)
{
ret = fun7(*(a + 4), b);
ret *= 2
}
else if (*a - b == 0)
return 0;
else
{
ret = fun7(*(a + 8), b);
ret = ret * 2 + 1;
}
return ret;
}
由于最后的返回值是5,根据函数数的结构可以想到这样的结构:
用穷举的方法试了下,似乎也只有这样一个唯一的结构可以得出5。
根据以上的结构以及C代码,就可以知道要做些什么了。
1)*0x804a720=0x24,之后在*a-b<0 (0x24 – b < 0)的分支中*(a + 8)=0x804a708,所以fun7(0x804a708, b)。
2)*0x804a708=0x32,*a-b>0 (0x32 - b > 0),递归fun7(*(0x804a708 + 4) = 0x804a6f0, b)。
3)*0x804a6f0=0x2d,*a-b<0 (0x2d - b < 0),fun7(*(0x804a6f0 + 8) = 0x804a684, b)。
4)*0x804a684=0x2f,*a – b == 0 (0x2f – b == 0),所以b = 0x2f,递归返回。
得到3个不等式和一个等式:
1) 0x24 – 0x2f < 0
2) 0x32 – 0x2f > 0
3) 0x2d – 0x2f < 0
4) b = 0x2f
都符合要求,所以我们得出最后输入的值就是十进制数47(0x2f)。
参考逆向工程——二进制炸弹(CSAPP Project) - chkkch - 博客园 (cnblogs.com)