smc

参考文档

探究SMC局部代码加密技术以及在CTF中的运用 – Sw’Blog

SHangwendada/CodeRepo: 这是SWDD的逆向技术实现代码存储仓库~

http://gdufs-king.github.io/2020/05/22/smc%E5%8A%A0%E5%AF%86%E7%A0%94%E7%A9%B6/

https://bbs.kanxue.com/thread-263816-1.htm

原理

SMC,即Self Modifying Code,动态代码加密技术,指通过修改代码或数据,阻止别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果,而计算机病毒通常也会采用SMC技术动态修改内存中的可执行代码来达到变形或对代码加密的目的,从而躲过杀毒软件的查杀或者迷惑反病毒工作者对代码进行分析。通常来说,SMC使用汇编去写会比较好,因为它涉及更改机器码,但SMC也可以直接通过C、C++来实现。

简单代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
#include<stdint.h>

void show(){
printf("sdjasdjals");
}


int main(){
uint8_t *data=(uint8_t *)show;
for(int i=0;i<123;i++){
printf("%x",data[i]);
}

}

输出

1
55 48 89 e5 48 83 ec 20 48 8d d a1 2a 0 0 e8 4c 15 0 0 90 48 83 c4 20 5d c3 55 48 89 e5 48 83 ec 30 e8 8 1 0 0 48 8d 5 d1 ff ff ff 48 89 45 f0 c7 45 fc 0 0 0 0 eb 24 8b 45 fc 48 98 48 8b 55 f0 48 1 d0 f b6 0 f b6 c0 89 c2 48 8d d 64 2a 0 0 e8 4 15 0 0 83 45 fc 1 83 7d fc 7a 7e d6 b8 0 0 0 0 48 83 c4 30 5d c3 90 90 90 90 90 90 90 90 90 90 

后面的90 90就是内存加载的问题了 这里不讲

这段代码直接的作用就是读取show函数的地址 获取函数的字节码

image-20250319210738319

ida中我们可以直接看到

这里直接模仿ctf中最常见的xor+smc

先写一个简单的看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdint.h>
#include<stdio.h>

void fun()
{
char flag[] = "flag{this_is_test}";
printf("%s", flag);
}
int main()
{

uint8_t* a = (uint8_t*)fun;
uint8_t* b = a;
for (; *a != 0x90 && *(a + 1) != 0x90; a++)
{
*a = *a ^ 3;
printf("%x", *a);
}
fun();
}

image-20250319213138829

我们进行简单的修改 给个权限 VirtualProtect 这里也牵扯到一些技巧 smc需要修改页面的权限 所以可以查看导入表 看看都有啥

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
#include <windows.h>
#include <stdio.h>
#include<stdint.h>

void fun()
{
char flag[] = "flag{this_is_test}";
printf("%s", flag);
}

int main()
{
uint8_t* a = (uint8_t*)fun;

// VirtualProtect to make the function's memory readable, writable, and executable
DWORD oldProtect;
VirtualProtect(a, 4096, PAGE_EXECUTE_READWRITE, &oldProtect);

// Now you can modify the memory and call fun() as needed
for (; *a!=0x90&&*(a+1)!=0x90; a++)
{
*a = *a ^ 3; // Modifying memory (not recommended for production)
printf("%x", *a);
}

fun(); // Calling the function
return 0;
}

输出

1
564b8ae64b80ef434bbb656f626478776b6a4b8a46e34bbb705c6a705c7766704b8a46eb65c446f3777ec546f134b8e46e34b8ac14b8ee777933ebac603

未xor

1
554889e54883ec4048b8666c61677b746869488945e048b8735f69735f746573488945e866c745f0747dc645f20488d45e04889c2488dd747a00e89f630flag{this_is_test}

image-20250319215103251

这里没输出内容是以为内存被修改了

image-20250319215531363

我们打个断点开调试也可以看到fun函数出现了改变 (这里我直接拿g++搞的 vs的话输出是一样的)

smc加密解密函数

ctf中大多都是将加密后的函数丢进exe中 写一个解密函数进行解密 解出函数再去调用函数进行加密或者进行一些其他的操作 这里使用idc或者idapython进行事先加密

(我一直也好奇出题人是这么操作 获得可以进行smc解密的字节码 )

1
2
3
4
5
6
7
8
9
10
#include <idc.idc>

static main()
{
auto addr =140003000 ;
auto i;
for(i = 0; addr+i != 140003042; ++i){
PatchByte(addr+i,Byte(addr+i)^0x3);
}
}

image-20250319224923975

将正常的exe用脚本加密patch保存一下

image-20250319225220836

运行程序 smc解密 完毕 燃尽了