SMC,即Self Modifying Code,动态代码加密技术,指通过修改代码或数据,阻止别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果。
SMC的实现方式有很多种,可以通过修改PE文件的Section Header、使用API Hook实现代码加密和解密、使用VMProtect等第三方加密工具等。关于具体的实现方式可以参考合天的文章:探究SMC局部代码加密技术以及在CTF中的运用 - SecPulse.COM | 安全脉搏
SMC一般有俩种破解方法,第一种是找到对代码或数据加密的函数后通过idapython写解密脚本。第二种是动态调试到SMC解密结束的地方dump出来。
SMC的实现是需要对目标内存进行修改的,.text一般是没有写权限的。那么就需要拥有修改目标内存的权限:
因此也可以观察是否有这俩个函数来判断是否进行了SMC。
文件先查壳,无壳,32bit文件,一打开就可以发现VitualProtect函数,这里对内存权限进行了修改,大概率是SMC。
这里的逻辑大致看下,通过scanf让我们输入一个长度为24的flag,再wrong函数和omg。
key = 0x66, 0x6B, 0x63, 0x64, 0x7F, 0x61, 0x67, 0x64, 0x3B, 0x56, 0x6B, 0x61, 0x7B, 0x26, 0x3B, 0x50, 0x63, 0x5F, 0x4D, 0x5A, 0x71, 0x0C, 0x37, 0x66
flag = ''
for i in range(24):
if i % 2 == 1:
flag += chr(key[i] + i)
else:
flag += chr(key[i] ^ i)
print(flag)
这里解出一个flag{fak3_alw35_sp_me!!} 是个假flag。
接着往下看,可以看到encrypt,并且encrypt函数打开出错了。
可以判断是在encrypt处进行了SMC加密,随便下个断点,用 Local windows debugger调试
根据前面得判断,输入长度为24得flag 123456789123456789123456
这里,F7单步走到00401833的call处,单步进入_Z7函数内
点击__Z7encryptPc 先用U将其设为无定义
接着选中下面所有数据内容,按C转换为代码。
选择Force,再点击yes
之后再点击_Z7进行P定义为函数,再用F5反编译。这里可以得到encrypt函数
下面还有一个函数不要忽略了,同样P定义为函数,再F5反编译。
通过第一个encrpyt函数 逆向一下,
hh = 'hahahaha_do_you_find_me?'
v2 = [0x0E, 0x0D, 0x9, 0x6, 0x13, 0x5, 0x58, 0x56, 0x3E, 0x6,
0x0C, 0x3C, 0x1F, 0x57, 0x14, 0x6B, 0x57, 0x59, 0x0D]
flag = []
for i in range(19):
flag.append(chr(v2[i] ^ ord(hh[i])))
解出flag{d07abccf8a410c 明显还有后半部分,看第二个函数,大致的意思就是%tp&:这五个字符和某个数字异或得到最后的flag,已知是flag的最后一位一定是},那么就很好求了。
v3 = [37, 116, 112, 38, 58]
key = ord('}') ^ 58
for i in range(5):
flag.append(chr(v3[i] ^ key))
print(''.join(flag))
这样就可以解出flag{d07abccf8a410cb37a}
在导入表中看到VirtualProtect函数
在最下面可以看到一个错误地址。点进去 看汇编,
大量的数据,可以直接尝试下断点动态调试。用本地win就好
F8步过,进入数据处,这里会提示是否让IDA自己根据RIP生成代码,选no,因为这里IDA已经不能自主分辨主函数的结构了。
No了后,从上方的main头一直选到第一个retn处,按C后选择Force强制转换为代码,之后直接F5反编译就好了。