分析
照例先丢exeinfo, 没壳, 运行一下, 复制一下字符串, 拖进IDA, main函数直接出 (图中已经加了些注释)
函数逻辑还是很清晰的:
- scanf进flag
- 获取flag长度(24)
- 将flag的值赋给Des
- wrong() 函数进行第一次判断
- omg() 函数进行第二次判断
- 对encrypt进行处理
- encrypt() 函数判断 Des
- finally() 函数判断第二次 Des
接下来看这些具体的函数:
wrong:
omg:
encrypt:
SMC + 栈平衡
如果点进去, 会发现这个根本不是一个函数, 直接看汇编代码发现:
很明显这不是一个函数, 但是main函数中对其调用并传参数, 可以判断这里应该是一个函数
finally:
SMC
点进去发现是一个奇怪的函数
定义了一堆变量, 但是并没用调用, 而且函数逻辑也非常奇怪, 直接看汇编
果然也是被混淆了
到这里初步完成了程序分析
解题思路
获得原始的字符串输入
可以通过逆omg和wrong函数来获得原始输入的flag
1 | ciper = [0x00000066, 0x0000006B, 0x00000063, 0x00000064, 0x0000007F, 0x00000061, 0x00000067, 0x00000064, 0x0000003B, 0x00000056, 0x0000006B, |
很明显这个原始输入的flag是假的(废话), 但也不是没用
得到原始输入flag后, 就有两个思路来解encrypt 和 finally:
- 打断点动调释放出encruypt和finally函数, 然后dump出内存看含函数
- 打断点动调,靠IDA的重新定位函数入口直接看函数
修复栈平衡 + 动调获得encrypt
这里选择的是第二中办法(dump还不太熟练)
先来修一下栈平衡
在main函数的汇编代码中找到调用encrypt函数的地方
选中, 按 alt+k ,将偏移地址改为0
下面的finally同理,之后可以开始动调还原函数了
先在 if (encrtpt(Des)) 处下断点, 然后直接IDA本地动调
程序出来后,输入原始字符串, 程序会在断点处停下:
进入encrypt函数,
从函数开头到那一大段数据全部选中
然后按u (取消原来的定义)
回到函数开头, 按p (重新识别函数)
按f5后即可看到encrypt函数的原样了
简单的逻辑 ~ ( ̄︶ ̄)
写一下脚本
1 | cipher = [0x0000000E, 0x0000000D, 0x00000009, 0x00000006, 0x00000013, 0x00000005, 0x00000058, 0x00000056, 0x0000003E, 0x00000006, 0x0000000C, 0x0000003C, 0x0000001F, 0x00000057, 0x00000014, 0x0000006B, 0x00000057, 0x00000059, 0x0000000D] |
获得finally
还需要下半部部分flag, 继续看finally
还是一样, ,选中全部, 按u ,在函数开头按p, 然后f5
特殊的在于:
函数调用了随机数, 但是随机数一般很难确定, 所以只能猜 (其实卡了好久, 以为代码分析出问题了 X﹏X)
看大佬们的wp后知道: 用某个固定值对v3这个字符串进行异或,由于flag最后一定是 } 结尾, 所以可以出来了
1 | # print(chr(ord(':') ^ ord('}'))) |
总结及一些问题
总结
- 很适合用来练手的SMC题(没有加反动调太棒了)
- 思维有点固定了, 没想到最后的函数是一个异或
收获
- 一开始看到这条函数脑子傻了, 以为是什么复杂的vmp壳, 其实和这个函数只是取消对encrypt函数的读写保护
- 原本想要找到SMC的解密函数的, 但是死活找不到, 后来看别人的wp了解到这两函数加壳了, 这也是为什么运行后encrypt函数还是混过的原因 (静态壳还在)
在这里留下你的足迹...