VC实现SMC加密技术

前言

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);

然后定位到节表。

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++;
    }
}

首先通过对PE结构的解析找到第一个节表所在的地方,然后用strcmp对Name字段进行比较,如果Name字段的值=.SMC 就说明是被加密的代码块,然后取出VirtualAddress和SizeOfRawData调用xorPlus对节表.SMC进行解密。

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编译
链接:http://pan.baidu.com/s/1kTvfWF1 密码:89wv
我的VS配色 O(∩_∩)O~
这里写图片描述

  • 7
    点赞
  • 6
    收藏
  • 打赏
    打赏
  • 5
    评论
软件加密技术内幕chm文档及配套光盘 第1章 PE文件格式深入研究 1.1 PE文件格式格式纵览 1.1.1 区块(Section) 1.1.2 相对虚拟地址(Relative Virtual Addresses) 1.1.3 数据目录 1.1.4 输入函数(Importing Functions) 1.2 PE文件结构 1.2.1 The MS-DOS头部 1.2.2 IMAGE_NT_HEADERS头部 1.2.3 区块表(The Section Table) 1.2.4 各种块(Sections)的描述 1.2.5 输出表 1.2.6 输出转向(Export Forwarding) 1.2.7 输入表 1.2.8 绑定输入(Bound import) 1.2.9 延迟装入数据(Delayload Data) 1.2.10 资源 1.2.11 基址重定位(Base Relocations) 1.2.12 调试目录(DebugDirectory) 1.2.13 NET头部 1.2.14 TLS初始化 1.2.15 程序异常数据 第2章 PE分析工具编写 2.1 文件格式检查 2.2 FileHeader和OptionalHeader内容的读取 2.3 得到数据目录(Data Dircetory)信息 2.4 得到块表(SectionTable)信息 2.5 得到输出表(ExportTable)信息 2.6 得到输入表(ImportTable)信息 第3章 Win32 调试API 3.1 Win32调试API原理 3.1.1 调试相关函数简要说明 3.1.2 调试事件 3.1.3 如何在调试时创建并跟踪一个进程 3.1.4 最主要的循环体 3.1.5 如何处理调试事件 3.1.6 线程环境详解 3.1.7 如何在另一个进程中注入代码 3.2 利用调试API编写脱壳机 3.2.1 tElock 0.98脱壳简介 3.2.2 脱壳机的编写 3.3 利用调试API制作内存补丁 3.3.1 跨进程内存存取机制 3.3.2 Debug API机制 第4章 Windows下的异常处理 4.1 基本概念 4.1.1 Windows下的软件异常 4.1.2 未公开的可靠吗 4.2 结构化异常处理(SEH) 4.2.1 异常处理的基本过程 4.2.2 SEH的分类 4.2.3 相关API 4.2.4 SEH相关数据结构 4.3 异常处理程序设计 4.3.1 顶层(top-level)异常处理 4.3.2 线程异常处理 4.3.3 异常处理的堆栈展开(Stack unwind) 4.3.4 异常处理程序设计中的几个注意事项: 4.4 SEH的简单应用 4.4.1 Win9x下利用SEH进ring0 4.4.2 利用SEH实现对自身的单步自跟踪 4.4.3 其它应用 4.5 系统背后的秘密 4.6 VC是如何封装系统提供的SEH机制的 4.6.1 扩展的EXCEPTION_REGISTRATION级相关结构 4.6.2 数据结构组织 4.7 Windows XP下的向量化异常处理(VEH) 第5章 软件加密技术 5.1 反调试技术(Anti-Debug) 5.1.1 句柄检测 5.1.2 SoftICE后门指令 5.1.3 int68子类型 5.1.4 ICECream子类型 5.1.5 判断NTICE服务是否运行 5.1.6 INT 1 检测 5.1.7 利用UnhandledExceptionFilter检测 5.1.8 INT 41子类型 5.2 反跟踪技术(Anti-Trace) 5.2.1 断点检测 5.2.2 利用SEH反跟踪 5.2.3 SMC技术实现 5.3 反加载技术(Anti-Loader) 5.3.1 利用TEB检测 5.3.2 利用IsDebuggerPresent函数检测 5.3.3 检查父进程 5.4 反DUMP技术(Anti-Dump) 5.5 文件完整性检验 5.5.1 CRC校验实现 5.5.2 校验和(Checksum) 5.5.3 内存映像校验 5.6 反监视技术(Anti-Monitor) 5.6.1 窗口方法检测 5.6.2 句柄检测 5.7 反静态分析技术 5.7.1 扰乱汇编代码 5.7.2 花指令 5.7.3 信息隐藏 5.8 代码与数据结合技术 5.9 软件保护的若干忠告 第6章 加壳软件编写 6.1 外壳编写基础 6.1.1 判断文件是否是PE格式的EXE文件 6.1.2 文件基本数据的读入 6.1.3 额外数据保留 6.1.4 重定位数据的去除 6.1.5 文件的压缩 6.1.6 资源区块的处理 6.1.7 区块的融合 6.1.8 输入表的处理 6.1.9 外壳部分的编写 6.1.10 将外壳部分添加至原程序 6.1.10 小结 6.2 加壳程序综合运用的实例 6.2.1 程序简介 6.2.2 加壳子程序(WJQ_ShellBegin()) 6.2.3 PE外壳程序 6.2.4 加进Anti技术 6.2.5 通过外壳修改被加壳PE 6.2.6 VC++调用汇编子程序 第7章 如何让壳与程序融为一体 7.1 序 7.1.1 为何需要壳和程序一体化 7.1.2 为阅读此章节需要的知识 7.1.3 基于此章节用的的例子程序说明 7.2 欺骗检查壳的工具 7.2.1 fi是如何检查壳的 7.2.2 欺骗fi 7.3 判断自己是否给脱壳了 7.3.1 判断文件尺寸 7.3.2 检查标记 7.3.3 外部检测(使用dll) 7.3.4 hook 相关的api(防止loader和调试api) 7.4 使用sdk把程序和壳溶为一体 7.4.1 sdk的意义 7.4.2 做一个带sdk的壳 7.5 后记:关于壳和程序的思考 第8章 Visual Basic 6 逆向工程 8.1 简介 8.2 P-code传奇 8.3 VB编译奥秘 8.4 VB与COM 8.5 VB可执行程序结构研究 8.6 VB程序事件解读 8.7 VB程序图形界面(GUI)解读 8.8 VB程序执行代码研究 8.9 我们的工具 8.10 VB程序保护篇 附录A 在Visual C++中使用内联汇编 附录B 在Visual Basic中使用汇编

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论 5

打赏作者

PandaOS

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值