首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
逆向工程
发新帖
12
5
[原创]SMC自修改代码
发表于: 2025-11-9 18:37
12802
[原创]SMC自修改代码
南行
3
2025-11-9 18:37
12802
## 简介 自修改代码(SMC, Self Modifying Code)是在程序执行过程中修改其自身指令的技术。通常,这种做法旨在通过减少指令路径长度来提高性能,或者为了避免重复代码,从而简化维护。自修改代码一般是有意进行的,目的是优化或保护程序的运行,而不是由于错误导致的意外修改。 在逆向工程中,自修改代码通常表现为大量的无序代码,未被执行的部分无法直接分析。通过动态修改代码或数据,程序能够在运行时自我解密,防止静态分析工具的破解。这种技术常用于动态代码加密中,旨在通过修改代码来阻止逆向分析,直到程序执行到相应时刻才会恢复正常的可执行代码,从而达到反调试、反逆向的效果。 ## 实现与破解 SMC 的实现方式有很多种,可以通过修改 PE 文件的 Section Header、使用 API Hook 实现代码加密和解密、使用 VMProtect 等第三方加密工具等。 在 CTF 中常见的形式是首先对`.text`段的代码进行加密,然后通过 SMC 进行自解密修改。 而一般程序的`.text`段是没有写权限的,在进行自修改之前,程序需要修改目标内存的权限。 ### 内存权限修改 在不同的系统中定义有不同的 API 函数来修改内存权限。 - 在 Linux 系统中,可以通过`mprotect`函数修改目标内存的权限。 `mprotect`是 Linux 系统的一个 API 函数,可以改写内存权限。这个函数的原型如下: ```c int mprotect(void *addr, size_t len, int prot); ``` 第一个参数:开始地址(为了页对齐,该地址必须是一个内存页的起始地址,即页大小整数倍,1 页 = 4k = 0x1000) 第二个参数:指定长度(长度也应该是页的整倍数,即 0x1000 的整数倍) 第三个参数:指定属性(r=4(读)、w=2(写)、x=1(执行)) - 在 Windows 系统中,调用`VirtualProtect`函数实现内存权限的修改。 `VirtualProtect`是 Windows 操作系统中的一个 API 函数,它允许应用程序改变一个内存页的保护属性。这个函数的原型如下: ```C BOOL VirtualProtect( LPCVOID lpAddress, // 要改变保护属性的内存页的起始地址 SIZE_T dwSize, // 内存页的大小 DWORD flNewProtect, // 新的保护属性 PDWORD lpflOldProtect // 存储旧的保护属性 ); ``` 第一个参数:起始地址,同样也必须对齐,即页大小整数倍,1页 = 4k = 0x1000 第二个参数:指定长度(长度也应该是页的整倍数,即 0x1000 的整数倍) 第三个参数: - `PAGE_READONLY`:页面为只读。 - `PAGE_READWRITE`:页面为可读写。 - `PAGE_WRITECOPY`:页面为写时复制(COW)模式。 - `PAGE_EXECUTE`:页面为可执行。 - `PAGE_EXECUTE_READ`:页面为可执行和可读。 - `PAGE_EXECUTE_READWRITE`:页面为可执行、可读和可写。 - `PAGE_NOACCESS`:页面无访问权限(即不可读、不可写、不可执行)。 第四个参数:指向一个 `DWORD` 类型的变量,函数会把原先的保护标志存储在此变量中。如果不关心旧的保护属性,可以传入 `NULL`。 因此我们也可以通过搜索程序是否使用了这两个函数来结合判断是否进行了 SMC。 ### 实现 >环境为 Linux 系统环境 gcc 编译器 下面我们通过实现简单的 SMC 代码来理解其原理。 我们编写一段 C 语言代码,在其中插入汇编代码,功能为输出字符串。 进行编译后,将`func`函数部分的字节码进行`xor 0xAA`加密,然后作为我们要解密的函数。 ```c #include <stdio.h> #include <stdlib.h> void func(){ __asm__ __volatile__( "mov $1, %rdi\n\t" "mov $0x0a434d53, %rsi\n\t" "push %rsi\n\t" "mov %rsp,%rsi\n\t" "mov $4, %rdx\n\t" "mov $1, %rax\n\t" "syscall\n\t" "mov $60, %rax\n\t" "xor %rdi, %rdi\n\t" "syscall\n\t" ); } void main(int argc, char *argv[]) { func(); } ``` 接下来我们编写 SMC 代码,将加密后的函数字节码作为数据插入到代码中。 加密算法为`xor`,异或值是`0xAA`,同样解密算法也是`xor`。 首先调用内存权限修改函数给加密函数内存添加可写权限,然后再通过解密算法解密函数的代码数据。 这就实现了一段简单的 SMC 代码。 ```c #include <stdint.h> #include <stdlib.h> #include <sys/mman.h> #include <unistd.h> //被加密的函数字节码 unsigned char code[] = { 0xff, 0xe2, 0x23, 0x4f, 0xe2, 0x6d, 0x6d, 0xab, 0xaa, 0xaa, 0xaa, 0xe2, 0x6d, 0x6c, 0xf9, 0xe7, 0xe9, 0xa0, 0xfc, 0xe2, 0x23, 0x4c, 0xe2, 0x6d, 0x68, 0xae, 0xaa, 0xaa, 0xaa, 0xe2, 0x6d, 0x6a, 0xab, 0xaa, 0xaa, 0xaa, 0xa5, 0xaf, 0xe2, 0x6d, 0x6a, 0x96, 0xaa, 0xaa, 0xaa, 0xe2, 0x9b, 0x55, 0xa5, 0xaf, 0x3a, 0xf7, 0x69 }; void main() { size_t page_size = sysconf(_SC_PAGE_SIZE); size_t code_size = sizeof(code); mprotect((void *)((uintptr_t)code & ~(page_size - 1)), 0x1000, 7); // 解密函数字节码 for (size_t i = 0; i < code_size; i++) { code[i] ^= 0xAA; } // 将解密后的字节码转换为函数指针调用 void (*func)() = (void (*)())code; func(); } ``` ### 破解 针对这样的 SMC 我们有两种破解方法: - 动态解密法: 动态调试目标程序,待 SMC 代码运行解密后直接使用 IDA 分析。 ida 反编译后分析代码逻辑,发现这跟我们写的代码逻辑大差不差。  接下来我们通过动态解密法破解这个 SMC 程序。 我们在最后一行处打一个断点,然后动态调试让程序运行到这里。  到了这里`code`函数已经解密完毕,我们跟进查看。  在函数开始处按快捷键`c`将数据定义为代码,然后按快捷键`p`将代码定义为函数。 之后`F5`反编译。  可以看到反编译成功,这就是动态调试解密法。 我们也可以使用 Dump 功能将解密后的 SMC 字节数据 Dump 出来,对密文进行静态替换,再使用 IDA 进行分析。 不过这种方法对比上一种方法或者是下面的静态解密法来说比较麻烦,这里就不细讲了。 - 静态解密法: 找到对代码或数据加密的逻辑后,根据代码逻辑编写 idapython 逆向解密脚本解密代码。 分析代码逻辑,程序对函数起始地址`0x404060`到相对偏移`0x35`处的代码进行异或解密,异或值为`0xAA`  - idapython解密脚本 ```python import idc addr = 0x404060 for i in range(0x35): val = idc.get_bytes(addr + i, 1) val = ord(val) val ^= 0xAA idc.patch_byte(addr + i, val) ``` 快捷键`Shift+F2`打开运行脚本代码窗口,选择 Python。 然后将代码复制进去运行即可。  之后就是定义代码定义函数,然后再进行反编译了。 同样,反编译成功。  总之这两种方法就是哪个方便用哪个。 ## 例题 ### 动态解密法 >例题:2024 Newstar SMC - 分析 拿到附件,程序为 Linux 下的 ELF 文件,ida 反编译分析代码逻辑。 一眼丁真,发现程序调用了`mprotect`修改`encrypt`函数内存权限,判断可能存在 SMC。 然后程序通过`scanf`函数向变量`s`中输入字符串,长度要求必须为 28 字节。 之后的代码逻辑很明显就是通过循环异或对`encrypt`函数进行解密了。  这里我们对照着`encrypt`函数的情况来查看一下。 很明显`encrypt`函数的汇编代码都是有问题的,这就是进行过加密的函数。 接下来程序会通过上面的`for`循环逻辑对每字节进行异或解密。 然后就是通过`encrypt`函数判断输入是否正确了。  这里我们可以通过动态解密法来破解 SMC。 在比较输入处打一个断点,然后动态调试让程序运行起来,这样可以直接让程序执行完 SMC 代码。  运行完之后我们进入`encrypt`函数查看代码。  可以看到代码仍然是乱序的。  我们首先在`encrypt`函数处按快捷键`u`将其取消定义,然后再按快捷键`c`将其解释为代码,然后再按快捷键`p`将其解释为函数。  然后即可`F5`反编译,反编译后我们就可以看到`encrypt`函数的代码逻辑了。 可以看到是一段非常复杂的等式,我们直接通过 z3 来进行求解。  - exp ```python from z3 import * import struct s=z3.Solver() v2,v3,v4,v5,v6,v7,v8=Ints("v2 v3 v4 v5 v6 v7 v8") s.add(5 * (v3 + v2) + 4 * v4 + 6 * v5 + v6 + 9 * v8 + 2 * v7 == 0xD5CC7D4FF) s.add(4 * v8 + 3 * v5 + 6 * v4 + 10 * v3 + 9 * v2 + 9 * v7 + 3 * v6 == 0x102335844B) s.add(9 * v6 + 4 * (v5 + v4) + 5 * v3 + 4 * v2 + 3 * v8 + 10 * v7 == 0xD55AEABB9) s.add(9 * v3 + 5 * v2 + 9 * v8 + 2 * (v4 + 2 * v5 + 5 * v6 + v7) == 0xF89F6B7FA) s.add(5 * v6 + 9 * v5 + 7 * v2 + 2 * v3 + v4 + 3 * v8 + 9 * v7 == 0xD5230B80B) s.add(8 * v8 + 6 * v5 + 10 * v4 + 5 * v3 + 6 * v2 + 3 * v7 + 9 * v6 == 0x11E28ED873) s.add(v2 + 4 * (v4 + v3 + 2 * v5) + 9 * v6 + v7 + 3 * v8 == 0xB353C03E1) if s.check() == sat: m=s.model() v=[0]*7 v[0]=m[v2].as_long() v[1]=m[v3].as_long() v[2]=m[v4].as_long() v[3]=m[v5].as_long() v[4]=m[v6].as_long() v[5]=m[v7].as_long() v[6]=m[v8].as_long() dec = b''.join(struct.pack('<I', val) for val in v) result = dec.decode('ascii') print(result) #flag{D0_Y0u_Kn0w_sMC_4nD_Z3} ``` ### 静态解密法 >例题:[网鼎杯 2020 青龙组]jocker - 分析 ida 反编译分析,看到`// positive sp value has been detected, the output may be wrong!`。 这是因为栈不平衡,ida 识别报错。  点击菜单的 Options-> General->Stack pointer 来打开 ida 的栈指针设置,分析栈不平衡的位置。  发现异常处,程序在调用了`__Z7encryptPc`函数和`__Z7finallyPc`函数后栈帧值不断减少。  按快捷键`alt+k`,将两处调用函数地方的汇编指令栈帧值修改为 0。  之后再进行反编译,可以看到 ida 没有再报错了。 接下来我们分析程序逻辑。 `main`函数调用了`VirtualProtect`函数修改`encrypt`函数的内存权限为可写。 然后调用`scanf`函数向`Str`变量输入字符串,如果输入长度不等于 24 则报错退出。 然后将输入的字符串复制到`Destination`变量中,将`Str`变量作为`wrong`函数和`omg`函数的参数传入处理。 最后的`for`循环就是 SMC 的解密逻辑。  接下来我们跟进查看`wrong`函数和`omg`函数的代码逻辑。 `wrong`函数是一个加密逻辑,对奇数进行偏移加密,对偶数进行异或加密。 接下来分析`omg`函数。  可以看到`omg`函数将输入和`unk_4030c0`数据进行了按位比较,判断`unk_4030c0`应该就是密文。  我们根据密文和代码逻辑,尝试写一下解密逻辑。 ```python enc = [0x66, 0x6B, 0x63, 0x64, 0x7F, 0x61, 0x67, 0x64, 0x3B, 0x56, 0x6B, 0x61, 0x7B, 0x26, 0x3B, 0x50, 0x63, 0x5F, 0x4D, 0x5A, 0x71, 0x0C, 0x37, 0x66] result="" for i in range(len(enc)): if i % 2 == 1: result += chr(enc[i] + i) else: result += chr(enc[i] ^ i) print(result) # flag{fak3_alw35_sp_me!!} ``` 试了一下,这个 flag 是假的。 接下来继续逆向 SMC 部分。 可以看到程序对`encrypt`函数进行了逐字节异或,解密`encrypt`函数。 我们这里就使用静态解密法来编写 idapython 代码解密`encrypt`函数。  - idapython解密脚本 根据解密`encrypt`函数逻辑编写 idapython 解密脚本。 ```python import idc addr = 0x401500 # encrypt函数的地址 for i in range(187): #获取地址字节,1代表一个字节 b = get_bytes(addr + i, 1) #修改地址内存的字节 #第一个参数为指定地址,第二个参数为修改后的值 idc.patch_byte(addr + i, ord(b) ^ 0x41) ``` 然后运行 idapython脚本,这里前面讲过了。 接下来我们修复解密后的`encrypt`函数代码。  从函数开始处开始按快捷键`u`将所有指令设置为无定义。  然后在函数开头处按快捷键`c`将所有数据内容转换为代码。  最后在函数开始处按快捷键`p`定义为函数。  然后就可以`F5`反编译了。 继续分析代码逻辑,`encrypt`函数的参数是存储输入内容的变量。 `encrypt`函数将输入与`Buffer`变量存储的内容进行逐字节异或,如果最后的结果不等于密文则报错。  我们跟进查看`Buffer`变量的内容,可以看到是`wrong`函数中输出的字符串。  我们根据代码逻辑,将密文与`Buffer`变量进行逐字节异或运算后即可得出符合的输入。 根据逻辑编写解密代码。 - exp ```python key='hahahaha_do_you_find_me?' enc=[0x0000000E, 0x0000000D, 0x00000009, 0x00000006, 0x00000013, 0x00000005, 0x00000058, 0x00000056, 0x0000003E, 0x00000006, 0x0000000C, 0x0000003C, 0x0000001F, 0x00000057, 0x00000014, 0x0000006B, 0x00000057, 0x00000059, 0x0000000D] flag="" for i in range(len(enc)): flag+=chr(ord(key[i])^enc[i]) print(flag) #flag{d07abccf8a410c ``` 只解出了前半部分,继续分析程序。 可以看到,在`encrypt`函数调用成功之后继续调用了`finally`函数。  跟进分析`finally`函数代码逻辑。 程序中将`%tp&:`复制到了`v3`变量中,我们可以猜测这是后半部分的密文。 尝试用先前逻辑进行解密,结果不行。 我们知道 flag 的最后一位一定是`}`,我们可以尝试将`}`与密文最后一位进行异或。 然后将异或结果与密文进行异或,尝试是否可以解密。  尝试成功,得到flag。 - exp ```python key='hahahaha_do_you_find_me?' enc=[0x0000000E, 0x0000000D, 0x00000009, 0x00000006, 0x00000013, 0x00000005, 0x00000058, 0x00000056, 0x0000003E, 0x00000006, 0x0000000C, 0x0000003C, 0x0000001F, 0x00000057, 0x00000014, 0x0000006B, 0x00000057, 0x00000059, 0x0000000D] flag="" for i in range(len(enc)): flag+=chr(ord(key[i])^enc[i]) print(flag) enc_end="%tp&:" key=ord(enc_end[-1])^ord("}") for i in range(len(enc_end)): flag+=chr(ord(enc_end[i])^key) print(flag) #flag{d07abccf8a410cb37a} ``` ## Reference ><a href="elink@702K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4k6s2g2X3M7#2)9J5k6r3E0A6L8X3N6Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8U0t1H3x3U0m8Q4x3V1j5H3y4g2)9J5c8U0t1J5i4K6u0r3M7$3#2U0i4K6t1#2c8e0g2Q4x3U0f1^5b7g2)9J5y4f1p5H3i4K6t1#2c8e0g2Q4x3U0g2m8c8W2)9J5y4e0R3$3i4K6t1#2c8e0N6Q4x3U0g2m8x3q4)9J5y4e0V1@1i4K6t1#2c8e0N6Q4x3U0g2m8z5g2)9J5y4f1t1$3i4K6u0r3">smc加密研究 - V1ct0r的博客 (gdufs-king.github.io)</a> <a href="elink@d55K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2S2L8Y4q4#2j5h3&6C8k6g2)9J5k6h3y4G2L8g2)9J5c8Y4m8G2M7%4c8Q4x3V1k6A6k6q4)9J5c8U0t1K6z5o6j5@1y4g2)9J5x3$3R3K6i4K6u0V1y4b7`.`.">SMC自解码总结-安全客 - 安全资讯平台 (anquanke.com)</a> <a href="elink@c86K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2K6k6h3y4H3N6h3I4K6k6g2)9J5k6h3y4G2L8g2)9J5c8X3q4J5j5$3S2A6N6X3g2K6i4K6u0r3x3e0V1%4x3U0R3#2i4K6u0W2K9s2c8E0L8l9`.`.">探究SMC局部代码加密技术以及在CTF中的运用 - SecPulse.COM | 安全脉搏</a> [[原创]VC实现SMC加密技术-编程技术-看雪-安全社区|安全招聘|kanxue.com](https://bbs.kanxue.com/thread-201708.htm)
登录后可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
上传的附件:
smc.zip
(38.41kb,16次下载)
收藏
・
12
点赞
・
5
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
asdll
你的分享对大家帮助很大,非常感谢!
2025-11-12 12:32
值得怀疑
+5
感谢你分享这么好的资源!
2025-11-12 11:12
马来
这个讨论对我很有帮助,谢谢!
2025-11-11 10:33
考拉
这个讨论对我很有帮助,谢谢!
2025-11-11 10:10
我的小拇指啊
你的帖子非常有用,感谢分享!
2025-11-9 20:46
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
2
)
MsScotch
雪 币:
4164
活跃值:
(4901)
能力值:
( LV2,RANK:10 )
在线值:
发帖
3
回帖
394
粉丝
5
关注
私信
MsScotch
2
楼
mark
2025-11-9 22:58
0
michallsun
雪 币:
4
活跃值:
(1301)
能力值:
( LV2,RANK:10 )
在线值:
发帖
2
回帖
89
粉丝
0
关注
私信
michallsun
3
楼
感谢分享
2025-11-24 07:47
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
南行
3
6
发帖
5
回帖
140
RANK
关注
私信
他的文章
[原创]Linux内核modprobe_path覆盖利用技术
19219
[原创]RC4算法逆向分析
12405
[原创]SMC自修改代码
12802
花指令分析
13355
[原创Linux程序保护机制
7236
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
谁下载
×
huangyalei
michallsun
智童
dxzhang
appview
诶一
byhk
AHook
ONewTach
npc0vo
mb_lthgjpwj
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部