先上图吧:
ali.exe是加了壳的,WindowsProject1.exe是源程序,最明显的区别是加上该壳子后,它的文件大小变大了——壳的数据加了上去;
第二个区别是它们节的数目不一样,壳子程序原本有九个节,加上把源程序加进来的那一个节总共十个,而源程序是五个节
而他们的运行却是没有区别的:若壳子没有做任何事的话;
1.壳子是一个exe程序,源程序也是exe程序,所以需要写另外一个程序来把这两个exe程序合并在一起,合并的方式是让源程序作为壳子程序的数据,存放位置就是在壳子程序上再开一个节区出来,把源程序作为数据存放在该节区里面,至于节表的属性就无所谓了,加密不加密该节的数据也是看壳子程序的解壳逻辑。
2.另外合并壳与源程序的程序逻辑就是把两个目标程序的二进制代码读入到内存中,接下来根据PE的基础知识增加一个节,改一下应该改动的数据,把源程序的二进制代码导入该节,最后把内存数据保存为新的exe程序即可,这个新的exe程序 就是加完壳后的程序了。
3.剩下的就是解壳了,自然在第二步开始之前壳子程序的程序逻辑就应该默认已经拥有源程序的二进制数据了,即默认已经有了第一步加载后的那一个新增的节。PS:这会使得写第二步 合并出来的程序十分的难以调试,因为是反汇编新手,所以我使用了MESSAGEBOX这个windows窗口函数显示一些程序运行的实时信息来帮助自己调试。
4.说完壳子程序默认的这一注意事项就该说说壳子的代码实现了:
GetModuleFileName(NULL, exeFullPath, MAX_PATH);
这一行代码是加载该程序自身的启动路径的,win32的API什么的都不熟,找了好久的函数才发现就这一行代码就搞定了,获取路径的原因是因为要在该进程里以二进制的形式再次打开该exe程序,只不过目的不是让它跑起来,而是先前加载了一个新的节,要把它取出来,等等再以当前程序为基础再次创建一个新的进程(以挂起的形式——自然不能让它跑起来),然后再把PE文件的指针指向这个节的起始位置并调用更新PE各个HEAD的函数,例如更新DOS头啊,NT头啊诸如此类的——这之后所有的PE数据就变成了源程序的PE数据,而不是壳子程序的了。
5.然后主菜来了:以挂起的方式创建再次创建该进程一次,其实创建别的程序应该也不影响趴,这里没有尝试过,有空再看到这里又有些想法的话再试试吧——(因为这之后要获得该进程的主线程的CONTEXT结构,并且把该进程卸载了)——使用的函数是ntdll.dll库里面的NtUnmapViewOfSection 函数来卸载掉的——(该函数据说并未文档化,所以MSDN我自己没有查到,至于用法,自然是查网上大佬的程序代码)。卸载完之后就把之前那个新的节的数据当成PE文件给加载到以挂起的形式创建的那个程序的内存中去,不过要拉伸后的,申请内存的地址就是ImageBase,大小就是SizeOfImage,接下来把CONSTEXT里面的数据中的EAX改为源程序的OEP——(真实的那个OEP,不能是在硬盘中的原原本本的那个,那个是RVA),然后再把ImageBase改了,ImageBase在(Ebx寄存器记录了一个结构体)如果该数据偏移8,就是指向ImageBase的一个指针了,这里是看别人的代码,傀儡程序教程里面的,所以是不是指针有待查证,不能全信这里的,不过知识有限的我目前只能这样理解了。接着说,(Ebx寄存器记录了一个结构体)如果该数据偏移8,就是ImageBase的一个指针,用WriteProcessMemory 把该位置的数据改成源程序的ImageBase,然后使用SetThreadContext 改一下挂起进程的CONTEXT,接下来让它继续跑,如果源程序正常运行,且加壳后的程序与源程序的二进制数据不一样,那么恭喜,加壳、解壳,你都成功了!!!——这里要小小提醒下,如果程序一直无法运行,可能的原因就会有两种,加壳程序逻辑错误或者解壳程序逻辑错误,所以写的时候要仔细一点,要不然按我这样的写法,调bug 可就要两个程序块一起调了,联动BUG。。。嘿嘿,刺激!!!接着往下说吧:然后就是最后一步了,一个API搞定,不过这个API具体干了什么可就没表面这么简单咯,ResumeThread 该API我还是似懂非懂的,不过我相信接下来的学习一定可以让我清楚的了解到该API以及该API完成之后电脑到底做了些什么,现在只能蒙个大概(不知道对错)。
大概思路就是这样的吧,贴个代码,留个纪念。
PEStudy类的,学习PE后自己写的一些功能,直接搬过来用了。
先来PEStudy的头文件吧,如下:
鹅,两个代码中间加不了文字了。。再下面是PEStudy的cpp文件:
#pragma once #define _CRT_SECURE_NO_WARNINGS #include <wtypes.h> #include<stdio.h> #include<Windows.h> #include<stdlib.h> class PEStudy { public: size_t thisSize;//This data is the size of opende file IN CHAR _lpszfFile[100] = "C:\\Users\\86182\\source\\repos\\Shell\\Release\\Shell.exe"; OUT LPVOID* _pFileBuffer = nullptr; PIMAGE_RESOURCE_DIRECTORY pImageResourceHeader = nullptr;//this ptr is init in the findresource() func PIMAGE_DOS_HEADER pImageDosHeader = nullptr; PIMAGE_FILE_HEADER pImageFileHeader = nullptr; PIMAGE_OPTIONAL_HEADER pImageOptionalPEHeader = nullptr; PIMAGE_OPTIONAL_HEADER64 pImageOptionalHeader64 = nullptr; PIMAGE_SECTION_HEADER pImageSectionHeads[99] = { nullptr }; WORD SectionNumNow = 0;//The section number void initAllPoint(); size_t readFileBufferToTempBuffer(IN CHAR* lpszfFile, OUT LPVOID* pFileBuffer);// lpszFile: The road of the desFile void printDosHeader(); void printFilePEHeader(); void printOptionalPEHeader();//各种表的数据均未导出 void getSectionHeaders(); WORD maxNow(DWORD a, DWORD b) { return a > b ? a : b; } DWORD RVAToFOA(DWORD RVA); DWORD FOAToRVA(DWORD RVA); DWORD querryAddress(DWORD beforeAddress, DWORD Size, DWORD align); void addASection(const char* chunkName,DWORD chunkSize); void SaveToTheSameCatalog(const char* fileName); void addSize(DWORD chunkSize); /********************************************************************************* * the size of this added chunk is 0x1000 byte ,the same catalog is not doing now * fileName is the name of the file which saves your changeed data * chunkName is the name of the chunk you add *********************************************************************************/ void changeExportTable();//DataDirectory[0]; 导出表 void splayTheFile(); void findResource(); static PEStudy* getInstance(); private: void DFSFindResource1(DWORD offset); void DFSFindResource2(DWORD offset); static PEStudy* _Instance; };
#include "PEStudy.h" #include<Windows.h> PEStudy* PEStudy::_Instance = nullptr; PEStudy* PEStudy::getInstance() { if (!_Instance) { _Instance = new PEStudy; } return _Instance; } void PEStudy::addSize(DWORD chunkSize) { char* des = (char*)((DWORD)(*_pFileBuffer) + thisSize); thisSize += chunkSize; void* tmp = realloc(*_pFileBuffer, thisSize); if (!tmp);//printf("realloc failed!!!\n"); else *_pFileBuffer = tmp; memset(des, 0, chunkSize); } void PEStudy::changeExportTable()//DataDirectory[0]; 导出表 { if (!pImageOptionalPEHeader) { //printf("Open file failed!!!\n"); return; } getSectionHeaders(); _IMAGE_DATA_DIRECTORY now = pImageOptionalPEHeader->DataDirectory[0]; DWORD to = RVAToFOA(now.VirtualAddress); PIMAGE_EXPORT_DIRECTORY ExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)(*_pFileBuffer) + to); /*DWORD* to2 = (DWORD*)((DWORD)(*_pFileBuffer) + (DWORD)RVAToFOA((DWORD)ExportTable->AddressOfNames)); for (int i = 0; i < ExportTable->NumberOfNames + 7; i++) { DWORD dress = RVAToFOA(*to2); char* name1 = (char*)((DWORD)(*_pFileBuffer) + dress); to2++; }*/ DWORD* addressOfFunctions, * addressOfNames, * addressOfOrdinals; //这三个数组记录每个单元的是这些具体数据的虚拟地址 addressOfFunctions = (DWORD*)((DWORD)(*_pFileBuffer) + (DWORD)RVAToFOA((DWORD)ExportTable->AddressOfFunctions)); addressOfNames = (DWORD*)((DWORD)(*_pFileBuffer) + (DWORD)RVAToFOA((DWORD)ExportTable->AddressOfNames)); addressOfOrdinals = (DWORD*)((DWORD)(*_pFileBuffer) + (DWORD)RVAToFOA((DWORD)ExportTable->AddressOfNameOrdinals)); char* WriteTo = (char*)pImageSectionHeads[pImageFileHeader->NumberOfSections - 1]->PointerToRawData; ExportTable->AddressOfFunctions = FOAToRVA((DWORD)WriteTo); WriteTo = (char*)((DWORD)WriteTo + (DWORD)*_pFileBuffer);//把修改内存的writeto指针指向要修改的区域 DWORD* ali = (DWORD*)WriteTo; for (int i = 0; i < ExportTable->NumberOfNames; i++) { char* see = (char*)((DWORD)*_pFileBuffer + RVAToFOA((DWORD)addressOfNames[i])); } //memcpy(WriteTo, addressOfFunctions, ExportTable->NumberOfFunctions * 4); DWORD* RVAChange = (DWORD*)ali, * RVADes = NULL; for (int i = 0; i < ExportTable->NumberOfFunctions; i++)ali++;//挪出导出函数RVA储存空间 RVADes = (DWORD*)ali;//该地址开始转RVA for (int i = 0; i < ExportTable->NumberOfFunctions; i++) { DWORD source = (DWORD)((DWORD)(*_pFileBuffer) + RVAToFOA((DWORD)addressOfFunctions[i])); DWORD* tmp = (DWORD*)source; *ali = *tmp; ali++; }//函数的具体数据,挪出的空间存储他们 for (int i = 0; i < ExportTable->NumberOfFunctions; i++) { RVAChange[i] = FOAToRVA((DWORD)(RVADes + i) - (DWORD)(*_pFileBuffer)); } return; } DWORD PEStudy::querryAddress(DWORD beforeAddress, DWORD Size, DWORD align)// 原来的偏移地址 数据大小 对齐大小 { while ((beforeAddress + Size) % align)Size++; return beforeAddress + Size; } void PEStudy::SaveToTheSameCatalog(const char* fileName) { FILE* fp; if ((fp = fopen(fileName, "wb+")) == NULL) { //printf("Open failed!!!\nThere is another .exe opened this file"); return; } void* now = (*_pFileBuffer); if (!fwrite(now, 1, thisSize, fp)) { //printf("Push data wrong,the problem question is the data size is not same!!!\n"); return; } fflush(fp); fclose(fp); //printf("Sucess!!!\n"); } void PEStudy::addASection(const char* chunkName, DWORD chunkSize) { if (!pImageFileHeader) { //printf("FIle open failed!!!\n"); return; } if (!SectionNumNow) { getSectionHeaders(); if (!SectionNumNow) { //printf("FIle open failed!!!\n"); return; } } //pImageFileHeader pImageOptionalPEHeader->SizeOfImage += chunkSize; SectionNumNow = ++pImageFileHeader->NumberOfSections;//section加一(块) strcpy((char*)pImageSectionHeads[SectionNumNow - 1]->Name, chunkName); pImageSectionHeads[SectionNumNow - 1]->Misc.VirtualSize = (DWORD)chunkSize; pImageSectionHeads[SectionNumNow - 1]->Characteristics = (DWORD)0x60000020; pImageSectionHeads[SectionNumNow - 1]->SizeOfRawData = (DWORD)chunkSize; pImageSectionHeads[SectionNumNow - 1]->PointerToRawData = querryAddress(pImageSectionHeads[SectionNumNow - 2]->PointerToRawData , pImageSectionHeads[SectionNumNow - 2]->SizeOfRawData, pImageOptionalPEHeader->FileAlignment); pImageSectionHeads[SectionNumNow - 1]->VirtualAddress = querryAddress(pImageSectionHeads[SectionNumNow - 2]->VirtualAddress, maxNow(pImageSectionHeads[SectionNumNow - 2]->SizeOfRawData, pImageSectionHeads[SectionNumNow - 2]->Misc.VirtualSize), pImageOptionalPEHeader->SectionAlignment); pImageSectionHeads[SectionNumNow - 1]->NumberOfLinenumbers = pImageSectionHeads[SectionNumNow - 1]->NumberOfRelocations = pImageSectionHeads[SectionNumNow - 1]->PointerToLinenumbers = pImageSectionHeads[SectionNumNow - 1]->PointerToRelocations = 0; } void PEStudy::getSectionHeaders() { if (!pImageFileHeader) { //printf("pImageFileHeader is nullptr!!!\n"); return; } SectionNumNow = pImageFileHeader->NumberOfSections; auto filePoint = *(_pFileBuffer); for (int i = 0; i < SectionNumNow + 7; i++)//加上7的原因是获得接下来空节表的地址,配合addSection函数使用的 pImageSectionHeads[i] = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalPEHeader + pImageFileHeader->SizeOfOptionalHeader + i * 40); } void PEStudy::printOptionalPEHeader()//各种表的数据均未导出 { if (!pImageOptionalPEHeader) { //printf("pImageOptionalPEHeader is nullptr!!!\n"); return; } return; } void PEStudy::printFilePEHeader() { if (!pImageFileHeader) { //printf("pImageFileHeader is null ptr"); return; } return; } void PEStudy::initAllPoint() { if (!thisSize) { //printf("Open failed!!!\n"); return; } auto filePoint = *(_pFileBuffer); pImageDosHeader = (PIMAGE_DOS_HEADER)(filePoint); pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)filePoint + pImageDosHeader->e_lfanew + 0x4); pImageOptionalPEHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pImageFileHeader + 20); } size_t PEStudy::readFileBufferToTempBuffer(IN CHAR* lpszfFile, OUT LPVOID* pFileBuffer)// lpszFile: The road of the desFile { FILE* fp; DWORD fileSize = 0; void* pTempFileBuffer = NULL; if ((fp = fopen(lpszfFile, "rb")) == NULL) { MessageBox(0, TEXT("OpenFailed!!!"), 0, 0); return 0; } //find the teXt size fseek(fp, 0, SEEK_END); fileSize = ftell(fp); fseek(fp, 0, SEEK_SET); pTempFileBuffer = malloc(fileSize); if (!pTempFileBuffer) { MessageBox(0, TEXT("The leaved space is not enough!!!!\n"), 0, 0); fclose(fp); return 0; } size_t readSucess = fread(pTempFileBuffer, fileSize, 1, fp); if (!readSucess) { fclose(fp); return 0; } thisSize = fileSize; *pFileBuffer = pTempFileBuffer; pTempFileBuffer = NULL; fclose(fp); return fileSize; } void PEStudy::printDosHeader() { if (!pImageDosHeader) { ////printf("The pImageDOsHeader is nullptr!!!\n"); return; } } DWORD PEStudy::RVAToFOA(DWORD RVA) { if (!SectionNumNow) { getSectionHeaders(); if (!SectionNumNow) { //printf("The File opened failed!!!\n"); system("pause"); return -1; } } for (int i = 0; i < SectionNumNow; i++) { DWORD see = pImageSectionHeads[i]->VirtualAddress + pImageSectionHeads[i]->SizeOfRawData;//在数据里,就不用考虑内存对齐前的大小了 if (see >= RVA && pImageSectionHeads[i]->VirtualAddress <= RVA) { ////printf("RVA is in the %dth Section!!!\n", i); ////printf("FOA is 0x%X\n", RVA - pImageSectionHeads[i]->VirtualAddress + pImageSectionHeads[i]->PointerToRawData); return RVA - pImageSectionHeads[i]->VirtualAddress + pImageSectionHeads[i]->PointerToRawData; } } //printf("Data is illegal!!!\n"); return -1; } DWORD PEStudy::FOAToRVA(DWORD FOA) { if (!SectionNumNow) { getSectionHeaders(); if (!SectionNumNow) { //printf("The File opened failed!!!\n"); system("pause"); return -1; } } for (int i = 0; i < SectionNumNow; i++) { if (pImageSectionHeads[i]->PointerToRawData <= FOA && pImageSectionHeads[i]->PointerToRawData + pImageSectionHeads[i]->SizeOfRawData > FOA) { //printf("FOA is in the %dth Section!!!\n", i); //printf("RVA is 0x%X\n", FOA - pImageSectionHeads[i]->PointerToRawData + pImageSectionHeads[i]->VirtualAddress); return FOA - pImageSectionHeads[i]->PointerToRawData + pImageSectionHeads[i]->VirtualAddress; } } //printf("Data is illegal!!!\n"); return -1; } void PEStudy::DFSFindResource2(DWORD offset) { int idx = -1; for (int i = 0; i <= SectionNumNow; i++) if (!strcmp((const char*)pImageSectionHeads[i]->Name, ".rsrc"))idx = i; auto now = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)offset + (DWORD)(*_pFileBuffer) + pImageSectionHeads[idx]->PointerToRawData); PIMAGE_RESOURCE_DIRECTORY_ENTRY DiaLogNow = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)( (DWORD)now + (DWORD)16); //printf("---------------该资源的语言内码为:%d\n", DiaLogNow[0].Name); auto real = (PIMAGE_RESOURCE_DATA_ENTRY)((DWORD)DiaLogNow[0].OffsetToData + (DWORD)(*_pFileBuffer) + pImageSectionHeads[idx]->PointerToRawData); //printf("RVA:%X\nFOA:%X\nSIZE:%X\n", real->OffsetToData, RVAToFOA(real->OffsetToData), real->Size); return; //DFSFindResource(DiaLogNow[i].OffsetToDirectory); } void PEStudy::DFSFindResource1(DWORD offset) { int idx = -1; for(int i=0;i<=SectionNumNow;i++) if(!strcmp((const char *)pImageSectionHeads[i]->Name,".rsrc"))idx=i; auto now = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)offset + (DWORD)(*_pFileBuffer) + pImageSectionHeads[idx]->PointerToRawData); PIMAGE_RESOURCE_DIRECTORY_ENTRY DiaLogNow = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)( (DWORD)now + (DWORD)16); for (int i = 0; i < now->NumberOfIdEntries + now->NumberOfNamedEntries; i++) { if (!DiaLogNow[i].NameIsString) { //printf("该物品编号为:%d\n该位置的下一项目录是否为具体资源:%s\n", //DiaLogNow[i].Id, DiaLogNow[i].DataIsDirectory ? "NO" : "YES"); DFSFindResource2(DiaLogNow[i].OffsetToDirectory); } else; //printf("---------------------------用户自制资源!!!------------------------------------\n"); } return; } void PEStudy::findResource() { if (!pImageOptionalPEHeader) { //printf("open file failed!!!!\n"); return; } auto ali = pImageOptionalPEHeader->DataDirectory[2]; pImageResourceHeader = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)RVAToFOA(ali.VirtualAddress) + (DWORD)(*_pFileBuffer)); PIMAGE_RESOURCE_DIRECTORY_ENTRY firstDia = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)pImageResourceHeader + (DWORD)16); for (int i = 0; i < pImageResourceHeader->NumberOfIdEntries + pImageResourceHeader->NumberOfNamedEntries; i++) { if (!firstDia[i].NameIsString) { //printf("该类型为:%d\n该位置的下一项目录是否为具体资源:%s\n", //firstDia[i].Id, firstDia[i].DataIsDirectory ? "NO" : "YES"); DFSFindResource1(firstDia[i].OffsetToDirectory); } else; //printf("用户自制资源!!!\n"); } return; }
接下来是合并壳子程序以及源程序的cpp源码:
#include <stdio.h> #include <windows.h> #include "PEStudy.h" PEStudy* Shell; PEStudy* Src; int main() { Shell = new PEStudy; Src = new PEStudy; //确定路径 printf("请选择壳源的绝对路径:\n"); scanf("%s", Shell->_lpszfFile); printf("请选择程序的绝对路径:\n"); scanf("%s", Src->_lpszfFile); //打开这两个文件 DWORD otherSize = Src->getSize(Src->_lpszfFile); Shell->_pFileBuffer = (LPVOID*)malloc(sizeof(LPVOID)); DWORD shellSize = Shell->readFileBufferToTempBuffer(Shell->_lpszfFile, Shell->_pFileBuffer, otherSize); Shell->initAllPoint(); Shell->getSectionHeaders(); Src->_pFileBuffer = (LPVOID*)malloc(sizeof(LPVOID)); Src->readFileBufferToTempBuffer(Src->_lpszfFile, Src->_pFileBuffer, 0); Src->initAllPoint(); Src->getSectionHeaders(); Shell->addASection(".other", Src->thisSize); //Shell->addSize((DWORD)Shell->thisSize); //copy data void* now = (void*)((DWORD)*Shell->_pFileBuffer + shellSize); memcpy(now, *Src->_pFileBuffer, Src->thisSize); Shell->SaveToTheSameCatalog("ali.exe"); return 0; }
还有就是壳子的了:
PS:卸载函数的地方有用到ImageBase,偷懒直接写了exe默认基址0x400000,若程序出错,有可能是该位置的源程序的ImageBase不是0x400000,要去改改!!!!
有些头文件是编译器自带的,可以直接删除!!!
// Shell.cpp : 定义应用程序的入口点。 // #define _CRT_SECURE_NO_WARNINGS #include "framework.h" #include "Shell.h" #include "PEStudy.h" #include<wdmguid.h> #include<ntddkbd.h> #include<string.h> PEStudy* PEInstance = nullptr; TCHAR exeFullPath[MAX_PATH]; // Full path typedef struct _ChildProcessInfo { DWORD dwBaseAddress; DWORD dwReserve; } CHILDPROCESS, * PCHILDPROCESS; //getInstance就是单例模式,很好理解的一种设计模式,写游戏的时候学到的, //它相当于一个全局对象,PEStudy有该函数 BOOL CreateInjectProcess( PPROCESS_INFORMATION pi, PCONTEXT pThreadCxt, CHILDPROCESS* pChildProcess) { STARTUPINFO si; ZeroMemory(&si, sizeof(si)); DWORD* PPEB; DWORD read; // if (CreateProcess( NULL, // 指向一个NULL结尾的、用来指定可执行模块的宽字节字符串 exeFullPath, // 命令行字符串 NULL, // 指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。 NULL, // 如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。<同上> false,// 指示新进程是否从调用进程处继承了句柄。 CREATE_SUSPENDED, // 指定附加的、用来控制优先类和进程的创建的标 // CREATE_NEW_CONSOLE 新控制台打开子进程 // CREATE_SUSPENDED 子进程创建后挂起,直到调用ResumeThread函数 NULL, // 指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境 NULL, // 指定子进程的工作路径 &si, // 决定新进程的主窗体如何显示的STARTUPINFO结构体 pi // 接收新进程的识别信息的PROCESS_INFORMATION结构体 )) { MessageBox(0, TEXT("挂起创建程序成功!!!"), 0, 0); pThreadCxt->ContextFlags = CONTEXT_FULL; GetThreadContext(pi->hThread, pThreadCxt); PPEB = (DWORD*)pThreadCxt->Ebx; // ReadProcessMemory( pi->hProcess, &PPEB[2], (LPVOID) & (pChildProcess->dwBaseAddress), sizeof(DWORD), &read); return TRUE; } else MessageBox(0, TEXT("挂起创建程序失败!!!"), 0, 0); return FALSE; } BOOL UnloadShell(HANDLE ProcHnd, unsigned long BaseAddr) { typedef unsigned long(__stdcall* pfZwUnmapViewOfSection)(unsigned long, unsigned long); pfZwUnmapViewOfSection ZwUnmapViewOfSection = NULL; BOOL res = FALSE; HMODULE m = LoadLibrary(TEXT("ntdll.dll")); if (m) { MessageBox(0, TEXT("load exe sucess!!!"), 0, 0); ZwUnmapViewOfSection = (pfZwUnmapViewOfSection)GetProcAddress(m, "ZwUnmapViewOfSection"); if (ZwUnmapViewOfSection) res = (ZwUnmapViewOfSection((unsigned long)ProcHnd, BaseAddr) == 0); FreeLibrary(m); } else MessageBox(0, TEXT("load exe false!!!"), 0, 0); return res; } DWORD GetSelfImageSize(HMODULE hModule) { DWORD dwImageSize; _asm { mov ecx, 0x30 mov eax, fs: [ecx] mov eax, [eax + 0x0c] mov esi, [eax + 0x0c] add esi, 0x20 lodsd mov dwImageSize, eax } return dwImageSize;//getSize } //本程序使用该函数卸载挂起进程 BOOL UnMapTargetProcess(HANDLE hProcess, CONTEXT& stThreadContext) { typedef ULONG(WINAPI* PFNNtUnmapViewOfSection) (HANDLE ProcessHandle, PVOID BaseAddress); HMODULE hNtModule = GetModuleHandle(_T("ntdll.dll")); DWORD dwProcessBaseAddr = 0x400000; if (hNtModule == NULL) { hNtModule = LoadLibrary(_T("ntdll.dll")); if (hNtModule == NULL) { MessageBox(0, L"ntdll find faied!!!", 0, 0); return -1; } } PFNNtUnmapViewOfSection pfnNtUnmapViewOfSection = (PFNNtUnmapViewOfSection)GetProcAddress(hNtModule, "NtUnmapViewOfSection"); if (pfnNtUnmapViewOfSection == NULL) { return -1; } pfnNtUnmapViewOfSection(hProcess, (PVOID)dwProcessBaseAddr); return true; } BOOL InjectProcess(void) { char szModulePath[MAX_PATH]; DWORD dwImageSize = 0; STARTUPINFO si; PROCESS_INFORMATION pi; CONTEXT ThreadCxt; DWORD* PPEB; DWORD dwWrite = 0; CHILDPROCESS stChildProcess; LPVOID lpVirtual = NULL; PIMAGE_DOS_HEADER pDosheader = NULL; PIMAGE_NT_HEADERS pVirPeHead = NULL; HMODULE hModule = NULL; GetModuleFileName(NULL, exeFullPath, MAX_PATH); //获取当前进程的运行目录 //wsprintf(str, TEXT("%08X"), num);//wstr PEStudy::getInstance()->_pFileBuffer = (OUT LPVOID*)malloc(sizeof(OUT LPVOID)); sprintf(PEStudy::getInstance()->_lpszfFile, "%ws", exeFullPath); PEStudy::getInstance()->readFileBufferToTempBuffer(PEStudy::getInstance()->_lpszfFile, PEStudy::getInstance()->_pFileBuffer); PEInstance = PEStudy::getInstance(); PEInstance->initAllPoint(); PEInstance = PEStudy::getInstance(); PEInstance->getSectionHeaders(); int whi = -1; for (int i = 0; i < PEInstance->pImageFileHeader->NumberOfSections; i++) if (!strcmp((const char*)PEInstance->pImageSectionHeads[i]->Name, ".other")) { whi = i; break; } if (-1 == whi) { MessageBox(0, TEXT("该程序未被加壳,或仅含壳(空)"), 0, 0); return 0; } //unLock char* now = (char*)((DWORD)PEInstance->pImageSectionHeads[whi]->PointerToRawData + (DWORD)*PEInstance->_pFileBuffer); DWORD size = PEInstance->pImageSectionHeads[whi]->SizeOfRawData; //解密,若该节数据有加密的话 /*while (size--) { (*now) ^= 999; now++; }*/ //sucess MessageBox(0, TEXT("程序解密已完成!!!"), 0, 0); CONTEXT pThreadCxt;//挂起上下文 //CHILDPROCESS stChildProcess;//基址 ZeroMemory(szModulePath, MAX_PATH); hModule = GetModuleHandle(NULL); if (hModule == NULL) { return FALSE; } pDosheader = (PIMAGE_DOS_HEADER)hModule; pVirPeHead = (PIMAGE_NT_HEADERS)((DWORD)hModule + pDosheader->e_lfanew); LPVOID lpPuppetProcessBaseAddr = NULL;//分配内存成功与否 dwImageSize = GetSelfImageSize(hModule); // 以挂起模式启动一个傀儡进程,传进去的是OUT类型的参数,便于获得这些数据 if (CreateInjectProcess(&pi, &ThreadCxt, &stChildProcess)) { // 卸载需要注入进程中的代码 if (UnMapTargetProcess(pi.hProcess, ThreadCxt)) { // 重新分配内存 //loadNewPE,把PE的指针指向该节,把它当作新PE文件 *PEInstance->_pFileBuffer = (LPVOID)((DWORD)PEInstance->pImageSectionHeads[whi]->PointerToRawData + (DWORD)*PEInstance->_pFileBuffer); PEInstance->initAllPoint();//重新初始化各个头DOS,PE,OPPE // lpPuppetProcessBaseAddr = VirtualAllocEx(pi.hProcess, (LPVOID)PEInstance->pImageOptionalPEHeader->ImageBase, PEInstance->pImageOptionalPEHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (lpPuppetProcessBaseAddr) { MessageBox(0, L"sucess", 0, 0); } else { MessageBox(0, L"重新分配内存 failed", 0, 0); return TRUE; } if (lpPuppetProcessBaseAddr) { BOOL bRet = WriteProcessMemory(pi.hProcess, lpPuppetProcessBaseAddr, *PEInstance->_pFileBuffer, PEInstance->pImageOptionalPEHeader->SizeOfHeaders, NULL); if (!bRet) { return FALSE; } // 替换节 LPVOID lpSectionBaseAddr = (LPVOID)((DWORD)*PEInstance->_pFileBuffer + PEInstance->pImageDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)); PIMAGE_SECTION_HEADER pSectionHeader; DWORD dwIndex = 0; TCHAR ans[100]; //一些调试信息 /*wsprintf(ans, L"共有%d个节", PEInstance->pImageFileHeader->NumberOfSections); MessageBox(0, ans, 0, 0);*/ for (; dwIndex < PEInstance->pImageFileHeader->NumberOfSections; ++dwIndex) { pSectionHeader = (PIMAGE_SECTION_HEADER)lpSectionBaseAddr; bRet = WriteProcessMemory(pi.hProcess, (LPVOID)((DWORD)lpPuppetProcessBaseAddr + pSectionHeader->VirtualAddress), (LPCVOID)((DWORD)*PEInstance->_pFileBuffer + pSectionHeader->PointerToRawData), pSectionHeader->SizeOfRawData, NULL); if (!bRet) { MessageBox(0, L"节区拓展失败!!!\n", 0, 0); return FALSE; } lpSectionBaseAddr = (LPVOID)((DWORD)lpSectionBaseAddr + sizeof(IMAGE_SECTION_HEADER)); } MessageBox(0, L"节区拓展成功!!!\n", 0, 0); DWORD dwImageBase = PEInstance->pImageOptionalPEHeader->ImageBase; //ebx指向了一个结构,8个偏移之后指向了一个指针,指针指向了imageBase,(未确定) bRet = WriteProcessMemory(pi.hProcess, (LPVOID)(ThreadCxt.Ebx + 8), (LPCVOID)&dwImageBase, sizeof(PVOID), NULL); if (!bRet) { return FALSE; } // 替换入口点 ThreadCxt.Eax = dwImageBase + PEInstance->pImageOptionalPEHeader ->AddressOfEntryPoint; bRet = SetThreadContext(pi.hThread, &ThreadCxt); if (!bRet) { return FALSE; } ResumeThread(pi.hThread); //该消息出来后若程序未远行,则加壳失败——原因可能是加壳程序逻辑错误,或者解壳程序逻辑错误,如开头所说 MessageBox(0, L"载入成功!!!", 0, 0); } } } return TRUE; } int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { //调试 //MessageBox(0, L"ReadFileFailed!!!\n", 0, 0); return InjectProcess(); }