上海高端品牌網(wǎng)站制作熱點(diǎn)新聞
我們繼續(xù)延申調(diào)試事件的話題,實(shí)現(xiàn)進(jìn)程轉(zhuǎn)存功能,進(jìn)程轉(zhuǎn)儲(chǔ)功能是指通過(guò)調(diào)試API使獲得了目標(biāo)進(jìn)程控制權(quán)的進(jìn)程,將目標(biāo)進(jìn)程的內(nèi)存中的數(shù)據(jù)完整地轉(zhuǎn)存到本地磁盤(pán)上,對(duì)于加殼軟件,通常會(huì)通過(guò)加密、壓縮等手段來(lái)保護(hù)其代碼和數(shù)據(jù),使其不易被分析。在這種情況下,通過(guò)進(jìn)程轉(zhuǎn)儲(chǔ)功能,可以將加殼程序的內(nèi)存鏡像完整地保存到本地,以便進(jìn)行后續(xù)的分析。
在實(shí)現(xiàn)進(jìn)程轉(zhuǎn)儲(chǔ)功能時(shí),主要使用調(diào)試API和內(nèi)存讀寫(xiě)函數(shù)。具體實(shí)現(xiàn)方法包括:以調(diào)試方式啟動(dòng)目標(biāo)進(jìn)程,將其暫停在運(yùn)行前的位置;讓目標(biāo)進(jìn)程進(jìn)入運(yùn)行狀態(tài);使用ReadProcessMemory函數(shù)讀取目標(biāo)進(jìn)程內(nèi)存,并將結(jié)果保存到緩沖區(qū);將緩沖區(qū)中的數(shù)據(jù)寫(xiě)入文件;關(guān)閉目標(biāo)進(jìn)程的調(diào)試狀態(tài)。
首先老樣子先來(lái)看OnException
回調(diào)事件,當(dāng)進(jìn)程被斷下時(shí)首先通過(guò)線程函數(shù)恢復(fù)該線程的狀態(tài),在進(jìn)程被正確解碼并運(yùn)行起來(lái)時(shí)直接將該進(jìn)程的EIP入口地址傳遞給MemDump();
內(nèi)存轉(zhuǎn)存函數(shù),實(shí)現(xiàn)轉(zhuǎn)存功能;
void OnException(DEBUG_EVENT *pDebug, BYTE *bCode)
{CONTEXT context;DWORD dwNum;BYTE bTmp;// 打開(kāi)當(dāng)前進(jìn)程與線程HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pDebug->dwProcessId);printf("[+] 當(dāng)前打開(kāi)進(jìn)程句柄: %d 進(jìn)程PID: %d \n", hProcess, pDebug->dwProcessId);HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pDebug->dwThreadId);printf("[+] 當(dāng)前打開(kāi)線程句柄: %d 線程PPID: %d \n", hThread, pDebug->dwThreadId);// 暫停當(dāng)前線程SuspendThread(hThread);// 讀取出異常產(chǎn)生的首地址ReadProcessMemory(hProcess, pDebug->u.Exception.ExceptionRecord.ExceptionAddress, &bTmp, sizeof(BYTE), &dwNum);printf("[+] 當(dāng)前異常產(chǎn)生地址為: 0x%08X \n", pDebug->u.Exception.ExceptionRecord.ExceptionAddress);// 設(shè)置當(dāng)前線程上下文,獲取線程上下文context.ContextFlags = CONTEXT_FULL;GetThreadContext(hThread, &context);printf("[-] 恢復(fù)斷點(diǎn)前: EAX = 0x%08X EIP = 0x%08X \n", context.Eax, context.Eip);// 將剛才的CC斷點(diǎn)取消,也就是回寫(xiě)原始的指令集WriteProcessMemory(hProcess, pDebug->u.Exception.ExceptionRecord.ExceptionAddress, bCode, sizeof(BYTE), &dwNum);// 當(dāng)前EIP減一并設(shè)置線程上下文context.Eip--;SetThreadContext(hThread, &context);printf("[+] 恢復(fù)斷點(diǎn)后: EAX = 0x%08X EIP = 0x%08X \n", context.Eax, context.Eip);printf("[+] 獲取到動(dòng)態(tài)入口點(diǎn): 0x%08x \n", pDebug->u.CreateProcessInfo.lpBaseOfImage);// 轉(zhuǎn)儲(chǔ)內(nèi)存鏡像MemDump(pDebug, context.Eip, (char *)"dump.exe");// 恢復(fù)線程ResumeThread(hThread);CloseHandle(hThread);CloseHandle(hProcess);
}
MemDump函數(shù)中,首先通過(guò)調(diào)用CreateFile
函數(shù)打開(kāi)me32.szExePath
路徑也就是轉(zhuǎn)存之前的文件,通過(guò)使用VirtualAlloc
分配內(nèi)存空間,分配大小是PE頭中文件實(shí)際大小,接著OpenProcess
打開(kāi)正在運(yùn)行的進(jìn)程,并使用ReadProcessMemory
讀取文件的數(shù)據(jù),此處讀取的實(shí)在內(nèi)存中的鏡像數(shù)據(jù),當(dāng)讀取后手動(dòng)修正,文件的入口地址,及文件的對(duì)齊方式,接著定位PE節(jié)區(qū)數(shù)據(jù),找到節(jié)區(qū)首地址,并循環(huán)將當(dāng)前節(jié)區(qū)數(shù)據(jù)賦值到新文件緩存中,最后當(dāng)一切準(zhǔn)備就緒,通過(guò)使用WriteFile
函數(shù)將轉(zhuǎn)存后的文件寫(xiě)出到磁盤(pán)中;
void MemDump(DEBUG_EVENT *pDe, DWORD dwEntryPoint, char *DumpFileName)
{// 得到當(dāng)前需要操作的進(jìn)程PIDDWORD dwPid = pDe->dwProcessId;MODULEENTRY32 me32;// 對(duì)系統(tǒng)進(jìn)程拍攝快照HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);me32.dwSize = sizeof(MODULEENTRY32);// 得到第一個(gè)模塊句柄,第一個(gè)模塊句柄也就是程序的本體BOOL bRet = Module32First(hSnap, &me32);printf("[+] 當(dāng)前轉(zhuǎn)儲(chǔ)原程序路徑: %s \n", me32.szExePath);// 打開(kāi)源文件,也就是dump之前的文件HANDLE hFile = CreateFile(me32.szExePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);if (hFile == INVALID_HANDLE_VALUE)exit(0);// 判斷PE文件的有效性IMAGE_DOS_HEADER imgDos = { 0 };IMAGE_NT_HEADERS imgNt = { 0 };DWORD dwReadNum = 0;// 讀入當(dāng)前內(nèi)存程序的DOS頭結(jié)構(gòu)ReadFile(hFile, &imgDos, sizeof(IMAGE_DOS_HEADER), &dwReadNum, NULL);// 判斷是否是一個(gè)合格的DOS頭if (imgDos.e_magic != IMAGE_DOS_SIGNATURE)return;// 設(shè)置文件指針到NT頭上SetFilePointer(hFile, imgDos.e_lfanew, 0, FILE_BEGIN);ReadFile(hFile, &imgNt, sizeof(IMAGE_NT_HEADERS), &dwReadNum, NULL);// 判斷是否是合格的NT頭if (imgNt.Signature != IMAGE_NT_SIGNATURE)return;// 得到EXE文件的大小DWORD BaseSize = me32.modBaseSize;printf("[+] 當(dāng)前內(nèi)存文件大小: %d --> NT結(jié)構(gòu)原始大小: %d 一致性檢測(cè): True \n", BaseSize, imgNt.OptionalHeader.SizeOfImage);// 如果PE頭中的大小大于實(shí)際內(nèi)存大小,則以PE頭中大小為模板if (imgNt.OptionalHeader.SizeOfImage > BaseSize){BaseSize = imgNt.OptionalHeader.SizeOfImage;}// 分配內(nèi)存空間,分配大小是PE頭中文件實(shí)際大小,并打開(kāi)進(jìn)程LPVOID pBase = VirtualAlloc(NULL, BaseSize, MEM_COMMIT, PAGE_READWRITE);printf("[+] 正在分配轉(zhuǎn)儲(chǔ)空間 句柄: %d \n", pBase);HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);// 讀取文件的數(shù)據(jù),此處讀取的實(shí)在內(nèi)存中的鏡像數(shù)據(jù)bRet = ReadProcessMemory(hProcess, me32.modBaseAddr, pBase, me32.modBaseSize, NULL);// 判斷PDOS頭的有效性PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBase;if (pDos->e_magic != IMAGE_DOS_SIGNATURE)return;// 計(jì)算出NT頭數(shù)據(jù)PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (PBYTE)pBase);if (pNt->Signature != IMAGE_NT_SIGNATURE)return;// 設(shè)置文件的入口地址pNt->OptionalHeader.AddressOfEntryPoint = dwEntryPoint - pNt->OptionalHeader.ImageBase;printf("[*] 正在設(shè)置Dump文件相對(duì)RVA入口地址: 0x%08X \n", pNt->OptionalHeader.AddressOfEntryPoint);// 設(shè)置文件的對(duì)齊方式pNt->OptionalHeader.FileAlignment = 0x1000;printf("[*] 正在設(shè)置Dump文件的對(duì)齊值: %d \n", pNt->OptionalHeader.FileAlignment);// 找到節(jié)區(qū)首地址,并循環(huán)將當(dāng)前節(jié)區(qū)數(shù)據(jù)賦值到新文件緩存中PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((PBYTE)&pNt->OptionalHeader + pNt->FileHeader.SizeOfOptionalHeader);for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++){pSec->PointerToRawData = pSec->VirtualAddress;printf("[+] 正在將虛擬地址: 0x%08X --> 設(shè)置到文件地址: 0x%08X \n", pSec->VirtualAddress, pSec->PointerToRawData);pSec->SizeOfRawData = pSec->Misc.VirtualSize;printf("[+] 正在將虛擬大小: %d --> 設(shè)置到文件大小: %d \n", pSec->Misc.VirtualSize, pSec->SizeOfRawData);pSec++;}CloseHandle(hFile);// 打開(kāi)轉(zhuǎn)儲(chǔ)后的文件.hFile = CreateFile(DumpFileName, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);if (hFile == INVALID_HANDLE_VALUE)exit(0);printf("[*] 轉(zhuǎn)儲(chǔ) %s 文件到本地 \n", DumpFileName);DWORD dwWriteNum = 0;// 將讀取的數(shù)據(jù)寫(xiě)入到文件bRet = WriteFile(hFile, pBase, me32.modBaseSize, &dwWriteNum, NULL);if (dwWriteNum != me32.modBaseSize || FALSE == bRet)printf("寫(xiě)入錯(cuò)誤 !");// 關(guān)閉于釋放資源CloseHandle(hFile);VirtualFree(pBase, me32.modBaseSize, MEM_RELEASE);CloseHandle(hProcess);CloseHandle(hSnap);
}
讀者可自行運(yùn)行這段程序,當(dāng)程序運(yùn)行后即可將指定的一個(gè)文件內(nèi)存數(shù)據(jù)完整的轉(zhuǎn)存到磁盤(pán)中,輸出效果如下圖所示;
本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/5e2f7b11.html
版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請(qǐng)注明出處!