本文为看雪论精华文章
看雪论坛作者ID:三一米田
一、框架
//1. 将 DLL 设置为 Release 版本进行编译、小、内联了一些函数
//2. CC++ -> 代码生成 -> GS安全检查 -> 禁用 (取消一些库函数的调用)
//3. CC++ -> 所有选项 -> 运行库 -> MT (取消一些库函数的调用)
//4. 合并区段,并设置属性为可读可写可执行(0xE00000E0)
-
设置 .exe(加壳器) 和 .dll(stub) 文件的输出路径为同一路径 -
项目属性 -> 常规 -> 输出目录 -
设置工作目录为刚才的输出路径 -
项目属性 -> 调试 -> 工作目录
二、加壳器
2.1 打开被加壳程序
//打开、读取、判断文件
VOID Pack::OpenPeFile(LPCSTR Path) {
HANDLE hFile = CreateFile(Path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
print("打开文件失败");//自己封装的判断函数
PeSize = GetFileSize(hFile, NULL);
PeBase = (DWORD)malloc(PeSize);
DWORD ReadSize = 0;
ReadFile(hFile, (LPVOID)PeBase, PeSize, &ReadSize, NULL);
if (pDosHead(PeBase)->e_magic!=0x5A4D)
print("不是有效的PE文件");
else if (pNtHead(PeBase)->Signature != 0x4550)
print("不是有效的PE文件");
CloseHandle(hFile);
}
// 对 dll 的区段合并为.text区段,并设置区段属性为[读写执行]
// 去除所有导出函数和导出变量的名称粉碎
extern "C"{
__declspec(dllexport) SHARE ShareData; //导出变量,用于壳代码与加壳器之间的通讯
__declspec(dllexport) __declspec(naked) void start() //导出函数地址,将会作为新的OEP
{
_asm{
mov eax, fs:[0x30]
mov eax, [eax + 0x8] //获取当前进程gImageBase
mov gImageBase, eax
}
GetFunAdd(); //获取GetProcAddress
GetApi(); //获取API
CallShllFun(); //密码弹窗
UnCompression(ShareData); //解压
DecSection(); //解密代码段
DecIat(ShareData); //加密IAT
FixReloca(ShareData); //修复重定位
CallTls(ShareData); //处理TLS
Debugging(); //反调试
__asm
{
mov eax, gImageBase //获取当前进程gImageBase
mov ebx, ShareData.DestOEP //获取原程序的OEP
add eax,ebx
jmp eax
}
}
}
2.2 打开dll文件
//打开 壳代码的DLL 文件,获取基址
VOID Pack::OpenDllFile(LPCSTR Path) {
//加载模块到内存中,并且不调用DllMain
DllBase = (DWORD)LoadLibraryEx(Path, NULL, DONT_RESOLVE_DLL_REFERENCES);
//获取导出函数StartOfsset地址
StartOfsset = (DWORD)GetProcAddress((HMODULE)DllBase, "start");
if (!StartOfsset)
print("获取DLLMain的导出函数失败");
StartOfsset = StartOfsset - DllBase - (DWORD)GetSecHeadAddr(DllBase, DllSecName)->VirtualAddress;
//获取导出变量通讯结构体,加壳器需要使用到
pShareData = (SHARE*)GetProcAddress((HMODULE)DllBase, "ShareData");
}
2.3 新增并拷贝区段
//添加并拷贝一个新区段
VOID Pack::CopySection(LPCSTR DestName) {
if (strlen(DestName) > 7)
print("新区段名称太长");
//获取需要添加的区段
auto pLastSection = IMAGE_FIRST_SECTION(pNtHead(PeBase)) + (pFileHead(PeBase)->NumberOfSections - 1);
auto pNewSection = pLastSection + 1;
//获取区段头的首地址
auto SrcAddr = GetSecHeadAddr(DllBase, DllSecName);
//拷贝区段头
memcpy(pNewSection, SrcAddr, sizeof(IMAGE_SECTION_HEADER));
//修改属性
pNewSection->VirtualAddress = pLastSection->VirtualAddress +
AlignmentToSize(pLastSection->Misc.VirtualSize, pOptionHead(PeBase)->SectionAlignment);
pNewSection->PointerToRawData = pLastSection->PointerToRawData +
AlignmentToSize(pLastSection->SizeOfRawData, pOptionHead(PeBase)->FileAlignment);
// Name
memcpy(pNewSection->Name, DestName, strlen(DestName));
// NumberOfSections
pFileHead(PeBase)->NumberOfSections += 1;
// SizeOfImage大小
pOptionHead(PeBase)->SizeOfImage +=
AlignmentToSize(pNewSection->Misc.VirtualSize, pOptionHead(PeBase)->SectionAlignment);
// 从新分配空间,将新区段添加到堆空间中
PeSize = pNewSection->SizeOfRawData + pNewSection->PointerToRawData;
PeBase = (DWORD)realloc((VOID*)PeBase, PeSize);
return;
}
//根据大小以及对齐粒度获取对齐大小
DWORD Pack::AlignmentToSize(DWORD Size, DWORD Algmt) {
return (Size / Algmt)?(((Size / Algmt) + 1)*Algmt):Algmt;
}
2.4 设置OEP
//设置OEP
VOID Pack::SetOep() {
//在重定位之前,保存原程序OEP
pShareData->DestOEP = pOptionHead(PeBase)->AddressOfEntryPoint;
//设置OEP为壳代码OEP
pOptionHead(PeBase)->AddressOfEntryPoint = StartOfsset + GetSecHeadAddr(PeBase,".pack")->VirtualAddress;
}
2.5 加密区段
//加密区段
VOID Pack::Encryption(CHAR* SectionName) {
//找到需要加密的区段
auto SectionHead = GetSecHeadAddr(PeBase, SectionName);
//加密的大小和加密起始地址
DWORD Size = SectionHead->Misc.VirtualSize;
CHAR* Address =(CHAR*)(SectionHead->PointerToRawData + PeBase);
for (size_t i = 0; i < Size; i++){
*Address ^= 0x15;
Address++;
}
}
2.6 压缩
//压缩
VOID Pack::Compression()
{
int SecTextSize = GetSecHeadAddr(PeBase, ".text")->SizeOfRawData;
pShareData->SizeOfRawData = SecTextSize;
char* TextSecData = (char*)(GetSecHeadAddr(PeBase, ".text")->PointerToRawData + PeBase);
//被压缩的数据,Packed保存压缩数据的空间,WorkMem为完成压缩需要使用的空间
char* Packed = (char*)malloc(aP_max_packed_size(SecTextSize));
char* WorkMem = (char*)malloc(aP_workmem_size(SecTextSize));
//压缩后的大小
int OutSize = aPsafe_pack(TextSecData, Packed, SecTextSize, WorkMem, NULL, NULL);
//对齐后的大小
int AlignSize = OutSize % 0x200 == 0 ? OutSize : (OutSize / 0x200 + 1) * 0x200;
//新空间大小
DWORD NewFileSize = PeSize - GetSecHeadAddr(PeBase, ".text")->SizeOfRawData + AlignSize;
//申请新的空间大小 文件大小 - 区段在文件中的大小 + 压缩后的大小(不对齐)
DWORD NewFileBase = (DWORD)malloc(NewFileSize);
//TextSecData之前的数据
DWORD PreText = GetSecHeadAddr(PeBase, ".text")->PointerToRawData - 1;
//拷贝TextSecData段之前的数据
memcpy((LPVOID)NewFileBase, (LPVOID)PeBase, PreText);
//拷贝压缩部分的数据
memcpy((LPVOID)(NewFileBase + PreText + 1), Packed, OutSize);
//拷贝TextSecData段后面的数据
LPVOID DestAddr = (LPVOID)(NewFileBase + PreText + AlignSize + 1);
DWORD TextSecSize = GetSecHeadAddr(PeBase, ".text")->SizeOfRawData;
DWORD TextSecPointRaw = GetSecHeadAddr(PeBase, ".text")->PointerToRawData;
LPVOID SrcAddr = (LPVOID)(PeBase + TextSecSize + TextSecPointRaw);
DWORD LastSize = NewFileSize - PreText - AlignSize;
memcpy(DestAddr, SrcAddr, LastSize);
//free(&FileBase);
PeBase = NewFileBase;
//free((void*)NewFileBase);
NewFileBase = NULL;
// 1. 获取到目标模块的区段表
auto Sections = IMAGE_FIRST_SECTION(pNtHead(PeBase));
// 2. 使用文件头中的区段数量遍历区段表
WORD Count = pFileHead(PeBase)->NumberOfSections;
BOOL bChangeFoa = FALSE;
for (WORD i = 0; i < Count; ++i)
{
if (bChangeFoa) {
Sections[i].PointerToRawData = Sections[i].PointerToRawData - GetSecHeadAddr(PeBase, ".text")->SizeOfRawData + AlignSize;
}
// 3. .text区段之前的区段不改变,操作.text区段之后的区段
if (!memcmp(Sections[i].Name, ".text", 7)) {
bChangeFoa = TRUE;
}
}
pShareData->TextCompressSize = OutSize;
GetSecHeadAddr(PeBase, ".text")->SizeOfRawData = AlignSize;
pShareData->TextRVA = GetSecHeadAddr(PeBase, ".text")->VirtualAddress;
PeSize = NewFileSize;
}
2.7 IAT加密
-
判断是否开启重定位,如果开启了,将 PE 文件加载到指定位置,并且修复目标PE 文件的重定位。
-
遍历导入表,加载使用到的所有模块到内存,修复模块相关的信息,并根据导入表中的函数名称,填充所有的IAT地址项。
-
查看当前是否存在TLS回调函数,如果存在,则传入进程创建事件,依次调用 所有的TLS回调函数。
-
以PE文件中的AddressOfEntryPoint为起始位置,创建线程并运行。
//令壳代码接管IAT表
VOID Pack::SetIatTo0() {
// 在重定位之前设置
pShareData->ImpTableVA = pOptionHead(PeBase)->DataDirectory[1].VirtualAddress;//备份原导入表RVA
pOptionHead(PeBase)->DataDirectory[1].VirtualAddress = 0; //清零
pOptionHead(PeBase)->DataDirectory[12].VirtualAddress = 0; //第[12]项也是导入表,也需要清零
}
2.8 处理TLS
//备份TLS后清空TLS
VOID Pack::SetTls() {
pShareData->TlsVirtualAddress = pOptionHead(PeBase)->DataDirectory[9].VirtualAddress;
//如果存在TLS表
if (pShareData->TlsVirtualAddress) {
pOptionHead(PeBase)->DataDirectory[9].VirtualAddress = 0;
DWORD TlsFoa = RvaToOffset(PeBase, pShareData->TlsVirtualAddress);
auto TlsTable = (PIMAGE_TLS_DIRECTORY)(TlsFoa + PeBase);
//备份TLS的回调函数
pShareData->TlsCallBackTableVa = TlsTable->AddressOfCallBacks;
}
}
2.9 修复重定位
// 对壳代码进行重定位
VOID Pack::FixReloc(){
DWORD OldImageBase = pOptionHead(DllBase)->ImageBase;
DWORD NewImageBase = pOptionHead(PeBase)->ImageBase;
DWORD OldSectionBase = GetSecHeadAddr(DllBase, ".text")->VirtualAddress;
DWORD NewSectionBase = GetSecHeadAddr(PeBase, ".pack")->VirtualAddress;
auto Relocs = (PIMAGE_BASE_RELOCATION)(DllBase + pOptionHead(DllBase)->DataDirectory[5].VirtualAddress);
// 遍历重定位表
while (Relocs->SizeOfBlock) {
DWORD OldProtect = 0;
VirtualProtect((LPVOID)(DllBase + Relocs->VirtualAddress), 0x1000, PAGE_READWRITE, &OldProtect);
TypeOffset* items = (TypeOffset*)(Relocs + 1);
//重定位项数量
int count = (Relocs->SizeOfBlock - 8) / 2;
//遍历并修复重定位项
for (int i = 0; i < count; ++i) {
if (items[i].Type == 3) {
DWORD* item = (DWORD*)(pOptionHead(DllBase)->ImageBase + Relocs->VirtualAddress + items[i].Offset);
*item = *item - OldImageBase - OldSectionBase + NewImageBase + NewSectionBase;
}
}
VirtualProtect((LPVOID)(DllBase + Relocs->VirtualAddress), 0x1000, OldProtect, &OldProtect);
Relocs = (PIMAGE_BASE_RELOCATION)(Relocs->SizeOfBlock + (DWORD)Relocs);
}
// 关闭源程序的随机基址
//pOptionHead(PeBase)->DllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
}
//备份并替换重定位表
VOID Pack::SetReloc() {
//1.备份原程序的重定位表地址,将原程序的重定位写入到通讯结构体中,将dll的重定位表替换到原程序的重定位表
//2.在壳代码中,系统会帮我们修复壳代码的重定位,待原程序解密之后,在壳代码中获取通讯结构体,获取原程序重定位表,修复原程序重定位
DWORD DllImageBase = pOptionHead(DllBase)->ImageBase;
//备份原重定位表
pShareData->dwRelocRva = pOptionHead(PeBase)->DataDirectory[5].VirtualAddress;
pShareData->dwRelocSize = pOptionHead(PeBase)->DataDirectory[5].Size;
pShareData->OldImageBase = pOptionHead(PeBase)->ImageBase;
//Dll重定位表
auto DllBaseReloc = (PIMAGE_BASE_RELOCATION)(pOptionHead(DllBase)->DataDirectory[5].VirtualAddress + DllImageBase);
DWORD DllRelocaSize = pOptionHead(DllBase)->DataDirectory[5].Size;
//新增区段
AddSection(".mysec", DllRelocaSize);
auto NewSecHed = GetSecHeadAddr(PeBase, ".mysec");
auto OldSecHed = GetSecHeadAddr(DllBase, ".text");
auto PackSecHed = GetSecHeadAddr(PeBase, ".pack");
auto NewRelocaSection = (PIMAGE_BASE_RELOCATION)(NewSecHed->PointerToRawData + PeBase);
DWORD OldSectionAddr = (DWORD)(OldSecHed->VirtualAddress + DllBase);
memcpy((DWORD*)NewRelocaSection, (DWORD*)(DllBaseReloc), DllRelocaSize);
while (NewRelocaSection->VirtualAddress){
//新的内存页起始RVA = 原RVA - 原段基址 +.pack段基址
NewRelocaSection->VirtualAddress = NewRelocaSection->VirtualAddress - (OldSectionAddr -DllBase) + PackSecHed->VirtualAddress;
NewRelocaSection = (PIMAGE_BASE_RELOCATION)(NewRelocaSection->SizeOfBlock + (DWORD)NewRelocaSection);
}
//替换原程序重定位表
pOptionHead(PeBase)->DataDirectory[5].VirtualAddress = NewSecHed->VirtualAddress;
pOptionHead(PeBase)->DataDirectory[5].Size = DllRelocaSize;
}
//添加一个新区段
VOID Pack::AddSection(LPCSTR Name, DWORD Size) {
PIMAGE_DOS_HEADER pDos = pDosHead(PeBase);
PIMAGE_NT_HEADERS pNt = pNtHead(PeBase);
//获取需要添加的区段
PIMAGE_SECTION_HEADER pLastSection = (IMAGE_FIRST_SECTION(pNt)) + (pNt->FileHeader.NumberOfSections - 1);
PIMAGE_SECTION_HEADER pNewSection = pLastSection + 1;
//Name
memcpy(pNewSection->Name, Name, 7);
//VirtualSize
pNewSection->Misc.VirtualSize = AlignmentToSize(Size, pNt->OptionalHeader.SectionAlignment);
//VirtualAddress = 最后一个区段的 VirtualAddress +最后一个区段内存大小
pNewSection->VirtualAddress = pLastSection->VirtualAddress +
AlignmentToSize(pLastSection->Misc.VirtualSize, pNt->OptionalHeader.SectionAlignment);
//SizeOfRawData
pNewSection->SizeOfRawData = AlignmentToSize(Size, pNt->OptionalHeader.FileAlignment);
//PointerToRawData = 最后一个区段的 PointerToRawData + 最后一个区段文件大小
pNewSection->PointerToRawData = pLastSection->PointerToRawData +
AlignmentToSize(pLastSection->SizeOfRawData, pNt->OptionalHeader.FileAlignment);
//Characteristics
pNewSection->Characteristics = 0xE00000E0;
//NumberOfSections
pNt->FileHeader.NumberOfSections += 1;
//SizeOfImage大小
pNt->OptionalHeader.SizeOfImage += AlignmentToSize(pNewSection->Misc.VirtualSize, pNt->OptionalHeader.SectionAlignment);
//从新分配空间,将新区段添加到堆空间中
PeSize = pNewSection->SizeOfRawData + pNewSection->PointerToRawData;
PeBase = (DWORD)realloc((VOID*)PeBase, PeSize);
pNewSection = GetSecHeadAddr(PeBase, Name);
memset((DWORD*)(pNewSection->PointerToRawData + PeBase), 0, pNewSection->SizeOfRawData);
return;
}
2.10 拷贝数据
//拷贝区段数据
VOID Pack::CopySecData(CHAR* DestName) {
//.text的地址
CHAR* Src = (CHAR*)(GetSecHeadAddr(DllBase, ".text")->VirtualAddress+DllBase);
//.pack的地址
CHAR* Dest = (CHAR*)(GetSecHeadAddr(PeBase, DestName)->PointerToRawData+PeBase);
memcpy(Dest, Src, GetSecHeadAddr(DllBase, ".text")->Misc.VirtualSize);
return;
}
2.11 写入磁盘
//将新文件写入磁盘
VOID Pack::WriteToFile(LPCSTR Path) {
HANDLE hFile = CreateFile(Path, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD WriteSize = 0;
DWORD Ret = WriteFile(hFile, (LPVOID)PeBase, PeSize, &WriteSize, NULL);
if (!Ret)
print("写入文件失败");
CloseHandle(hFile);
return;
}
三、壳代码
// 对 dll 的区段合并为.text区段,并设置区段属性为[读写执行]
// 去除所有导出函数和导出变量的名称粉碎
extern "C"{
__declspec(dllexport) SHARE ShareData; //导出变量,用于壳代码与加壳器之间的通讯
__declspec(dllexport) __declspec(naked) void start() //导出函数地址,将会作为新的OEP
{
_asm{
mov eax, fs:[0x30]
mov eax, [eax + 0x8] //获取当前进程gImageBase
mov gImageBase, eax
}
GetFunAdd(); //获取GetProcAddress
GetApi(); //获取API
CallShllFun(); //密码弹窗
UnCompression(ShareData); //解压
DecSection(); //解密代码段
DecIat(ShareData); //加密IAT
FixReloca(ShareData); //修复重定位
CallTls(ShareData); //处理TLS
Debugging(); //反调试
__asm
{
mov eax, gImageBase //获取当前进程gImageBase
mov ebx, ShareData.DestOEP //获取原程序的OEP
add eax,ebx
jmp eax
}
}
}
3.1 手动获取函数地址
//定义GetProcAddress函数指针
typedef FARPROC(WINAPI* MYGetProcAddress)(HMODULE hModule, LPCSTR lpProcName);
MYGetProcAddress MyGetProcAddress =0;
void GetFunAdd() {
DWORD Ent;
DWORD Eot;
DWORD Eat;
CHAR Buff[] = { 'G','e','t','P','r','o','c','A','d','d','r' ,'e','s','s',' ' };
__asm
{
pushad
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0x1c] //第一个模块
mov eax,[eax] //kernel模块
mov eax,[eax+0x8] //kernel基址
mov gKernelBase,eax
mov edx,[eax+0x3c]
add edx,eax
mov edx,[edx+0x78]
add edx,eax //导出表
mov ebx,[edx+0x1c]
add ebx,eax
mov Eat,ebx //地址表
mov ebx,[edx+0x20]
add ebx,eax
mov Ent,ebx //名称表
mov ebx,[edx+0x24]
add ebx,eax
mov Eot,ebx //序号表
xor ebx, ebx
jmp tag_FristCmp
tag_CmpFunNameLoop:
inc ebx
tag_FristCmp:
mov esi, Ent
mov esi, [ebx * 0x4 + esi] //遍历名称表
lea esi, [esi + eax] //函数名称VA
lea edi, dword ptr Buff //GetProAddress字符串地址
mov ecx, 0xE //GetProAddress长度
cld //清除方向位
repe cmpsb //循环比较字符
jne tag_cmpFunNameLoop
mov esi,Eot
xor edi, edi
mov di, [ebx * 2 + esi] //序号
//使用序号在地址表中找到函数地址
mov edx, Eat
mov esi, [edx + edi * 4] //得到函数地址RVA
lea eax, [esi + eax] //函数地址
mov MyGetProcAddress,eax //保存函数地址到函数指针
popad
}
}
void GetApi() {
MyLoadLibraryExA = (MYLoadLibraryExA)MyGetProcAddress(gKernelBase, "LoadLibraryExA");
MyVirtualProtect = (MYVirtualProtect)MyGetProcAddress(gKernelBase, "VirtualProtect");
gUser32Base = MyLoadLibraryExA("User32.dll", NULL, NULL);
MyMessageBoxA = (MYMessageBoxA) MyGetProcAddress(gUser32Base, "MessageBoxA");
MyVirtualAlloc = (MYVirtualAlloc)MyGetProcAddress(gKernelBase, "VirtualAlloc");
HMODULE hVcruntime = MyLoadLibraryExA("vcruntime140.dll", NULL, NULL);
Myatexit = (MYatexit)MyGetProcAddress(hVcruntime, "atexit");
Mymemcpy = (MYmemcpy)MyGetProcAddress(hVcruntime, "memcpy");
MyRegisterClassA = (MYRegisterClassA)MyGetProcAddress(gUser32Base, "RegisterClassA");
MyCreateWindowExA = (MYCreateWindowExA)MyGetProcAddress(gUser32Base, "CreateWindowExA");
MyGetModuleHandleA = (MYGetModuleHandleA)MyGetProcAddress(gKernelBase, "GetModuleHandleA");
MyShowWindow = (MYShowWindow)MyGetProcAddress(gUser32Base, "ShowWindow");
MyUpdateWindow = (MYUpdateWindow)MyGetProcAddress(gUser32Base, "UpdateWindow");
MyGetMessageA = (MYGetMessageA)MyGetProcAddress(gUser32Base, "GetMessageA");
MyTranslateMessage = (MYTranslateMessage)MyGetProcAddress(gUser32Base, "TranslateMessage");
MyDispatchMessageA = (MYDispatchMessageA)MyGetProcAddress(gUser32Base, "DispatchMessageA");
MyDefWindowProcA = (MYDefWindowProcA)MyGetProcAddress(gUser32Base, "DefWindowProcA");
MyPostQuitMessage = (MYPostQuitMessage)MyGetProcAddress(gUser32Base, "PostQuitMessage");
MyGetClientRect = (MYGetClientRect)MyGetProcAddress(gUser32Base, "GetClientRect");
MyGetWindowTextA = (MYGetWindowTextA)MyGetProcAddress(gUser32Base, "GetWindowTextA");
MyGetDlgItem = (MYGetDlgItem)MyGetProcAddress(gUser32Base, "GetDlgItem");
Mymemcmp = (MYmemcmp)MyGetProcAddress(hVcruntime, "memcmp");
MyExitProcess = (MYExitProcess)MyGetProcAddress(gKernelBase, "ExitProcess");
MySendMessageA = (MYSendMessageA)MyGetProcAddress(gUser32Base, "SendMessageA");
MyCreateThread = (MYCreateThread)MyGetProcAddress(gKernelBase, "CreateThread");
MyGetThreadContext = (MYGetThreadContext)MyGetProcAddress(gKernelBase, "GetThreadContext");
MyGetCurrentThread = (MYGetCurrentThread)MyGetProcAddress(gKernelBase, "GetCurrentThread");
MyTerminateThread = (MYTerminateThread)MyGetProcAddress(gKernelBase, "TerminateThread");
HMODULE hKernel32 = MyLoadLibraryExA("Kernel32.dll", NULL, NULL);
HMODULE hNtdll = MyLoadLibraryExA("ntdll.dll", NULL, NULL);
MYCreateToolhelp32Snapshot MyCreateToolhelp32Snapshot = (MYCreateToolhelp32Snapshot)MyGetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
MyThread32First = (MYThread32First)MyGetProcAddress(hKernel32, "Thread32First");
MyThread32Next = (MYThread32Next)MyGetProcAddress(hKernel32, "Thread32Next");
MyOpenThread = (MYOpenThread)MyGetProcAddress(hKernel32, "OpenThread");
MyNtQueryInformationThread = (MYNtQueryInformationThread)MyGetProcAddress(hNtdll, "NtQueryInformationThread");
MyIsDebuggerPresent = (MYIsDebuggerPresent)MyGetProcAddress(hKernel32, "IsDebuggerPresent");
}
3.2 密码弹窗
//密码弹框
void CallShllFun() {
WNDCLASS wc = { 0 };
HMODULE hInstance = MyGetModuleHandleA(NULL);
wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;//类风格
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;//实例句柄,代表此程序
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = 0;//BRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;//菜单
wc.lpszClassName = ("加密壳");
MyRegisterClassA(&wc);
gParentWnd = MyCreateWindowExA(
0,
"加密壳",//类名
"C++写的壳",//窗口名
WS_OVERLAPPEDWINDOW,//重叠窗口的风格 重要的参数----窗口风格
100, 200, 300, 130, //位置和大小
NULL, //父窗口句柄
NULL, //菜单句柄,没有菜单
hInstance, //实例句柄
NULL //附加信息
);
//显示、更新窗口
MyShowWindow(gParentWnd, SW_SHOW);
MyUpdateWindow(gParentWnd);
MSG msg = { 0 };
while (MyGetMessageA(&msg, 0, 0, 0)){
MyTranslateMessage(&msg);
MyDispatchMessageA(&msg);
}
return ;
}
//回调函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
CHAR Buff[0x11] = {0};
HWND h1000 = 0;
DWORD Len = 0;
switch (message)
{
case WM_CREATE:
MyCreateWindowExA(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 200, 30, hWnd, (HMENU)0x1000, NULL, NULL);
MyCreateWindowExA(WS_EX_CLIENTEDGE, TEXT("BUTTON"), TEXT("验证密码"), WS_CHILD | WS_VISIBLE, 10, 40, 80, 30, hWnd, (HMENU)0x1001, NULL, NULL);
MyCreateWindowExA(WS_EX_CLIENTEDGE, TEXT("BUTTON"), TEXT("取消启动"), WS_CHILD | WS_VISIBLE, 100, 40, 80, 30, hWnd, (HMENU)0x1002, NULL, NULL);
}
if (hWnd == gParentWnd)
{
switch (message)
{
case WM_CLOSE:
MyPostQuitMessage(0);
if (lParam != 100)
{
MyExitProcess(0);
}
break;
case WM_COMMAND:
{
int nId = LOWORD(wParam);
switch (nId)
{
case 0x1001:
h1000 = MyGetDlgItem(gParentWnd, 0x1000);
Len = MyGetWindowTextA(h1000, Buff, 0x10);
if (Len ==0)
{
MyMessageBoxA(0, TEXT("密码不能为空"), TEXT("提示"), 0);
}
else if (!Mymemcmp("1234", Buff, Len)) {
MyMessageBoxA(0, TEXT("密码正确"), TEXT("提示"), 0);
MySendMessageA(gParentWnd, WM_CLOSE, 0, 100);
}
else {
MyMessageBoxA(0, TEXT("密码错误"), TEXT("错误"), 0);
}
break;
case 0x1002:
MyExitProcess(0);
break;
default:
break;
}
}
}
}
return MyDefWindowProcA(hWnd, message, wParam, lParam);
};
3.3 解压
//解压缩函数
void UnCompression(SHARE ShareData){
// 1.获取节区头首地址
auto SecTextHead = GetSecHeadAddr(gImageBase, ".text");
// 2.解压压缩区段
// 内存地址
PCHAR lpPacked = ((PCHAR)gImageBase + ShareData.TextRVA);
// 获取解压后的大小
DWORD dwPackedSize = aPsafe_get_orig_size(lpPacked);
// 申请内存
PCHAR lpBuffer = (PCHAR)MyVirtualAlloc(NULL, dwPackedSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// 解压
aPsafe_depack(lpPacked, ShareData.TextCompressSize, lpBuffer, dwPackedSize);
//3. 还原区段,将.text区段的内容写入原地址
DWORD OldProtect = 0;
MyVirtualProtect(lpPacked, dwPackedSize, PAGE_EXECUTE_READWRITE, &OldProtect);
Mymemcpy(lpPacked, lpBuffer, SecTextHead->Misc.VirtualSize);
MyVirtualProtect(lpPacked, dwPackedSize, OldProtect, &OldProtect);
}
3.4 解密代码
//解密代码
void DecSection() {
DWORD OldProtect;
//获取.text区段
auto SectionHead = GetSecHeadAddr(gImageBase, ".text");
DWORD Size = SectionHead->Misc.VirtualSize;
CHAR* Address = (CHAR*)(SectionHead->VirtualAddress + gImageBase);
MyVirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, &OldProtect);
//循环解密
while (Size) {
*Address = *Address ^ 0x15;
Address++;
Size--;
}
MyVirtualProtect(Address, Size, OldProtect, &OldProtect);
}
3.5 加密IAT
//修复并加密IAT
void DecIat(SHARE ShareData) {
auto ImportTable = (PIMAGE_IMPORT_DESCRIPTOR)(gImageBase + ShareData.ImpTableVA);
while (ImportTable->Name)
{
//获取DLL名称
CHAR* Name = (CHAR*)(ImportTable->Name+gImageBase);
HMODULE Module = MyLoadLibraryExA(Name, NULL, NULL);
auto Iat = (int*)(ImportTable->FirstThunk + gImageBase);
for (size_t i = 0; Iat[i]!=0; i++)
{
DWORD FunAddr = 0;
DWORD OldProtect;
BYTE* DecCode = (BYTE*)MyVirtualAlloc(0, 0x50, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//解密代码
BYTE OpCode[] = {
0xE8, 0x01, 0x00, 0x00, 0x00, 0xE8, 0x58, 0x90, 0xB8,
0xA4, 0xA3, 0xA2, 0xA1, //加密
0xEB, 0x04, 0xE8, 0xBF, 0x78, 0xCC, 0x35, 0x59, 0x10, 0x40,
0x00, 0xEB, 0x01, 0xE9, 0x50, 0xEB, 0x02, 0x61, 0xE8, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xEB, 0x05, 0xE8, 0x5B,
0xC3, 0xE8, 0x88, 0x90, 0xC3, 0xE8, 0x72, 0x6B, 0x77, 0x88
};
MyVirtualProtect(&Iat[i], 0x50, PAGE_EXECUTE_READWRITE, &OldProtect);
//最高位为1,序号导入
if ((DWORD)Iat[i] & 0x80000000)
FunAddr = (DWORD)MyGetProcAddress(Module, (LPCSTR)(Iat[i] & 0xffff));
else{
auto AddressOfData = (PIMAGE_IMPORT_BY_NAME)(Iat[i] + gImageBase);
FunAddr = (DWORD)MyGetProcAddress(Module, AddressOfData->Name);//异或前77216210
}
//将加密后的函数地址写入堆空间的解密代码
FunAddr ^= 0x00401059;
OpCode[9] = FunAddr;
OpCode[10] = FunAddr >> 0x8;
OpCode[11] = FunAddr >> 0x10;
OpCode[12] = FunAddr >> 0x18;
Mymemcpy(DecCode, OpCode, 0x50);
Iat[i] = (int)DecCode;
MyVirtualProtect(&Iat[i], 0x50, OldProtect, &OldProtect);
}
ImportTable++;
}
}
3.6 修复重定位
//修复重定位
void FixReloca(SHARE ShareData) {
auto Relocs = (PIMAGE_BASE_RELOCATION)(ShareData.dwRelocRva + gImageBase);
DWORD OldProtect = 0;
while (Relocs->VirtualAddress)
{
DWORD OldProtect = 0;
MyVirtualProtect((LPVOID)(gImageBase + Relocs->VirtualAddress), 0x1000, PAGE_READWRITE, &OldProtect);
TypeOffset* items = (TypeOffset*)(Relocs + 1);
//遍历重定位项
int count = (Relocs->SizeOfBlock - 8) / 2;
for (int i = 0; i < count; ++i)
{
if (items[i].Type == 3)
{
DWORD Temp = 0;
// 计算出每一个需要重定位的数据所在的地址
DWORD* item = (DWORD*)(gImageBase + Relocs->VirtualAddress + items[i].Offset);
// 这里操作的是需要重定位的数据,通常是代码段(不可写)
*item = *item + gImageBase - ShareData.OldImageBase;
}
}
MyVirtualProtect((LPVOID)(gImageBase + Relocs->VirtualAddress), 0x1000, OldProtect, &OldProtect);
// 下一个重定位块
Relocs = (PIMAGE_BASE_RELOCATION)(Relocs->SizeOfBlock + (DWORD)Relocs);
}
}
3.7 处理TLS
//手动调用Tls
void CallTls(SHARE ShareData) {
//如果存在TLS表
if (ShareData.TlsVirtualAddress)
{
DWORD OldProtect = 0;
MyVirtualProtect(&(pOptionHead(gImageBase)->DataDirectory[9].VirtualAddress), 0x1000, PAGE_EXECUTE_READWRITE, &OldProtect);
//恢复Tls数据
pOptionHead(gImageBase)->DataDirectory[9].VirtualAddress = ShareData.TlsVirtualAddress;
MyVirtualProtect(&(pOptionHead(gImageBase)->DataDirectory[9].VirtualAddress), 0x1000, OldProtect, &OldProtect);
auto TlsTable = (PIMAGE_TLS_DIRECTORY)(pOptionHead(gImageBase)->DataDirectory[9].VirtualAddress + gImageBase);
//手动调用TLS回调函数
auto CallBackTable = (PIMAGE_TLS_CALLBACK*)(ShareData.TlsCallBackTableVa - ShareData.OldImageBase + gImageBase);
while (*CallBackTable)
{
(*CallBackTable)((PVOID)gImageBase, DLL_PROCESS_ATTACH, NULL);
CallBackTable++;
}
}
}
3.8 反调试
//反调试
void Debugging() {
gThread = MyCreateThread(NULL, NULL, ThreadProc1,NULL, 0, NULL);
//gThread = MyCreateThread(NULL, NULL, ThreadProc2, NULL, 0, NULL);//遍历所有线程的TEB+0xF24
if (MyIsDebuggerPresent())
MyMessageBoxA(0,"当前处于[被]调试状态n",0,0);
CheckNtGlobalFlag();
}
//反调试回调函数----硬件断点
DWORD WINAPI ThreadProc1(_In_ LPVOID lpParameter) {
while (true){
CONTEXT ConText;
DWORD Temp = CONTEXT_DEBUG_REGISTERS;
Mymemcpy(&ConText, &Temp, sizeof(CONTEXT));
//获取调试寄存器
MyGetThreadContext(MyGetCurrentThread(), &ConText);
//判断调试寄存器
if (ConText.Dr0 || ConText.Dr1 || ConText.Dr2 || ConText.Dr3)
{
MyMessageBoxA(0, "检测到硬件断点", 0, 0);
}
}
}
//反调试之 PEB+0x68
void CheckNtGlobalFlag() {
int NtGlobalFlag = 0;
__asm {
mov eax, dword ptr fs : [0x30]// 通过 TEB 偏移为 0x30 找到 PEB 结构
mov eax, dword ptr[eax + 0x68]// 通过 PEB 偏移为 0x68 的地方找到 NtGlobalFlag
mov NtGlobalFlag, eax // 如果当前的进程被调试,保存的就是 0x70
}
if(NtGlobalFlag==0x70)
MyMessageBoxA(0,"PEB+0x68==0x70,当前被调试n",0,0);
}
//反调试回调函数---遍历所有线程的TEB+0xF24使用到的结构体
typedef struct tagTHREADENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID; // this thread
DWORD th32OwnerProcessID; // Process this thread is associated with
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
} THREADENTRY32;
typedef THREADENTRY32 * PTHREADENTRY32;
typedef THREADENTRY32 * LPTHREADENTRY32;
typedef struct _CLIENT_ID {
DWORD UniqueProcess;
DWORD UniqueThread;
} CLIENT_ID;
typedef CLIENT_ID *PCLIENT_ID;
typedef enum MY_THREAD_INFORMATION_CLASS {
ThreadBasicInformation,
ThreadTimes,
ThreadPriority,
ThreadBasePriority,
ThreadAffinityMask,
ThreadImpersonationToken,
ThreadDescriptorTableEntry,
ThreadEnableAlignmentFaultFixup,
ThreadEventPair,
ThreadQuerySetWin32StartAddress,
ThreadZeroTlsCell,
ThreadPerformanceCount,
ThreadAmILastThread,
ThreadIdealProcessor,
ThreadPriorityBoost,
ThreadSetTlsArrayAddress,
ThreadIsIoPending,
ThreadHideFromDebugger
} MYTHREAD_INFORMATION_CLASS, *PMYTHREAD_INFORMATION_CLASS;
typedef ULONG KPRIORITY;
typedef struct _THREAD_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
KAFFINITY AffinityMask;
KPRIORITY Priority;
KPRIORITY BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
//遍历所有线程的TEB+0xF24的回调函数
DWORD WINAPI ThreadProc2(_In_ LPVOID lpParameter) {
while (true)
{
THREADENTRY32 entryThread ;
DWORD Temp = 28;
Mymemcpy(&entryThread, &Temp, sizeof(THREADENTRY32));
//线程快照
HANDLE hSnapshot = MyCreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return 0;
if (!MyThread32First(hSnapshot, &entryThread))
return 0;
//遍历线程快照
do{
DWORD ThreadId = entryThread.th32OwnerProcessID;
HANDLE hThread = MyOpenThread(THREAD_ALL_ACCESS, NULL, ThreadId);
THREAD_BASIC_INFORMATION threadBasicInfo;
//查询TEB地址
LONG status = MyNtQueryInformationThread(hThread, ThreadBasicInformation, &threadBasicInfo, sizeof(threadBasicInfo), NULL);
DWORD* DbgSsReserved = (DWORD*)((DWORD)(threadBasicInfo.TebBaseAddress) + 0xF20);
if (*(DbgSsReserved + 1))
MyMessageBoxA(0, "线程TEB+0xF24不为空,该线程正在调试程序", 0, 0);
} while (MyThread32Next(hSnapshot, &entryThread));
}
}
3.9 跳回原OEP
__asm
{
mov eax, gImageBase //获取当前进程gImageBase
mov ebx, ShareData.DestOEP
add eax,ebx
jmp eax
}
看雪ID:三一米田
https://bbs.pediy.com/user-home-881392.htm
*本文由看雪论坛 三一米田 原创,转载请注明来自看雪社区。
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
本文始发于微信公众号(看雪学院):使用 C++ 写壳
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论