先上圖吧:
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(); }