第一次在论坛发帖,多有不慎,还请多多包涵,如有错误,还请不吝赐教!
这个题其实如果用ida7.5来反编译其实很简单,但是这里我是ida7.0,多少有些问题,所以直接在汇编层面来解决,顺便锻炼了我的汇编阅读能力,本人技术很菜,如有错误,还望师傅们不吝赐教,谢谢!
先看到main函数,找到经过判断之后的关键函数:
这里看到传进来三个参数,但是在函数里面没有 用的这些参数,而且还有一些奇观的函数,这里判断应该是低版本的ida反编译了高版本的编译器编译的程序,所以直接在gdb里面看到汇编代码,直接对汇编进行分析。
先看到这是main函数在调用关键函数的时候的汇编代码
不难看出他只传进去了两个参数,这里看到[rbp-0x8]是我输入,$rsi里面是给出的加密字符串【DASCTF{5c715207e3abed7dfb7c8ea9c82d0e29}】
看到关键函数的汇编:
这里直接把我参数的内容保存下来,【rbp-0x38】是我的输入。
往下看:
这里解析出来了我ida里面没有解析出来的函数,我们把ida修补下:
修完的效果。
接下来就是确定关键函数的参数和这些局部变量的关系,以此来找到关键操作:
首先看前两行直到我们的输入被保存在了两个局部变量里面,然后看到:
相当于是【[rbp-0x20]=malloc(0x80) [rbp-0x20]=我的输入】
所以我的输入的放在了rbp-0x20指向的空间里
因为函数调用规则是从右到左,所以这里应该是:
rsi=[rax-0x7]//去除flag头的flag
rdi=[rbp-0x20]//输入
strcpy(rdi,rsi);
这里看到有一个[rax+0x7],这里是把我输入的flag的头去了,就剩中间的东西了
根据关键的异或,往上往下找到循环异或的代码块:
对其进行分析
0x00005555555553ca <+156>: mov eax,DWORD PTR [rbp-0x24]
0x00005555555553cd <+159>: movsxd rdx,eax #这里eax是计数器i,放到rdx的低位
0x00005555555553d0 <+162>: mov rax,QWORD PTR [rbp-0x20] #将输入放进rax里面
0x00005555555553d4 <+166>: add rax,rdx #rax+rdx就相当于array[i++]的意思,基地址+偏移
0x00005555555553d7 <+169>: movzx esi,BYTE PTR [rax] #给esi赋值,后面异或用到
0x00005555555553da <+172>: mov rdx,QWORD PTR [rip+0x2c2f] # 0x555555558010 <unk_6984657>
0x00005555555553e1 <+179>: mov eax,DWORD PTR [rbp-0x24]
0x00005555555553e4 <+182>: cdqe
0x00005555555553e6 <+184>: add rax,rdx #和上面一样,这里是【偏移量+段地址】
0x00005555555553e9 <+187>: movzx ecx,BYTE PTR [rax] #较小的值拷贝给大的值,剩下0填充例如【0x00000xxxx】
0x00005555555553ec <+190>: mov eax,DWORD PTR [rbp-0x24]
0x00005555555553ef <+193>: movsxd rdx,eax
0x00005555555553f2 <+196>: mov rax,QWORD PTR [rbp-0x20]
0x00005555555553f6 <+200>: add rax,rdx #原理同上,这里rax指向了输入的第[i]位
0x00005555555553f9 <+203>: xor esi,ecx //关键异或
0x00005555555553fb <+205>: mov edx,esi #结果保留在edx里面
0x00005555555553fd <+207>: mov BYTE PTR [rax],dl
0x00005555555553ff <+209>: add DWORD PTR [rbp-0x24],0x1 //计数器+1
0x0000555555555403 <+213>: mov eax,DWORD PTR [rbp-0x24] #计数器+1给到eax
0x0000555555555406 <+216>: movsxd rbx,eax #相当于计数器的值给到rbx的低八位
0x0000555555555409 <+219>: mov rax,QWORD PTR [rbp-0x20] #再把输入放进eax里面,恢复eax在【<+200>】的状态
0x000055555555540d <+223>: mov rdi,rax #把指向输入字符的指针给rdi,当参数给strlen
0x0000555555555410 <+226>: call 0x555555555100 <strlen@plt> //返回输入字符串的长度
0x0000555555555415 <+231>: cmp rbx,rax #当前计数器的值(rbx)和strlen返回值rax比较
0x0000555555555418 <+234>: jb 0x5555555553ca <subfhsadkhjbfleiowiuoyfgshjdvbsmnakl+156>#回到循环头
#rbx小于rax就跳,也就是说:计数器小于字符串长度就继续执行
这里看到有一个变量:【rbp-0x24】,【rbp-0x20】,【rip+0x2c2f】。
【rbp-0x24】:这是计数器【i】;
赋初始值为0;
抑或完+1。
【rbp-0x20】:输入的字符串也就是flag在循环完事之后就是存放输入经过循环的结果
【rip+0x2c2f】:这里存放的是异或用的数组
看具体的异或过程,到底是谁和谁在异或:
0x00005555555553f9 <+203>: xor esi,ecx //关键异或
对象是esi和ecx,看到对esi的操作:
就是输入的每一位。
看到ecx的操作:
总结:我输入的和给出的数组进行按位异或
关键循环分析完毕,在看到下面的代码:
0x000055555555541a <+236>: mov rcx,QWORD PTR [rip+0x2bff] # 0x555555558020 <unk_sa7ydu3jb432>
0x0000555555555421 <+243>: mov rdx,QWORD PTR [rbp-0x18] #rdx==mallco(0x80),给出的flag
0x0000555555555425 <+247>: mov rax,QWORD PTR [rbp-0x20] #输入经过循环异或的flag
0x0000555555555429 <+251>: mov rsi,rdx
0x000055555555542c <+254>: mov rdi,rax #传参
0x000055555555542f <+257>: call rcx #跳到rcx指向的地址,
0x0000555555555431 <+259>: add rsp,0x38 #堆栈平衡,函数准备结束
0x0000555555555435 <+263>: pop rbx
0x0000555555555436 <+264>: pop rbp
0x0000555555555437 <+265>: ret #函数结束
发现有一个[rbp-0x18],没有出现在循环异或的程序里,往上看一下:
发现也是一个类似于[rbp-0x20]的结构,相当于【[rbp-0x18]=mallco(0x80)】
动态调试发现rcx就是一个strcmp函数,比较的是 输入经过循环之后的值 和 一开始给出的值 是否相等。
相等返回1
这题就解了。
然后下载了ida7.5来验证自己的思路,发现每一个函数都判断正确,但是在最后的call rcx那里还需要再完善一下。