关闭页面特效

SMC的实现方法

以前以为SMC只能用汇编写,现在翻blog发现可以用vs写

SMC的原理就是程序中的部分代码在运行之前是一坨乱七八糟的数据,但在执行了相关解密函数对其解密后,它就变成了正常的可执行代码。

可以大致认为是下面这个逻辑

proc main: ............ IF .运行条件满足 CALL DecryptProc(Address of MyProc);对某个函数代码解密 ........ CALL MyProc ;调用这个函数 ........ CALL EncryptProc(Address of MyProc);再对代码进行加密,防止程序被Dump ...... end main

大概包括加解密算法以及寻找到加解密部分的地址这俩部分。加解密想怎么写都行,重点在于如何寻址。

下面介绍几种实现方法,以MyProc代指需要SMC保护的代码

1|0给定固定地址


这种写法是先把代码写好,然后动调拿到MyProc的首地址RVA,然后通过函数的ret指令计算出代码块的大小。把MyProc首地址RVA写入解密代码,再根据代码块大小进行加解密。

在保存程序后,再在二进制文件中找到MyProc的地址FOA,对其字节码加密,然后将其替换。

另外,还有一种简单的写法是:先将要进行SMC的部分的代码块取出来,将其加密后再放入数组中,在MyProc内先执行解密代码将该数据块解密,再通过指针调用这一块代码。

取RVA然后直接改程序文件这种写炸了,指针的写出来了,大概是这样

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <windows.h> char a[] = "TSCTF-J{do_you_think_this_is_a_real_flag}"; //char flag[] = "TSCTF-J{W3lC0M3_2_ReVEr$E_xOr_1s_$O0o_e2}"; int data[50] = // { 18, 20, 7, 17, 4, 110, 10, 58, 25, 124, 32, 14, 122, 6, 123, 22, 100, 8, 6, 48, 4, 22, 34, 117, 27, 0, 36, 18, 40, 4, 105, 42, 57, 67, 43, 85, 13, 60, 5, 83, 19 }; char code[] = // 416140 { 0x13, 0xe, 0xcf, 0xa3, 0xe, 0xc5, 0xaa, 0x56, 0xe, 0xcf, 0xb, 0x56, 0xe, 0xcf, 0x13, 0x5e, 0x81, 0x3, 0xba, 0x46, 0x46, 0x46, 0x46, 0x81, 0x3, 0xba, 0x46, 0x46, 0x46, 0x46, 0xad, 0x7d, 0xcd, 0x3, 0xba, 0xe, 0x25, 0x96, 0xe, 0xcd, 0x3, 0x56, 0xe, 0x47, 0x96, 0x49, 0xf0, 0x46, 0x49, 0xf8, 0x86, 0x75, 0x3, 0xba, 0xc5, 0xb6, 0x0, 0xcf, 0x84, 0xcd, 0x3, 0xba, 0xe, 0xde, 0xe, 0xcb, 0x4a, 0xc3, 0x46, 0x46, 0x46, 0x46, 0xe, 0xcd, 0x3, 0x5e, 0xe, 0x47, 0x8e, 0xcd, 0x46, 0x7f, 0x84, 0x32, 0x44, 0xad, 0x4c, 0xc5, 0x3, 0xba, 0x47, 0xc5, 0x3b, 0xba, 0x6e, 0x38, 0xf9, 0xc5, 0x3b, 0xba, 0x6f, 0x32, 0x41, 0xfe, 0x46, 0x46, 0x46, 0x46, 0xad, 0x43, 0xfe, 0x47, 0x46, 0x46, 0x46, 0xe, 0xc5, 0x82, 0x56, 0x1b, 0x85, }; //int run(char* a, int* b)// 4015C6 40154E //{ // int i = 0; // for (i = 0; i < 41; ++i) // { // if ((a[i] ^ i ^ 0x46) != b[i]) break; // } // if (i != 41) return 0; // else return 1; //} void dec(char *a, int len) { int i; for (i = 0; i < len; ++i) a[i] ^= 0x46; } int main() { char s[50]; scanf("%s", s); int len = 0x4015C6 - 0x40154E + 1; DWORD *newbloc = (DWORD *)malloc(32 * len); VirtualProtect((void *)0x416140, len, 0x40, newbloc); dec(code, len); int (*runcode)(char*, int*) = (int(*)(char*, int*))&code; if (runcode(s, data)) printf("Wow, You Win!"); else printf("OOps, Wrong!"); dec(code, len); return 0; }

注释掉的run函数是MyProc的源码,code数组是MyProc异或0x46后的数据。

值得注意的是,在进行SMC之前需要使用VirtualProtect等方法修改页面属性,这里的0x40就是可读可写可执行。这种写法写出来的runcode()实际上是在.data段而非.text段上的。.data段并不能执行程序,如果不修改的话程序根本跑不动。

2|0新增一个段


3|0pargma code_seg可以添加一个代码段,这里命名为.SMC,注意段名不能超过8字节


#pragma code_seg(".SMC") void __cdecl run(char *a, int *b) { int i; for (i = 0; i < 41; ++i) { if ((a[i] ^ i ^ 0x46) != b[i]) break; } if (i != 41) printf("OOps, Wrong!"); else printf("Wow, You Win!");; } #pragma code_seg() #pragma comment(linker,"/SECTION:.SMC,ERW")

ERW是可读可写可执行,编译后可以看到多了个.SMC段

那么考虑遍历程序自身的PE结构,找到该.SMC段,并直接对其进行SMC操作

#include <stdio.h> #include <windows.h> char a[] = "TSCTF-J{do_you_think_this_is_a_real_flag}"; //char flag[] = "TSCTF-J{W3lC0M3_2_ReVEr$E_xOr_1s_$O0o_e2}"; int data[50] = // { 18, 20, 7, 17, 4, 110, 10, 58, 25, 124, 32, 14, 122, 6, 123, 22, 100, 8, 6, 48, 4, 22, 34, 117, 27, 0, 36, 18, 40, 4, 105, 42, 57, 67, 43, 85, 13, 60, 5, 83, 19 }; #pragma code_seg(".SMC") void __cdecl run(char *a, int *b) { int i; for (i = 0; i < 41; ++i) { if ((a[i] ^ i ^ 0x46) != b[i]) break; } if (i != 41) printf("OOps, Wrong!"); else printf("Wow, You Win!");; } #pragma code_seg() #pragma comment(linker,"/SECTION:.scode,ERW") void decode() { // 寻址操作,板子 LPVOID pModule = GetModuleHandle(NULL); // 获得进程句柄 PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pModule; PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeader + 4); PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pNtHeader + IMAGE_SIZEOF_FILE_HEADER + 4); PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader); // 遍历表头找到名为“.SMC”的段地址 while (strcmp((char*)pSectionHeader->Name, ".SMC")) pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER); PBYTE pSection = (PBYTE)((DWORD)pModule + pSectionHeader->VirtualAddress); // 该段的VA // 对.SMC段的加密 DWORD* newbloc = (DWORD*)malloc(32 * pSectionHeader->SizeOfRawData); VirtualProtect((void*)pSection, pSectionHeader->SizeOfRawData, 0x40, newbloc); for (DWORD i = 0; i < pSectionHeader->SizeOfRawData; i++) *(pSection + i) = *(pSection + i) ^ 0x46; // 写.SMC修改后的shellcode //FILE* pFile = NULL; //char FileName[] = "./shellcode"; //pFile = fopen(FileName, "wb"); //if (!pFile) //{ // printf("Failed to Write File\n"); // return; //} //fwrite(pSection, 1, pSectionHeader->SizeOfRawData, pFile); //fclose(pFile); //printf("Write Success\n"); } int main() { char s[50]; scanf("%s", s); decode(); run(s, data); decode(); return 0; }

把第一次经过加密的.SMC段覆盖程序的原SMC段,然后就能跑起来了

但是这个方法有一点不好:程序里面莫名其妙多了个奇怪的段,逆向人员一看就知道有鬼。

4|0一些想法


SMC确实好用,但是逆向人员一看一大片垃圾数据,肯定会起疑。或许可以结合一些特殊的花指令的原理对少部分 机器码进行修改,来让ida等工具能正常反编译出伪代码,来达到误导静态调试的目的。

另外,写SMC一定要避免加密系统函数或一切带有动态地址的玩意:重定位表会先修正内存中的地址,然后才会执行代码;SMC会将修正完毕的地址再次加密,导致地址错误(例如VS的__CheckForDebuggerJustMyCode)。

SMC记得改页面属性,最好改成可读可写可执行


__EOF__

作  者iPlayForSG
出  处https://www.cnblogs.com/Here-is-SG/p/17153081.html
关于博主:编程路上的小学生,热爱技术,喜欢专研。评论和私信会在第一时间回复。或者直接私信我。
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!

posted @   iPlayForSG  阅读(257)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示