自修改代码(Self-Modifying Code),指在段代码执行前对它进行修改。把代码以加密的形式保存在可执行文件中(或静态资源中),然后在程序执行的时候进行动态解密。这样我们在采用静态分析:时,看到的都是加密的内容,从而减缓甚至队目止静态分析。
一段展示SMC思路的代码:

1
2
3
4
5
6
7
if(运行条件满足){
DecryptProc(Address of check)//对check代码解密
//......
check();//调用check
//......
EncryptProc(Address of check)//再对代码进行加密,防止程序被dump
}

以下打造一个使用SMC进行静态分析对抗的示例,首先正常写一个程序:

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
int check(int in) {
return in == 12345;
}
int main()
{
int input;
scanf("%d", &input);
if (check(input))
printf("good!");
return 0;
}

然后我们想把check这个函数保护起来,先把其机器码摘出来进行加密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<stdio.h>
#include<windows.h>
int check(int in) {
return in == 12345;
}
void decryption()
{
DWORD old;
VirtualProtect(check, 4096, PAGE_EXECUTE_READWRITE, &old);//VirtualProtect这个存在于Windows库里的函数作用为对函数进行权限修改
int de = 0x11;
for (int j = 0; j < 36; j++)
{
*((char*)check + j) ^= de;
de++;
}
VirtualProtect(check, 4096, old, NULL);
}
int main()
{
int input;
scanf("%d", &input);



decryption();



if (check(input))
printf("good!");
return 0;
}
//unsigned char ida_chars[] =
//{
// 0x55, 0x8b, 0xec, 0x51, 0x81, 0x7d, 0x08, 0x39, 0x30, 0x00,
//0x00, 0x75, 0x09, 0xc7, 0x45, 0xfc, 0x01, 0x00, 0x00, 0x00,
//0xeb, 0x07, 0xc7, 0x45, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x8b,
//0x45, 0xfc, 0x8b, 0xe5, 0x5d, 0xc3
//};
// int main()
// {
// int a = 0x11;
// for (int i = 0; i < sizeof(ida_chars); i++)
// {

// ida_chars[i] ^= a;
// a++;
// printf("%02x",ida_chars[i]);
// }
// return 0;
// }

另一种常见的实现方法是通过新增一个具备RWX属性的程序段,将需要保护的代码书写在其中,这样就可以避免调用 virtualAl1oc/virtualProtect/mprotect 这类API来暴露SMC 的意图。

对抗思路:

能动态调试最好直接动态调试,因为在程序运行的某一时刻,它一定是解密完成的,这时也就暴露了,使用动态分析运行到这一时刻即可过掉保护。
其次是根据静态分析获得解密算法,写出解密脚本提前解密这段代码。
解密得到的机器码可以通过IDAPython的patch_byte接口很方便地写回。