title: SMC代码自修改逆向分析
category: 逆向
写在前面
- 加密代码在文末附件
- 用到的idc解密脚本:
#include <idc.idc>
static xor_setp1(){
auto addr = 0x00414c3c; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414C7F;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x7D); //异或的数字根据情况修改
}
}
static xor_setp2(){
auto addr = 0x00414be0; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414C3A;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x43); //异或的数字根据情况修改
}
}
static xor_setp3(){
auto addr = 0x00414a84; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414a84 + 347;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x55); //异或的数字根据情况修改
}
}
static xor_setp4(){
auto addr = 0x00414a30; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414a30 + 83;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x4D); //异或的数字根据情况修改
}
}
static main()
{
xor_setp1();
}
实验分析
第一层
- 用IDA打开7-3的实验练习.exe,进入主函数如下图所示:
- 主函数第一部分,主要的功能是按要求输入一串字符,并判断字符串长度是否等于28,如果不等于则输出"Try again……",等于28则入下一模块
lea eax, [ebp+Str]
push eax
push offset a100s ; "%100s"
call _scanf
add esp, 8
lea ecx, [ebp+Str]
push ecx ; Str
call _strlen
add esp, 4
mov [ebp+var_4], eax
cmp [ebp+var_4], 1Ch (28)
jz short loc_40107F
- 接下来是判断字符串的最后一位(除\n)是否为7Dh("}"),如果不是则将eax置零并返回,之后跳转到最终的栈销毁操作。是则进入下一模块
- 之后是for循环,条件为对byte_414C3C数组的前67个元素循环,在循环内部:
mov edx, [ebp+var_70]
movsx eax, byte_414C3C[edx]
xor eax, 7Dh //将byte_414C3C数组前67位元素每一位都异或7Dh(加密)
mov ecx, [ebp+var_70]
mov byte_414C3C[ecx], al //异或结果覆盖原来的值
jmp short loc_401095
循环结束后:
loc_4010BC:
mov dword ptr [ebp-6Ch], offset byte_414C3C //将byte_414C3C数组赋给dword ptr [ebp-6Ch]
mov esi, esp
push offset unk_414BE0 //将unk414BE0也就是下一层加密代码的地址压入栈
lea edx, [ebp+Str]
push edx
call dword ptr [ebp-6Ch]
add esp, 8
cmp esi, esp
call __chkesp
jmp short loc_4010E1
loc_4010E1:
xor eax, eax //返回结果eax=0
- 了解第一层结构后对第一层代码进行解密,双击byte_414C3C查看内容,推测为SMC加密代码,长度为67,起始地址为00414C3C,结束地址为00414C7F
- 利用IDA执行idc解密脚本
#include <idc.idc>
static xor_setp1(){
auto addr = 0x00414c3c; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414C7F;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x7D); //异或的数字根据情况修改
}
}
static main()
{
xor_setp1();
}
点击File->Script file,打开解密脚本
解密后byte_414C3C变为:
按快捷键C将字符串转换为汇编语言:
右键loc_414C3C,点击create function将汇编语言转换为函数,使其可以进行F5反汇编:
结果如下:
发现其逻辑为判断前五个字符是否为"flag{",不是则跳转到loc_414C7B,返回eax=0,是则进入loc_414C64,分析如下:
xor byte ptr [ecx+edx], 43h //将byte数组前90位逐位异或43h
inc ecx //循环变量自增
cmp ecx, 90 //循环条件:byte数组前90位
jl short loc_414C64 // 即jump less指令,ecx小于90则跳转,do-while循环
add eax, 5
push offset unk_414A84 //将偏移量unk_414A84压入栈,该地址对应的是第三层SMC函数地址
push eax
call edx
pop ecx
pop ecx
回到第一层main函数内给出的第二层自加密第二层代码:
loc_4010BC:
mov dword ptr [ebp-6Ch], offset sub_414C3C
mov esi, esp
push offset unk_414BE0 //这里unk_414BE0嵌套在sub_414C3C,分析知为第二层的地址
lea edx, [ebp+Str]
push edx
call dword ptr [ebp-6Ch]
add esp, 8
cmp esi, esp
call __chkesp
jmp short loc_4010E1
下面对unk_414BE0分析
第二层
- 双击unk_414BE0,如下:
根据代码格式推测为SMC,字符串起始位置的地址为00414BE0,结束地址为00414C3B。对unk_414BE0进行解密,使用idc脚本
#include <idc.idc>
static xor_setp2(){
auto addr = 0x00414be0; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414C3A;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x43); //异或的数字根据情况修改
}
}
static main()
{
xor_setp2();
}
解密后:
按快捷键C转换为汇编语言:
右键create function得汇编函数,进行分析:
.data:00414BE0 sub_414BE0 proc near ; DATA XREF: _main_0+B5↑o
.data:00414BE0
.data:00414BE0 var_8 = dword ptr -8
.data:00414BE0 var_4 = byte ptr -4
.data:00414BE0 arg_0 = dword ptr 8
.data:00414BE0 arg_4 = dword ptr 0Ch
.data:00414BE0
.data:00414BE0 push ebp
.data:00414BE1 mov ebp, esp
.data:00414BE3 push ecx
.data:00414BE4 push ecx
.data:00414BE5 push ebx
.data:00414BE6 mov ebx, [ebp+arg_0]
.data:00414BE9 lea eax, [ebp+var_8]
.data:00414BEC push esi
.data:00414BED push edi
.data:00414BEE xor edx, edx //edx变量置0
.data:00414BF0 mov [ebp+var_8], 93A9A498h //[ebp+var_8]设
.data:00414BF7 mov edi, ebx //为-1817598824
.data:00414BF9 mov [ebp+var_4], dl
.data:00414BFC mov esi, edx
.data:00414BFE sub edi, eax //edi减去eax结果存入edi
.data:00414C00
.data:00414C00 loc_414C00: ; CODE XREF: sub_414BE0+32↓j
.data:00414C00 lea ecx, [ebp+var_8]
.data:00414C03 add ecx, esi //将ecx,esi相加
.data:00414C05 mov al, [edi+ecx] //将edi和ecx对应的数赋给al寄存器
.data:00414C08 xor al, 0CCh //al 异或0CCh
.data:00414C0A cmp al, [ecx] //判断al的值是否和
.data:00414C0C jnz short loc_414C31 //这里为while循环的标志,如果不相等跳到loc_414C31进行栈销毁,否则进入循环内部
.data:00414C0E inc esi //esi自增
.data:00414C0F cmp esi, 4 //判断esi是否和4相等
.data:00414C12 jl short loc_414C00 //如果esi小于4则进行下一层,否则进入if语句内部
.data:00414C14 mov ecx, [ebp+arg_4]
.data:00414C17
.data:00414C17 loc_414C17: ; CODE XREF: sub_414BE0+42↓j
.data:00414C17 xor byte ptr [edx+ecx], 55h //将edx和ecx相加的结果与55h异或
.data:00414C1B inc edx //edx自增
.data:00414C1C cmp edx, 15Bh //循环变量edx和15BH比较
.data:00414C22 jl short loc_414C17 //do-while循环入口,如果edx小于15BH则进入循环,跳转到loc_414C17,否则跳出循环
.data:00414C24 lea eax, [ebx+4] //ebx参数和4相加存入eax
.data:00414C27 push offset unk_414A30 //将地址unk_414A30压入栈传入下面的ecx
.data:00414C2C push eax
.data:00414C2D call ecx //调用ecx函数
.data:00414C2F pop ecx
.data:00414C30 pop ecx
.data:00414C31
.data:00414C31 loc_414C31: ; CODE XREF: sub_414BE0+2C↑j
.data:00414C31 pop edi
.data:00414C32 pop esi
.data:00414C33 xor eax, eax
.data:00414C35 pop ebx
.data:00414C36 mov esp, ebp
.data:00414C38 pop ebp
.data:00414C39 retn
.data:00414C39 sub_414BE0 endp
.data:00414C39
根据分析可知while代码执行的条件为: *(a1+v3)^0xCC == (&v5+v3), 也就是输入的字符异或0xCC对应与v5中的数0x93A9A498相等,求得字符为_ehT
, 由于与输入的字符顺序相反,故输入的第6-9个字符为The_
。
回到第二层sub_414C3C函数,对于传入的地址unk_414A84进行分析
第三层
- 双击unk_414A84
- 分析代码逻辑为SMC自加密,同理使用idc脚本:
#include <idc.idc>
static xor_setp3(){
auto addr = 0x00414a84; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414a84 + 347;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x55); //异或的数字根据情况修改
}
}
static main()
{
xor_setp3();
}
解密结果为:
按快捷键C转换为汇编语言:
右键点击create function转换为函数:
发现出现很多诸如"A-Z",“a-z”,“0-9"以及结尾出现的”="推测进行的是base64加密,分析汇编语言结构:前几个loc函数都有cmp和jle等循环标志,继续分析,注意到
.data:00414AD2 mov dword ptr [ebp+var_10], 'hVmc'
.data:00414AD9 mov dword ptr [ebp+var_10+4], '0NEb'
.data:00414AE0 mov dword ptr [ebp+var_10+8], '=8lR'
.data:00414AE7 stosd
.data:00414AE8 mov [ebp+var_10+0Ch], cl
.data:00414AEB stosd
.data:00414AEC mov edi, ecx
这里传入了固定字符串"cmVhbEN0Rl8="推测为flag的第10到17位,接下来的几个loc函数为base64的右移运算部分。在函数的末尾,注意到
dl和固定字符串在while循环内进行了相等判断,同时可以用OD进行动态运行该程序到第三个函数随便输入的字符串加密后的结果仍为固定字符串,因此对字符串base64解密得:realCtF_
,如果比较正确后则对sub_414A30函数解密并编译,即该程序的第三层嵌套:
回到第二层给出的地址 unk_414A30,进入下一层分析
第四层
- 双击unk_414A30查看
同理分析其为SMC自加密,使用idc脚本解密:
#include <idc.idc>
static xor_setp4(){
auto addr = 0x00414a30; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414a30 + 83;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x4D); //异或的数字根据情况修改
}
}
static main()
{
xor_setp4();
}
解密结果:
按快捷键C转换为汇编代码:
右键create function生成汇编函数:
分析逻辑,注意到函数压入了6FF22h,68344360h,7574766Bh,转换为字符串为"kvtu`C4h\"o
",在loc_414A55函数中,edx自增后,将byte ptr [ebp+edx+var_C]和bl(之前存入得三串字符串)判断是否相等,并且如果不相等则跳转到本身,分析为while循环,在循环内部,edi减去了esi,而esi为参数地址,edi为字符串,而在loc_414A68中又进行了dec ecx
即ecx自减,可知逻辑为将参数地址所有的字符减1和字符串比较,所以输入的字符串应该为"just_B3g!n"。
总结
综上所述,flag为 :flag{The_realCtF_just_B3g!n}
附件
链接:https://pan.baidu.com/s/1WsgS9GHjbZOjjS6YuJiYhQ
提取码:j2km