先祝各位看官端午节快乐吧~
前言
SMC是一种局部代码加密技术,通过对一段代码进行加密来达到增加逆向工程难度或者免杀的目的。
编写SMC代码不是汇编语言的专利。——《加密与解密 第三版》
是的,SMC不仅能使用汇编上实现,还能很容易的使用VC实现,但是有一个比较致命缺陷:要精准的定位某个函数非常麻烦,所以我们就要以区块为加密的基础单位。
预备知识
需要一点点的PE结构基础就ok啦。
加密算法
既然是加密,就必然需要加密算法。Internet上有许多优秀的开源加密算法,在这篇文章中我将使用自己编写的加密算法。
加密原理简单,是大家耳熟能详的异或加密。
异或的运算方法是一个二进制运算:
1^1=0
0^0=0
1^0=1
0^1=1
两者相等为0,不等为1.
对于一个字符来说,都可以用二进制码来表示.如A:01000001
字符的异或就是对每一位进行二进制运算.
用于加密算法时,假设你要加密的内容为A,密钥为B,则可以用异或加密:
C=A^B
在数据中保存C就行了.
用的时候:
A=B^C
即可取得原加密的内容,所以只要知道密钥,就可以完成加密和解密.
根据原理可以推导出,加密函数也自带解密功能。
void xorPlus(char *soure,int dLen,char *Key,int Klen)
{
for (int i=0;i<dLen;)
{
for (int j=0;(j<Klen) && (i<dLen);j++,i++)
{
soure[i]=soure[i] ^ Key[j];
soure[i]=~soure[i];
}
}
}
参数说明
1. soure 被加数据的指针
2. dLen 被加密数据的长度
3. key 密匙数据的指针
4. key 的长度
NOTE:这个函数即时加密函数也是解密函数。
保护敏感代码
这里的敏感代码指的是Cracker和杀毒软件感兴趣的代码,也许你并不想让它们轻松地得到想要的结果。
因为定位一个具体的函数很繁琐,所以我们选择直接定位一个节表。把敏感的代码放入一个新的节表中,然后在需要的时候进行解密,这就是SMC动态加密技术的精髓。
如何把敏感代码放入一个新的节表中?
#pragma code_seg(".SMC")
void Fun1()
{
MessageBoxA(NULL,"正在执行被加密算法。",NULL,MB_OK);
}
#pragma code_seg()
#pragma comment(linker, "/SECTION:.SMC,ERW")
#pragma code_seg(“.SMC”) 是一条预编译指令,作用是告诉链接器下面的代码放入 .SMC 代码段中。参数是代码段节表名,学过PE结构的人都知道,节表名<=8 个字符
#pragma code_seg() 是指示链接器,下面的代码放在原来的代码段中。
因为我们是加密敏感代码,而不是主程序代码,所以必须把它切换回去,要不然加密的时候主程序也被一起加密了,就没有负责解密的代码了。
#pragma comment(linker, “/SECTION:.SMC,ERW”) 是设置节表的属性。因为这个节表的数据是要被解密的,所以它必须具有可读写的属性;因为解密后这个节表中有代码,所以它必须具有可执行的属性。当然,这一步不是必须的,也可以在解密前调用VirtualProtect函数来修改内存属性。
一般写法:
#pragma code_seg(".SMC")
#include "关键的源代码.h"
#pragma code_seg()
#pragma comment(linker, "/SECTION:.SMC,ERW")
现在,你可以尝试调用Fun1() ,它是能准确无误的执行的。
解密我们的代码
现在来说说如何解密。
要解密我们首先要定位到镜像文件的载入基址。
这还不简单?GetModuleHandle(0);
解密程序首先通过对PE结构的解析找到第一个节表所在的地方,然后对Name字段进行比较,如果Name字段的值=.SMC 就说明是被加密的代码块,然后取出VirtualAddress和SizeOfRawData调用xorPlus对节表.SMC进行解密。
void SMC(char *pBuf,char *key)
{
// SMC 加密XX区段
const char *szSecName = ".SMC";
short nSec;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeader;
PIMAGE_SECTION_HEADER pSec;
pDosHeader=(PIMAGE_DOS_HEADER)pBuf;
pNtHeader=(PIMAGE_NT_HEADERS)&pBuf[pDosHeader->e_lfanew];
nSec=pNtHeader->FileHeader.NumberOfSections;
pSec=(PIMAGE_SECTION_HEADER)&pBuf[ sizeof(IMAGE_NT_HEADERS)+pDosHeader->e_lfanew];
for (int i=0;i<nSec;i++)
{
if (strcmp((char *)&pSec->Name,szSecName)==0)
{
int pack_size;
char *packStart;
pack_size=pSec->SizeOfRawData;
packStart = &pBuf[pSec->VirtualAddress];
//VirtualProtect(packStart,pack_size,PAGE_EXECUTE_READWRITE,&old);
xorPlus(packStart,pack_size,key,strlen(key));
//AfxMessageBox(_T("SMC解密成功。"));
return;
}
pSec++;
}
}
调用被加密的函数:
void CTestDlg::OnBnClickedButton2()
{
__try
{
Fun1();
}
__except(1)
{
UnPack(KeyBuffer); //修正数据
AfxMessageBox(_T("Key不正确,请重新输入。"));
}
}
如果解密失败,Fun1() 一定会发生异常,主程序捕获这个异常并提示用户,就可以达到注册码检查的目的。。。
加密我们的敏感代码
读者可能已经注意到了,编译器肯定是没有加密功能的,所以只有我们自己写程序来加密。
原理:
打开PE文件->定位到节表->查找 .SMC 节表->加密.SMC节表
void SMC(HANDLE hFile,char *key)
{
// SMC 加密XX区段
HANDLE hMap;
const char *szSecName = ".SMC";
char *pBuf;
int size;
short nSec;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeader;
PIMAGE_SECTION_HEADER pSec;
size = GetFileSize(hFile,0);
hMap=CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,size,NULL);
if (hMap==INVALID_HANDLE_VALUE)
{
_viewf:
AfxMessageBox(_T("映射失败"));
return ;
}
pBuf=(char *)MapViewOfFile(hMap,FILE_MAP_WRITE|FILE_MAP_READ,0,0,size);
if(!pBuf) goto _viewf;
pDosHeader=(PIMAGE_DOS_HEADER)pBuf;
pNtHeader=(PIMAGE_NT_HEADERS)&pBuf[pDosHeader->e_lfanew];
if (pNtHeader->Signature!=IMAGE_NT_SIGNATURE)
{
AfxMessageBox(_T("不是有效的win32 可执行文件"));
goto _clean;
}
nSec=pNtHeader->FileHeader.NumberOfSections;
pSec=(PIMAGE_SECTION_HEADER)&pBuf[ sizeof(IMAGE_NT_HEADERS)+pDosHeader->e_lfanew];
for (int i=0;i<nSec;i++)
{
if (strcmp((char *)&pSec->Name,szSecName)==0)
{
int pack_size;
char *packStart;
pack_size=pSec->SizeOfRawData;
packStart = &pBuf[pSec->PointerToRawData];
xorPlus(packStart,pack_size,key,strlen(key));
AfxMessageBox(_T("SMC加密成功。"));
goto _clean;
}
pSec++;
}
AfxMessageBox(_T("未找到 .SMC 段"));
_clean:
UnmapViewOfFile(pBuf);
CloseHandle(hMap);
return ;
}
写在最后
vs2012编译的MFC工程的基址是变动,如果基址是变动的,程序每次在载入后都会进行重定位,敏感代码中的绝对地址就会被改变,因为改写的时候在解密代码之前。所以当解密后将会得到错误的结果。所以,这个技术不适用与基址会变得DLL中。
你需要这样配置:
源代码:
一个工程,两个项目,分别是:
SMC Pack SMC加密程序
Test 被加密的程序
使用VS2012编译
SMC pack.zip
如果觉得看雪的排版不好,可以转到csdn博客去阅读http://blog.csdn.net/pandaos/article/details/46575441
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法