每天有时候日常工作做的很烦,疲乏之余做个小 crackme 玩下,这个的难度比较简单,进入 main ,跟着流程走一遍基本就明白了.crackme 的下载路径在这里: https://crackmes.one/crackme/62072dd633c5d46c8bcbfd9b ,
可以自己看下面的代码和注释来理解,不清楚的话看最底下的流程讲解
Dump of assembler code for function main:
0x0000555555555179 <+0>: push %rbp #标准开局,保存堆栈环境
0x000055555555517a <+1>: mov %rsp,%rbp
0x000055555555517d <+4>: sub $0x30,%rsp
#非常标准的对应 main 函数的两个参数 int main(int argc, char** argv),这个是 argc
0x0000555555555181 <+8>: mov %edi,-0x24(%rbp)
0x0000555555555184 <+11>: mov %rsi,-0x30(%rbp) #对应 argv
0x0000555555555188 <+15>: mov %fs:0x28,%rax
0x0000555555555191 <+24>: mov %rax,-0x8(%rbp)
0x0000555555555195 <+28>: xor %eax,%eax
# #比较有没有参数,换言之有没有输入参数,如果有输入参数跳转到下面箭头指的地方
0x0000555555555197 <+30>: cmpl $0x2,-0x24(%rbp)
------- 0x000055555555519b <+34>: je 0x5555555551c5 <main+76>
| # 没输入参数的话直接开始调用 printf 函数打印下面的格式
| 0x000055555555519d <+36>: mov -0x30(%rbp),%rax
| 0x00005555555551a1 <+40>: mov (%rax),%rax
| 0x00005555555551a4 <+43>: mov %rax,%rsi
| # 格式的字符对应"Usage : %s <license pass code here [numbers only]>\n"
| 0x00005555555551a7 <+46>: lea 0xe5a(%rip),%rax # 0x555555556008
| 0x00005555555551ae <+53>: mov %rax,%rdi
| 0x00005555555551b1 <+56>: mov $0x0,%eax
| 0x00005555555551b6 <+61>: callq 0x555555555050 <printf@plt>
| 0x00005555555551bb <+66>: mov $0x0,%edi
| 0x00005555555551c0 <+71>: callq 0x555555555070 <exit@plt>
| #下面的两个地址存储着两个参数
| # 清空两个数值,这里存储着一个计算出来的和
------> 0x00005555555551c5 <+76>: movl $0x0,-0x10(%rbp)
#这个地址存储的是处理过的字符串的字符的个数
0x00005555555551cc <+83>: movl $0x0,-0xc(%rbp)
-- 0x00005555555551d3 <+90>: jmp 0x555555555201 <main+136>
--------> 0x00005555555551d5 <+92>: mov -0x30(%rbp),%rax #还是拿到输入的数字的位置
| | 0x00005555555551d9 <+96>: add $0x8,%rax
| | 0x00005555555551dd <+100>: mov (%rax),%rdx
| | 0x00005555555551e0 <+103>: mov -0xc(%rbp),%eax # 这个存储着处理过的字符串字符的个数,第一次是 0
| | 0x00005555555551e3 <+106>: cltq
| | 0x00005555555551e5 <+108>: add %rdx,%rax #找到当前处理的字符
| | #取 rax ,实际上就是输入的数字所在的字符串的 1byte ,拓展到 eax 里
| | 0x00005555555551e8 <+111>: movzbl (%rax),%eax
| | 0x00005555555551eb <+114>: mov %al,-0x11(%rbp) #丢到栈里面,可以看到是刚才-0x10(%rbp)的低一位
| | 0x00005555555551ee <+117>: lea -0x11(%rbp),%rax
| | 0x00005555555551f2 <+121>: mov %rax,%rdi
| | #转换为整数,整个过程是一个循环,就是在不断地将每一位转换为数字,然后加到-0x10(%rbp)上
| | 0x00005555555551f5 <+124>: callq 0x555555555060 <atoi@plt>
| | #eax 存储着从字符转换为数字的结果(返回值),存储到求和到刚才舒适的存储和的位置
| | 0x00005555555551fa <+129>: add %eax,-0x10(%rbp)
| | #addl 加了一个 1 ,实际上就是诸位比较,还是继续运行,
| | 0x00005555555551fd <+132>: addl $0x1,-0xc(%rbp)
| | #argv 的起始地址,
| --->0x0000555555555201 <+136>: mov -0x30(%rbp),%rax
| # 偏移 8 字节,实际上目的是找到第二个元素,也就是我们输入参数所对应字符串的地址
| 0x0000555555555205 <+140>: add $0x8,%rax
| # 将元素的值赋值给 rax ,就是输入的数字的所在字符串的地址,
| 0x0000555555555209 <+144>: mov (%rax),%rax
| 0x000055555555520c <+147>: mov %rax,%rdi
| # 对字符串调用 strlen 判断长度
| 0x000055555555520f <+150>: callq 0x555555555040 <strlen@plt>
| # 返回的字符串长度放在了 eax 里面,和已经操作的字符串的字符个数比较一下,看看是不是处理完了
| 0x0000555555555214 <+155>: cmp %eax,-0xc(%rbp)
--- 0x0000555555555217 <+158>: jl 0x5555555551d5 <main+92>
#比较输入的参数字符串每一个转换为数字后加起来以后是不是 0x32 ,所以构造一个字符串各位求和是 0x32 的即可
0x0000555555555219 <+160>: cmpl $0x32,-0x10(%rbp)
0x000055555555521d <+164>: jne 0x555555555238 <main+191>
0x000055555555521f <+166>: lea 0xe1a(%rip),%rax # 0x555555556040
0x0000555555555226 <+173>: mov %rax,%rdi
0x0000555555555229 <+176>: callq 0x555555555030 <puts@plt>
0x000055555555522e <+181>: mov $0x0,%edi
0x0000555555555233 <+186>: callq 0x555555555070 <exit@plt>
0x0000555555555238 <+191>: lea 0xe25(%rip),%rax # 0x555555556064
0x000055555555523f <+198>: mov %rax,%rdi
0x0000555555555242 <+201>: callq 0x555555555030 <puts@plt>
0x0000555555555247 <+206>: mov $0x0,%edi
0x000055555555524c <+211>: callq 0x555555555070 <exit@plt>
所以构造一个求和是 0x32 的数字字符串即可,999995 ,通过