trycrackme 的下载地址为 https://crackmes.one/crackme/61c8deff33c5d413767ca0ea ,直接从汇编就能看出来到底在做什么
Dump of assembler code for function main:
# 标准开局,保存堆栈
0x00005555555551af <+0>: push %rbp
0x00005555555551b0 <+1>: mov %rsp,%rbp
0x00005555555551b3 <+4>: sub $0xe0,%rsp
#存储 argc & argv ,不过这里没用到
0x00005555555551ba <+11>: mov %edi,-0xd4(%rbp)
0x00005555555551c0 <+17>: mov %rsi,-0xe0(%rbp)
0x00005555555551c7 <+24>: mov %fs:0x28,%rax
0x00005555555551d0 <+33>: mov %rax,-0x8(%rbp)
0x00005555555551d4 <+37>: xor %eax,%eax
# 注意这个常量,是我们比较的关键
0x00005555555551d6 <+39>: movabs $0x3534323773734034,%rax
# 存储到了-0xbb(%rbp)的位置
0x00005555555551e0 <+49>: mov %rax,-0xbb(%rbp)
# 给上面的常量补充了两个字节的数字,拼接到最后面,拼接完以后看一下具体的内容
# (gdb) x/16xb 140737488347509
# 0x7fffffffe175: 0x34 0x40 0x73 0x73 0x37 0x32 0x34 0x35
# 0x7fffffffe17d: 0x33 0x36 0x00 0x00 0x00 0x00 0x00 0x00
0x00005555555551e7 <+56>: movw $0x3633,-0xb3(%rbp)
0x00005555555551f0 <+65>: movb $0x0,-0xb1(%rbp)
0x00005555555551f7 <+72>: lea -0xbb(%rbp),%rax
0x00005555555551fe <+79>: mov %rax,%rdi
0x0000555555555201 <+82>: callq 0x555555555050 <strlen@plt>
# 存储我们刚才拼接出来的字符串长度,明确地看出来是 10
0x0000555555555206 <+87>: mov %eax,-0xc0(%rbp)
0x000055555555520c <+93>: mov $0x0,%eax
# 调用 banner 打印一些 flag ,没有啥用,不用管
0x0000555555555211 <+98>: callq 0x555555555199 <banner>
# 准备调用 printf 提示用户输入数据,格式化字符串为 Put the key:
0x0000555555555216 <+103>: lea 0xef9(%rip),%rax # 0x555555556116
0x000055555555521d <+110>: mov %rax,%rdi
0x0000555555555220 <+113>: mov $0x0,%eax
0x0000555555555225 <+118>: callq 0x555555555070 <printf@plt>
# 记住这个-0xb0(%rbp)的地址,这个是存储 scanf 输入进来的地址
0x000055555555522a <+123>: lea -0xb0(%rbp),%rax
0x0000555555555231 <+130>: mov %rax,%rsi
0x0000555555555234 <+133>: lea 0xee9(%rip),%rax # 0x555555556124
0x000055555555523b <+140>: mov %rax,%rdi
0x000055555555523e <+143>: mov $0x0,%eax
0x0000555555555243 <+148>: callq 0x555555555080 <__isoc99_scanf@plt>
# 初始化两个变量,分别存储上面-0xbb(%rbp)字符串处理过的字符个数
# 和要算出来作为正确的 code 所处理过的字符个数
0x0000555555555248 <+153>: movl $0x0,-0xc8(%rbp)
0x0000555555555252 <+163>: movl $0x0,-0xc4(%rbp)
|--- 0x000055555555525c <+173>: jmp 0x5555555552ab <main+252>
| # -0xc8(%rbp)是刚才那个常量字符串处理过的 byte 数,所以 cltq 下面那句就很明显了
|-----> 0x000055555555525e <+175>: mov -0xc8(%rbp),%eax
| | 0x0000555555555264 <+181>: cltq
| | # 这里的意思是把刚才-0xbb(%rbp)字符串的字符,hex 形式丢到 eax
| | 0x0000555555555266 <+183>: movzbl -0xbb(%rbp,%rax,1),%eax
| | # 先做有符号数拓展,再做无符号数拓展,不过都小于 0x80 ,所以无所谓了
| | 0x000055555555526e <+191>: movsbl %al,%eax
| | 0x0000555555555271 <+194>: movzbl %al,%eax
| | 0x0000555555555274 <+197>: mov -0xc4(%rbp),%edx
| | 0x000055555555527a <+203>: movslq %edx,%rdx
| | # rcx 存储了计算结果的首地址, -0x70(%rbp)是我们最后比较的参照物的地址
| | 0x000055555555527d <+206>: lea -0x70(%rbp),%rcx
| | # 加上已经结算过的结果,实际上就是挪动指针,存储下面 sprintf 的结果
| | 0x0000555555555281 <+210>: add %rdx,%rcx
| | 0x0000555555555284 <+213>: mov %eax,%edx
| | # 这个字符是 %02x
| | 0x0000555555555286 <+215>: lea 0xe9a(%rip),%rax # 0x555555556127
| | 0x000055555555528d <+222>: mov %rax,%rsi
| | 0x0000555555555290 <+225>: mov %rcx,%rdi
| | 0x0000555555555293 <+228>: mov $0x0,%eax
| | # 这里调用 sprintf 的含义就非常清楚了,从 hex 编码转换为字符串
| | # 原先是 hex 0x34 ,那么转换为字符串"34"
| | 0x0000555555555298 <+233>: callq 0x555555555090 <sprintf@plt>
| | # hex 字符串处理过一 byte 后挪一
| | # 而 sprintf 处理的结果是 2byte (两个 char 字符)
| | 0x000055555555529d <+238>: addl $0x1,-0xc8(%rbp)
| | 0x00005555555552a4 <+245>: addl $0x2,-0xc4(%rbp)
| | #开始处理,先找到第一个字符,看看和上面的字符串长度 10 的大小,判断有没有处理完
| ---> 0x00005555555552ab <+252>: mov -0xc8(%rbp),%eax
| 0x00005555555552b1 <+258>: cmp -0xc0(%rbp),%eax
|------ 0x00005555555552b7 <+264>: jl 0x55555555525e <main+175>
0x00005555555552b9 <+266>: lea -0x70(%rbp),%rax
0x00005555555552bd <+270>: mov %rax,%rdi
0x00005555555552c0 <+273>: callq 0x555555555050 <strlen@plt>
0x00005555555552c5 <+278>: mov %rax,%rdx
# 算出来的正确的 code
0x00005555555552c8 <+281>: lea -0x70(%rbp),%rcx
# 输入的 code
0x00005555555552cc <+285>: lea -0xb0(%rbp),%rax
0x00005555555552d3 <+292>: mov %rcx,%rsi
0x00005555555552d6 <+295>: mov %rax,%rdi
# 比较
0x00005555555552d9 <+298>: callq 0x555555555030 <strncmp@plt>
0x00005555555552de <+303>: test %eax,%eax
|----0x00005555555552e0 <+305>: je 0x5555555552fd <main+334>
| 0x00005555555552e2 <+307>: lea 0xe43(%rip),%rax # 0x55555555612c
| 0x00005555555552e9 <+314>: mov %rax,%rdi
| 0x00005555555552ec <+317>: mov $0x0,%eax
| 0x00005555555552f1 <+322>: callq 0x555555555070 <printf@plt>
| 0x00005555555552f6 <+327>: mov $0xffffffff,%eax
| 0x00005555555552fb <+332>: jmp 0x555555555316 <main+359>
| # 这里就是正确的结果,所以我们只需要输入一个字符串和上面常量字符串从 hex 到字符串转换的结果即可
|--->0x00005555555552fd <+334>: lea 0xe3b(%rip),%rax # 0x55555555613f
0x0000555555555304 <+341>: mov %rax,%rdi
0x0000555555555307 <+344>: mov $0x0,%eax
0x000055555555530c <+349>: callq 0x555555555070 <printf@plt>
0x0000555555555311 <+354>: mov $0x0,%eax
0x0000555555555316 <+359>: mov -0x8(%rbp),%rdx
0x000055555555531a <+363>: sub %fs:0x28,%rdx
0x0000555555555323 <+372>: je 0x55555555532a <main+379>
0x0000555555555325 <+374>: callq 0x555555555060 <__stack_chk_fail@plt>
0x000055555555532a <+379>: leaveq
0x000055555555532b <+380>: retq
End of assembler dump.
简单说一下过程
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.