C/C++ 对代码节的动态加解密
加壳的原理就是加密或者压缩程序中的已有资源,然后当程序执行后外壳将模拟PE加载器对EXE中的区块进行动态装入,下面我们来自己实现一个简单的区块加解密程序,来让大家学习了解一下壳的基本运作原理。
本次使用的工具,依旧是上次编写的PETools: https://www.cnblogs.com/LyShark/p/12960816.html
加密第一个节表:
#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")
void EncrySection(LPSTR szFileName)
{
HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);
PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);
printf("节表数量: %d \n", FileHdr->NumberOfSections);
printf("节虚拟地址: %x \n", pSection->Misc.VirtualSize);
printf("读入FOA基地址: %x \n", pSection->PointerToRawData);
printf("读入节表长度: %x \n", pSection->SizeOfRawData);
DWORD dwRead = 0;
PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
memset(pByte, 0, pSection->SizeOfRawData);
ReadFile(hFile, pByte, pSection->SizeOfRawData, &dwRead, NULL);
for (int x = 0; x < pSection->SizeOfRawData; x++)
pByte[x] ^= 0x10;
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);
UnmapViewOfFile(lpBase);
}
int main(int argc, char * argv[])
{
EncrySection("c://win32.exe");
system("pause");
return 0;
}
当需要打印第二个节只需要递增指针.
void EncrySection(LPSTR szFileName)
{
HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);
PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);
printf("节表数量: %d \n", FileHdr->NumberOfSections);
printf("节虚拟地址: %x \n", pSection->Misc.VirtualSize);
printf("读入FOA基地址: %x \n", pSection->PointerToRawData);
printf("读入节表长度: %x \n", pSection->SizeOfRawData);
DWORD dwRead = 0;
PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
memset(pByte, 0, pSection->SizeOfRawData);
// 逐字节读入
for (int x = 0; x < pSection->PointerToRawData; x++)
{
ReadFile(hFile, &pByte[x], 1, &dwRead, NULL);
}
for (int x = 0; x < pSection->SizeOfRawData; x++)
pByte[x] ^= 0x10;
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);
pSection++;
printf("节虚拟地址: %x \n", pSection->Misc.VirtualSize);
printf("读入FOA基地址: %x \n", pSection->PointerToRawData);
printf("读入节表长度: %x \n", pSection->SizeOfRawData);
UnmapViewOfFile(lpBase);
}
循环加密所有的节,可能会出现问题
#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")
void EncrySection(LPSTR szFileName,DWORD Key)
{
HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);
PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);
printf("[-] 加密节表数量: %d \n", FileHdr->NumberOfSections);
for (int x = 0; x < FileHdr->NumberOfSections; x++)
{
printf("[-] 节虚拟地址: 0x%08X 虚拟大小: 0x%08X\n", pSection->VirtualAddress,pSection->Misc.VirtualSize);
printf("[-] 读入FOA基地址: 0x%08X 节表长度: 0x%08X \n\n", pSection->PointerToRawData,pSection->SizeOfRawData);
DWORD dwRead = 0;
PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
memset(pByte, 0, pSection->SizeOfRawData);
ReadFile(hFile, pByte, pSection->SizeOfRawData, &dwRead, NULL);
for (int x = 0; x < pSection->SizeOfRawData; x++)
{
pByte[x] ^= Key;
}
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);
free(pByte);
pSection = pSection + 1;
}
UnmapViewOfFile(lpBase);
}
int main(int argc, char * argv[])
{
EncrySection("c://win32.exe",0x10);
system("pause");
return 0;
}
添加壳代码
#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")
void EncrySection(LPSTR szFileName, DWORD Key)
{
HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);
PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);
printf("[-] 节虚拟地址: 0x%08X 虚拟大小: 0x%08X\n", pSection->VirtualAddress, pSection->Misc.VirtualSize);
printf("[-] 读入FOA基地址: 0x%08X 节表长度: 0x%08X \n", pSection->PointerToRawData, pSection->SizeOfRawData);
printf("[*] 已对 %s 节 --> XOR加密/解密 --> XOR密钥: %d \n\n", pSection->Name, Key);
DWORD dwRead = 0;
PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
memset(pByte, 0, pSection->SizeOfRawData);
ReadFile(hFile, pByte, pSection->SizeOfRawData, &dwRead, NULL);
for (int x = 0; x < pSection->SizeOfRawData; x++)
{
pByte[x] ^= Key;
}
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);
pSection->Characteristics = 0xE0000020;
free(pByte);
FlushViewOfFile(lpBase, 0);
UnmapViewOfFile(lpBase);
}
void DecodeCode(LPSTR szFileName)
{
// 第一步修正程序OEP位置,修正为最后一个节的地址
HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);
PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
DWORD ImageBase = NtHdr->OptionalHeader.ImageBase;
DWORD BaseRVA = NtHdr->OptionalHeader.AddressOfEntryPoint;
printf("Base RVA %x \n", BaseRVA);
PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);
// 首先得到最后一个节的指针,然后找到里面的虚拟偏移值,填入到程序OEP位置即可。
DWORD SectionNum = FileHdr->NumberOfSections;
char Code[] =
{
"\x60"
"\xb8\x00\x00\x00\x00"
"\x80\x30\x88"
"\x40"
"\x3d\xff\x4f\x40\x00"
"\x75\xf5"
"\x61"
"\xb8\x00\x00\x00\x00"
"\xff\xe0"
};
DWORD dwWrite = 0;
printf("%x \n", ImageBase + pSection->VirtualAddress);
*(DWORD *)&Code[2] = ImageBase + pSection->VirtualAddress;
*(DWORD *)&Code[11] = ImageBase + pSection->VirtualAddress + pSection->Misc.VirtualSize;
*(DWORD *)&Code[19] = ImageBase + BaseRVA;
pSection = pSection + (SectionNum - 1);
printf("得到最后一个节的实际地址: %x \n", pSection->PointerToRawData);
SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
WriteFile(hFile, (LPVOID)Code, sizeof(Code), &dwWrite, NULL);
FlushViewOfFile(lpBase, 0);
UnmapViewOfFile(lpBase);
}
加壳的首要目标是要创建一个具有可写属性的新节

我们使用PESection对win32.exe加一个.hack节,然后大小为2048

加入后再次使用PETools工具检查,发现已经添加成功了。

下一步就是将.text节进行加密了,这里为了简单我使用的是异或加密,如下是加密前的机器码。

使用我们编写的工具进行加密,传入两个参数,一个是文件,一个则是加密密钥

加密有区段会变成如下样子。

接着使用 addpack 传入一个参数,写入解密代码。

电脑管家可能会拦截,请将其取出来。

我们X64dbg载入看看,程序默认停在了,我们的壳的位置,。

运行后对.text节进行动态解密,然后一个jmp跳转到程序的OEP位置即可,这也就是壳的基本原理。

C/C++ 对代码节的动态加解密的更多相关文章
- [改善Java代码]使用forName动态加载类文件
动态加载(Dynamic Loading)是指在程序运行时加载需要的类库文件,对Java程序来说,一般情况下,一个类文件在启动时或首次初始化时会被加载到内存中,而反射则可以在运行时再决定是否需要加载一 ...
- AX 用代码创建FORM动态加控件,重载动态添加的控件的方法。
eg. 范例:class\RFIDReadWriteForm/Build方法. formRun.controlMethodOverload(true); formRun.controlMethodOv ...
- Dex动态加载
Dex动态加载是为了解决什么问题? 在Android系统中,一个App的所有代码都在一个Dex文件里面. Dex是一个类似Jar的存储了多个Java编译字节码的归档文件. 因为Android系统使用D ...
- Android应用安全之外部动态加载DEX文件风险
1. 外部动态加载DEX文件风险描述 Android 系统提供了一种类加载器DexClassLoader,其可以在运行时动态加载并解释执行包含在JAR或APK文件内的DEX文件.外部动态加载DEX文件 ...
- js动态加载的蒙板弹框
我们访问一些网站时总会遇到这种点击后,背景像被打上一层模板一样,这个是怎么做到的呢? 它是将这个弹框div独立于页面容器wrap,设置position为absolute,将其水平垂直之后都居中,设置弹 ...
- goloader - golang动态加载的实现
github地址:https://github.com/dearplain/goloader 这里有以前的一些思路:http://www.cnblogs.com/dearplain/p/8145985 ...
- android中使用jni对字符串加解密实现分析
android中使用jni对字符串加解密实现分析 近期项目有个需求.就是要对用户的敏感信息进行加密处理,比方用户的账户password,手机号等私密信息.在java中,就对字符串的加解密我们能够使用A ...
- JavaScript与C#互通的DES加解密算法
原文地址:传送门 本文提供了一个能使JavaScript与C#互通的DES加解密算法的实现,在前台页面中用JavaScript版本的DES算法将数据加密之后,传到服务器端,在服务器端可用C#版本的DE ...
- .NET Core加解密实战系列之——消息摘要与数字签名算法
目录 简介 功能依赖 消息摘要算法 MD算法 家族发展史 应用场景 代码实现 MD5 示例代码 SHA算法 应用场景 代码实现 SHA1 SHA256 示例代码 MAC算法 HMAC算法的典型应用 H ...
随机推荐
- vscode中js文件使用typescript语法报错,如何解决
原因:由于vcode自身的语法检查有些问题 解决办法:在设置里面加上 "javascript.validate.enable": false 禁用默认的 js 验证 总结: 由于v ...
- SpringMVC-02 第一个SpringMVC程序
SpringMVC-02 第一个SpringMVC程序 第一个SpringMVC程序 配置版 新建一个Moudle , springmvc-02-hello,确定依赖导入进去了 1.配置web.xml ...
- php伪协议分析与CTF例题讲解
本文大量转载于:https://blog.csdn.net/qq_41289254/article/details/81388343 (感谢博主) 一,php:// 访问输入 ...
- rest framework renderers
渲染器 前TemplateResponse实例可以被返回给客户端,它必须被渲染.渲染过程需要模板和上下文的中间表示,并把它变成能够提供给客户端的最后一个字节流. - Django文档 REST框架包含 ...
- 过多if - else 的问题, 以及策略模式 + 反射解决方法
策略模式解决if - else 的代码 业务场景: 外包企业的审批人需要审批打卡的场景: 审批人分为多种不同的级别,多种级别中具有方式相同但是内容不同的操作:审批. 原来场景: 有前端传来审批人参数, ...
- 【关系抽取-R-BERT】加载数据集
认识数据集 Component-Whole(e2,e1) The system as described above has its greatest application in an arraye ...
- 基于sinc的音频重采样(一):原理
我在前面的文章<音频开源代码中重采样算法的评估与选择>中说过sinc方法是较好的音频重采样方法,缺点是运算量大.https://ccrma.stanford.edu/~jos/resamp ...
- 【python+selenium的web自动化】- 针对上传操作的实现方案
如果想从头学起selenium,可以去看看这个系列的文章哦! https://www.cnblogs.com/miki-peng/category/1942527.html 关于上传操作 上传有两 ...
- Spring 声明式事务与编程式事务详解
本文转载自IBM开发者论坛:https://developer.ibm.com/zh/articles/os-cn-spring-trans 根据自己的学习理解有所调整,用于学习备查. 事务管理对于企 ...
- C# 输出一个字符串的前缀、后缀和它的子串(信息内容安全 实验一)
一.什么是前后缀 字符串的前缀:符号串左部的任意子串(或者说是字符串的任意首部) 字符串的后缀:符号串右部的任意子串(或者说是字符串的任意尾部) 举例:比如 101110 它的前缀就是空串.1.10. ...