每天有时候日常工作做的很烦,疲乏之余做个小 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 ,通过