偶然翻了一下手機日歷,原來今天是夏至啊,時間過的真快。ISCC的比賽已經持續了2個多月了,我也跟著比賽的那些題目學了2個月.......雖然過程很辛苦,但感覺還是很幸運的,能在大三的時候遇到ISCC,不管怎樣,對我來說都會是一個很好的鍛煉機會。
在做綜合關的逆向破解的題目,遇到了很多蛋疼的問題,磕磕碰碰把《加密與解密》看完了。還是老習慣,把這1個多星期以后學到的,想到的做一個總結,希望能有一個思想上的提高,也希望能對其他人有一個借鑒的效果吧。這里想起lenus大神的一句話:
Hacker的精神里面除了學習(learn)的第一精神以外,還應該是共享(share)的精神。
好,廢話不多說,開始吧。
1. 逆向?破解?
一開始接觸逆向,我直接是使用的一些加殼脫殼工具,都是鼠標點一下就出來了,這段時間深入了解了逆向的原理之后,我感覺逆向是一個綜合性很強的技術,中間還需要windows編程,PE結果,匯編等知識。
對于脫殼,我的理解是:脫殼不意味著它字面上的意思,把外殼直接拿掉,然后就扔掉(像吃水果一樣),脫殼本質上是一個高級的crack技術,也就是繞過破解。
這是一張關于殼的圖示:
而我們不管用lordPE, procDump, 還是手工脫殼也好,其實本質上就是要改變程序的執行流,讓程序跳過頭部(實際中不一定是頭部,這里只是邏輯上的)的一段殼代碼,而直接來到原程序的入口代碼處,也就是常說的OEP,那問題就來了,那脫殼就這么簡單?
當然不是,我個人感覺,脫殼并不僅僅是改變程序執行流這么簡單,因為被加過殼的程序往往IAT,重定位表,節區都被加密,壓縮處理過了。如果你直接強行跳轉過來,程序也是不能執行的。所以,網上常說的找OEP的原理就在這里。為什么要找OEP呢?其實本質上來說,這并不是最重要的,不要被這些形式上的東西框住了思維,我們從原理上思考,一個程序要運行,無非需要幾種東西:
1. 可以執行的機器碼
2. IAT
3. 重定位表(DLL)
4. 資源代碼(不太重要就是了,因為并不影響逆向的結果)
轉換了思維之后,我就豁然開朗了,我們要做的就是找到代碼中的某一行,恰好已經全部完成了代碼的解壓縮和解密,IAT的重寫,重定位表的重寫,做好這些事后,我們就可以dump下來了,至于是不是OEP其實不是很重要,所謂的找OEP是因為,一般情況下在OEP的時候恰好都滿足了上面的條件,而且也比較好找到,所以,OEP是dump的首選,如果你理解了原理,這么做也沒有錯。
?
第二點就是:為什么要用ImportREC來重建IAT表呢?它的原理又是什么呢?
這里首先要從加殼的原理說起,不管你是UPX壓縮殼,ASProtect,穿山甲加密殼,還是VMP虛擬機殼...基本上來說都會破壞原本的IAT,將原本的IAT移到一個新的節區里面并加密等處理,然后構建自己的IAT表,并修改PE頭。為什么要這么做呢?
因為殼必須保證它能調用到自己需要的函數,但是原程序并不能保證這點,所以殼必須自己構造自己的IAT三劍客:
調用LoadLibrary將dll文件映射到調用進程的地址空間中
調用GetModualHandle獲得dll模塊句柄
調用GetProcAddress獲取輸入函數的地址
一般殼程序都會構造這三個API,然后重建自己的IAT表,這樣殼程序就能保證調用到自己需要的所有winAPI了。
而我們脫殼的是內存中的映像,即使這時候原程序的IAT已經恢復過來了(解壓縮,解密),但是位置已經變了,原程序無法直接調用了。而ImportREC的作用就是找到這里API對用RVA,然后在一段空的地方重建這些結構,逆向構造出一個IAT出來,然后修改PE頭,完成IAT的修復。明白了原理,自己手工修復IAT也是一樣的。
修復重定位表和資源表的原理也是一樣的,就不在詳述了。
?
下面,我們通過一個dump實例的編程過程來深刻的理解一個dump的思想。
接下來的程序是我們對看雪上的一篇帖子的學習筆記,是lenus大神的脫殼教程,我這里就當是做一個學習筆記。
?
一:dump小程序的目標
對于dump來說,他的英文翻譯就是“轉存”。也就是說把內存中或者其他的輸入轉存到另一個位置,當然對于我們現在說的dump就是把內存中運行的PE進程的數據,從內存中抓取出來,然后在用文件的形式保存下來。
根據上面的分析我們基本上得到了一個這樣的思維。Dump程序要做的事分幾個基本的步驟:
1.??在系統中找到目標進程
2.??在進程中確定進程的大小imagesize
3.??把進程中的數據保存到文件
?
Code:
resource.h
#define ID_FLESH??????????????????????? 2
#define IDD_DIALOG1???????????????????? 101
#define ICO_MAIN??????????????????????? 103
#define IDC_PROCESS???????????????????? 1028
#define IDC_CORRECT???????????????????? 1033
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE??????? 105
#define _APS_NEXT_COMMAND_VALUE???????? 40001
#define _APS_NEXT_CONTROL_VALUE???????? 1034
#define _APS_NEXT_SYMED_VALUE?????????? 101
#endif
#endif
?
#include <windows.h>
#include <tlhelp32.h>
#include "resource.h"
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL GetProcessListFunc(HWND hDlg,HWND hWindList); // 列出各個進程的函數,刷新時也用到
LPCTSTR SaveAsFunc(HWND hDlg);???????????????????? //通用對話框函數
BOOL DumpFunc(HWND hDlg,HWND hWindList);?????????? // 主要的dump函數,調用其他的小功能函數實現其功能
BOOL CreateDumpFile(HWND hDlg,LPCTSTR Dump_Name,HGLOBAL hMem);??? //生成dump文件函數,把dump的東西寫到磁盤上
HGLOBAL ReadProcess(HWND hDlg,DWORD IDProcess);????? //讀取目標進程空間,放置到本空間申請的堆中
int GetSizeOfImage(HWND hDlg,DWORD IDProcess);?????? // 獲取pe文件的SizeOfImage
BOOL CheckPEFunc(HWND hDlg,HANDLE hProcess);??????? //檢查pe文件的完整性
BOOL CorrectSizeFunc(HWND hDlg,HWND hWindList);?????????????????? //糾正文件的大小
LPCTSTR GetFilePath(HWND hDlg,DWORD IDProcess);//獲取目標exe的絕對路徑
BOOL ModifySectionFunc(HWND hDlg,LPCTSTR Dump_Name);//修改文件的節表使其RA=RVA ,RS=RVS
BOOL CopyThePEHead(HWND hDlg,LPCTSTR Dump_Name);?? //把原來PE文件的頭部復制到dump文件中
全局變量/
int sizeoffile=0;
int sizeofimage=0;????????????? //當使用了CorrectSizeFunc后這個有了具體數值,就不需要再次獲取了
int BaseAddress=0x400000;???? ?
DWORD ID=0;?????????????????? //這個是用來控制進程的切換的
///
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
?????????????????? ??? ??? ??? ??? ??? ??? ?PSTR szCmdLine, int iCmdShow)
{
?? ?DialogBoxParam (hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DlgProc,(LPARAM)hInstance);
?? ?return TRUE;
}
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
?? ???? static HINSTANCE hInstance;
?? ??? ?static HWND hWindList;
??? ??? ?switch (message)
??? ??? ?{
?? ??? ?case??? WM_CLOSE:
?? ??? ??? ???? EndDialog (hDlg, 0) ;
????????????? ??? ?return TRUE ;
??? ??? ?case ?? ?WM_INITDIALOG :
?? ??? ??? ???? hInstance = (HINSTANCE) lParam;
?? ??? ??? ???? SendMessage(hDlg,WM_SETICON,ICON_BIG,(LPARAM)LoadIcon(hInstance,MAKEINTRESOURCE(ICO_MAIN)));
?? ??? ??? ??? ?hWindList = GetDlgItem(hDlg,IDC_PROCESS);
?? ??? ??? ??? ?if(!GetProcessListFunc(hDlg,hWindList))
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?MessageBox(hDlg,TEXT("Fail to get the process"),TEXT("Sorry"),MB_OK | MB_ICONSTOP);
?? ??? ??? ??? ??? ?EndDialog(hDlg,0);?? ??? ??? ?
?? ??? ??? ??? ?}
?? ??? ??? ??? ?return TRUE ;
?? ??? ??? ??? ?
??? ??? ?case ?? ?WM_COMMAND :
???????? ??? ??? ?switch (LOWORD (wParam))
???????? ??? ??? ?{
???????? ??? ??? ??? ?case ?? ?IDOK :
????????????? ??? ??? ??? ??? ??? ?DumpFunc(hDlg,hWindList);
????????????? ??? ??? ??? ??? ??? ?return TRUE ;
?? ??? ??? ??? ??? ?case?? ?ID_FLESH:
?? ??? ??? ??? ??? ??? ??? ??? ?if(!GetProcessListFunc(hDlg,hWindList))
?? ??? ??? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ??? ??? ?MessageBox(hDlg,TEXT("Fail to get the process"),TEXT("Sorry"),MB_OK | MB_ICONSTOP);
?? ??? ??? ??? ??? ??? ??? ??? ??? ?EndDialog(hDlg,0);?? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ??? ??? ?return TRUE;
?? ??? ??? ??? ??? ?case?? ?IDC_CORRECT:
?? ??? ??? ??? ??? ??? ???????? if(!CorrectSizeFunc(hDlg,hWindList))
?? ??? ??? ??? ??? ??? ??? ??? ??? ?MessageBox(hDlg,TEXT("The correct size function is faile..."),TEXT("Fail..."),MB_OK | MB_ICONWARNING);
?? ??? ??? ??? ??? ??? ???????? return TRUE;
?? ??? ??? ??? ?}
???????? ??? ??? ?break ;
???? }
??? ?return FALSE ;
}
BOOL GetProcessListFunc(HWND hDlg,HWND hWindList)
{
?? ?//此函數是進程列表的作用,在此不作過多介紹
?? ?HANDLE hProcessSnap = NULL;
?? ?PROCESSENTRY32 pe32?? = {0};
?? ?SendMessage(hWindList,LB_RESETCONTENT,0,0);
?? ?pe32.dwSize = sizeof(PROCESSENTRY32);
?? ?hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
?? ?if (hProcessSnap == INVALID_HANDLE_VALUE) ?
?? ?return FALSE;
?? ?
?? ?if (!Process32First(hProcessSnap, &pe32))
?? ?{
?? ??? CloseHandle (hProcessSnap);
?? ??? return FALSE;
?? ?}
?? ?do
?? ?{
?? ??? ?WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_ADDSTRING,0,(LPARAM)pe32.szExeFile);
?? ??? ?SendMessage(hWindList,LB_SETITEMDATA,tmp,(LPARAM)pe32.th32ProcessID);
?? ?}
?? ?while (Process32Next(hProcessSnap, &pe32));
?? ?CloseHandle (hProcessSnap);
?? ?
??? return TRUE;
}
BOOL DumpFunc(HWND hDlg,HWND hWindList)
{
?? ?HGLOBAL hMem;
??? LPCTSTR Dump_Name=NULL;??????????????????? //感覺有點問題,這個只是指針沒有開辟足夠的空間。
????????????????????????????????????????????? //從數組(規定好的)到指針(未規定好的)就可以,
?? ?????????????????????????????????????????? //從指針到數組就不可以
??? WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_GETCURSEL,0,0);
?? ?if (tmp==LB_ERR)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("Please choose a process..."),TEXT("oh...no,no,no..."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?DWORD IDProcess=SendMessage(hWindList,LB_GETITEMDATA,tmp,0); //獲得此列單里面的進程ID
?? ?ID=IDProcess;
?? ?hMem=ReadProcess(hDlg,IDProcess);
?? ?if(hMem)?????????????????????????????????? //如果返回的hMen不正確說明沒有正確的申請到空間
?? ?{
?? ??? ?if(sizeoffile!=0)???????????????????? //沒有大小的dump 是沒有意義的
?? ??? ?{
?? ??? ??? ?Dump_Name=SaveAsFunc(hDlg);?????? //要保存的文件名
?? ??? ??? ?if(Dump_Name)???????????????????? //如果得到的文件名是空就不繼續執行
?? ??? ??? ?{
?? ??? ??? ??? ?CreateDumpFile(hDlg,Dump_Name,hMem); //把數據寫入文件中
?? ??? ??? ??? ?GlobalFree(hMem);??????????????????? //資源是可貴的,釋放空間
?? ??? ??? ?}
?? ??? ?}
?? ?}
?? ??? ?
?? ??? ?return TRUE;
}
LPCTSTR SaveAsFunc(HWND hDlg)
{
?? ?//獲取你要保存的文件名,默認為dumped.exe
?? ?HANDLE hFile;
??? static?? ?char szFileName[MAX_PATH]="dumped";
?? ?OPENFILENAME stOF={0};
??? stOF.hwndOwner=hDlg;
?? ?stOF.lStructSize=sizeof(stOF);
?? ?stOF.lpstrFilter="*.*";
?? ?stOF.lpstrDefExt="exe";
?? ?stOF.nMaxFile=MAX_PATH;?? ?
?? ?stOF.lpstrFile=szFileName;
?? ?if(!GetSaveFileName(&stOF))
?? ??? ??? ?return FALSE;
??? char szBuffer[100];
?? ?char szMsg[]="%s 已存在。要替換它嗎?";
?? ?wsprintf(szBuffer,szMsg,szFileName);
?? ?hFile=CreateFile(szFileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile != INVALID_HANDLE_VALUE)
?? ?{
?? ??? if(IDNO==MessageBox(hDlg,szBuffer,TEXT("另存為"),MB_YESNO | MB_ICONWARNING))
?? ??? {
?? ??? ??? CloseHandle(hFile);
?? ??? ??? return FALSE;
?? ??? }
?? ?}
?? ?CloseHandle(hFile);
?? ?return szFileName;
}
BOOL CreateDumpFile(HWND hDlg,LPCTSTR Dump_Name,HGLOBAL hMem)
{
?? ?//創建一個新的dump文件
?? ?HANDLE hFile=CreateFile(Dump_Name,GENERIC_WRITE | GENERIC_READ,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("Maybe you have alreadly had a this name file:("),
?? ??? ??? ??????? TEXT("Can't create a file"),MB_OK | MB_ICONWARNING);
?? ??? ?GlobalFree(hMem);
?? ??? ?return FALSE;
?? ?}
?? ?int NumberOfBytesWritten;
?? ?WriteFile(hFile,hMem,sizeoffile,(LPDWORD)&NumberOfBytesWritten,NULL);??? //注意這個函數第三個參數是必要的!
??? CloseHandle(hFile);
?? ?if(!CopyThePEHead(hDlg,Dump_Name))
?? ?{
?? ??? ?//復制PE頭
?? ??? ?MessageBox(hDlg,TEXT("復制PE頭失敗了"),TEXT("失敗了"),MB_OK | MB_ICONWARNING);
?? ?}?? ?
?? ?if(!ModifySectionFunc(hDlg,Dump_Name))
?? ?{
?? ??? ?//節表對齊
?? ??? ?MessageBox(hDlg,TEXT("修改節表失敗了"),TEXT("失敗了"),MB_OK | MB_ICONWARNING);
?? ?}
?? ?MessageBox(hDlg,TEXT("文件已經dump成功"),TEXT("Lenus'ExeDump"),MB_OK | MB_ICONINFORMATION);//勝利的號角!?? ?
?? ?return TRUE;
}
HGLOBAL ReadProcess(HWND hDlg,DWORD IDProcess)
{
?? ?//此函數是讀取目標進程的空間,并把他寫入到自己內存空間里面的一個內存塊中
?? ?
?? ?HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,IDProcess);//使用上面獲得的進程id
?? ?if(!hProcess)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't open the process:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?if(sizeofimage==0 || ID!=IDProcess)??????????????? ?
?? ?//當更換當前的進程或者沒有使用修正的功能的時候需要重新的獲取
?? ?//由于不知道在更換選項的時候會發出什么消息,所以只能這么干 so foolish!!
?? ?{
?? ??? ??? ?sizeofimage=GetSizeOfImage(hDlg,IDProcess);
?? ?}
?? ?
?? ?if(!sizeofimage)
?? ?{
?? ??? ?return FALSE;
?? ?}
??? //為了以防萬一,讓sizeofimage增加一個文件對齊度。
?? ?if(!(sizeofimage%0x1000))????????????????????????? //如果是文件對齊度的整數倍的時候就不處理
?? ??? ?sizeoffile=sizeofimage;
?? ?else
?? ??? ?sizeoffile=(sizeofimage/0x1000+1)*0x1000;???? //如果不是就增加一個文件對齊度
?? ?
??? //申請一個文件空間的內存塊
?? ?static HGLOBAL hMem=0;
?? ?hMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,sizeoffile);
?? ?if(!hMem)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I think i have enough space to get!:("),TEXT("Wrong!!!"),MB_OK | MB_ICONSTOP);
?? ??? ?return FALSE;
?? ?}
?? ?//將這個pe文件在內存中的大小全部讀到申請的塊中
?? ?
?? ?DWORD NumberOfBytesReadorWrite;
?? ?if(!ReadProcessMemory(hProcess,(LPCVOID)BaseAddress,hMem,sizeofimage,&NumberOfBytesReadorWrite))
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't read the process:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?CloseHandle(hProcess);?? //有開始就,有關閉
?? ?Sleep(200);?????????????? //等待一會
?? ?return hMem;
}
int GetSizeOfImage(HWND hDlg,DWORD IDProcess)
{
?? ?//這個函數的作用是獲取SizeOfImage的數值
?? ?//當函數執行失敗返回的是0
?? ?//成功返回的是非0
?? ?HANDLE hModuleSnap = NULL;
?? ?MODULEENTRY32 stModE? = {0};
?? ?stModE.dwSize = sizeof(MODULEENTRY32);
?? ?hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,IDProcess);? //快照,對本進程中所有的模塊進行snap
?? ?if (hModuleSnap == INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("The Module snapshot can't get!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
?? ???? return FALSE;??? //返回0
?? ?}
?? ?if (!Module32First(hModuleSnap, &stModE))
?? ?{
?? ??? MessageBox(hDlg,TEXT("The Module32First can't work!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
?? ??? CloseHandle (hModuleSnap);
?? ??? return FALSE;
?? ?}
?? ?CloseHandle (hModuleSnap);
?? ?return stModE.modBaseSize;//初始化為0
}
BOOL CheckPEFunc(HWND hDlg,HANDLE hProcess)
{
?? ?//檢查pe文件,檢查兩個標志
?? ?//如果不是pe文件那么會迫使GetSizeOfImage直接返回
?? ?//下面不是重點,所以不介紹了
?? ?int BaseAddress=0x400000;
?? ?IMAGE_DOS_HEADER DosHead;
?? ?_IMAGE_NT_HEADERS NtHead;
?? ?if(!ReadProcessMemory(hProcess,(LPCVOID)BaseAddress,&DosHead.e_magic,2,NULL))
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't read the IMAGE_DOS_SIGNATURE:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?if(DosHead.e_magic != IMAGE_DOS_SIGNATURE)
?? ?{
?? ??? ?return FALSE;
?? ?}
?? ?if(!ReadProcessMemory(hProcess,(LPCVOID)(BaseAddress+0x3c),&DosHead.e_lfanew,4,NULL))
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't read the e_lfanew:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?if(!ReadProcessMemory(hProcess,(LPCVOID)(BaseAddress+DosHead.e_lfanew),&NtHead.Signature,4,NULL))
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't read the e_lfanew:("),TEXT("oh my god.."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?if(NtHead.Signature != IMAGE_NT_SIGNATURE)
?? ?{
?? ??? ?return FALSE;
?? ?}
?? ?return TRUE;?? ?
}
BOOL CorrectSizeFunc(HWND hDlg,HWND hWindList)
{
?? ?//函數能獲取文件的PE頭部的SizeOfImage,作為正確的SizeOfImage
??? LPCTSTR File_Name=NULL;????????????????? ?
??? WPARAM tmp=(WPARAM)SendMessage(hWindList,LB_GETCURSEL,0,0);
?? ?if (tmp==LB_ERR)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("Please choose a process..."),TEXT("oh...no,no,no..."),MB_OK);
?? ??? ?return FALSE;
?? ?}
?? ?DWORD IDProcess=SendMessage(hWindList,LB_GETITEMDATA,tmp,0); //獲得此列單里面的進程ID
?? ?ID=IDProcess;//全局變量ID的作用是控制在不同的進程的切換
?? ?File_Name=GetFilePath(hDlg,IDProcess);
?? ?if(!File_Name)
?? ??? ?return FALSE;
??? //打開文件
?? ?HANDLE? hFile;
?? ?hFile=CreateFile(File_Name,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if (hFile == INVALID_HANDLE_VALUE )
?? ?{
?? ??? ?return FALSE ;
?? ?}
?? ?//創建文件映射內核對象
?? ?HANDLE hMapping;
?? ?hMapping =?? ?CreateFileMapping (hFile, NULL, PAGE_READONLY,0,0,NULL);
?? ?if (hMapping == NULL )
?? ?{
?? ??? ?CloseHandle (hFile ) ;
?? ??? ?return FALSE;
?? ?}
?? ?//創建文件視圖
?? ?LPVOID ImageBase ;
?? ?ImageBase =MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0) ;
?? ?if (ImageBase == NULL)
?? ?{
?? ??? ?CloseHandle (hMapping) ;
?? ??? ?return FALSE;
?? ?}
??? //下面的代碼就是從文件的PE頭找到SizeOfImage的
?? ?PIMAGE_DOS_HEADER DosHead = NULL ;
?? ?PIMAGE_NT_HEADERS32 pNtHeader = NULL ;
??? PIMAGE_FILE_HEADER pFileHeader = NULL ;
??? PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL ;
??? PIMAGE_SECTION_HEADER pSectionHeader = NULL ;
?? ?DosHead=(PIMAGE_DOS_HEADER)ImageBase;
?? ?pNtHeader = ( PIMAGE_NT_HEADERS32 ) ((DWORD)ImageBase + DosHead->e_lfanew ) ;
?? ?pOptionalHeader = &pNtHeader->OptionalHeader;?? ?
??? sizeofimage=(int)pOptionalHeader->SizeOfImage;
??? //找到了以后,輸出結果
?? ?char szBuffer[100];
?? ?char szMsg[]="原來的image size是:%08X\n修整的image size是:%08X";
?? ?wsprintf(szBuffer,szMsg,GetSizeOfImage(hDlg,IDProcess),sizeofimage);
?? ?MessageBox(hDlg,szBuffer,TEXT("糾正結果"),MB_OK );
??? CloseHandle (hMapping);
??? CloseHandle (hFile) ;
?? ?Sleep(200);
?? ?return TRUE;
}
LPCTSTR GetFilePath(HWND hDlg,DWORD IDProcess)
{
?? ?//此函數獲得目標進程的絕對路徑
?? ?//如果獲取失敗返回NULL
?? ?HANDLE hModuleSnap = NULL;
?? ?MODULEENTRY32 a?? = {0};
?? ?a.dwSize = sizeof(MODULEENTRY32);
?? ?hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,IDProcess);? //快照,對本進程中所有的模塊進行snap
?? ?if (hModuleSnap == INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("The Module snapshot can't get!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
?? ???? return FALSE;??? //返回0
?? ?}
?? ?if (!Module32First(hModuleSnap, &a))
?? ?{
?? ??? MessageBox(hDlg,TEXT("The Module32First can't work!"),TEXT("Error!"),MB_OK | MB_ICONSTOP);
?? ??? CloseHandle (hModuleSnap);
?? ??? return FALSE;
?? ?}
?? ?CloseHandle (hModuleSnap);
?? ?return a.szExePath;
}
BOOL ModifySectionFunc(HWND hDlg,LPCTSTR Dump_Name)
{
?? ?//此函數的將修改dump下來的exe,使其RA=RVA ,RS=RVS
?? ?//首先是打開dump文件
?? ?HANDLE hFile=CreateFile(Dump_Name,GENERIC_WRITE | GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can open the dump file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
?? ??? ?return FALSE;
?? ?}
??? //下面移動到節表前面
?? ?IMAGE_DOS_HEADER myDosHeader;
?? ?DWORD NumberOfBytesReadorWrite;?? ?
?? ?ReadFile(hFile,(LPVOID)&myDosHeader,sizeof(IMAGE_DOS_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew+sizeof(DWORD),NULL,FILE_BEGIN);
??? IMAGE_FILE_HEADER myNtHeader;
?? ?ReadFile(hFile,(LPVOID)&myNtHeader,sizeof(IMAGE_FILE_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?int nSectionCount;
?? ?nSectionCount = myNtHeader.NumberOfSections;???????????? // 保存Section個數
?? ?// 過了IMAGE_NT_HEADERS結構就是IMAGE_SECTION_HEADER結構數組了,注意是結構數組,有幾個Section該結構就有幾個元素
?? ?// 這里動態開辟NumberOfSections個內存來存儲不同的Section信息
?? ?IMAGE_SECTION_HEADER *pmySectionHeader = (IMAGE_SECTION_HEADER *)calloc(nSectionCount, sizeof(IMAGE_SECTION_HEADER));
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
??? ReadFile(hFile,(LPVOID)pmySectionHeader,sizeof(IMAGE_SECTION_HEADER)*nSectionCount,
?? ??? ????? &NumberOfBytesReadorWrite,NULL);
?? ?//移動回到節表的開始,準備寫入
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
?? ?for (int i = 0; i < nSectionCount; i++, pmySectionHeader++)
?? ?{
?? ??? ?//將RA=RVA ,RS=RVS
?? ??? ?pmySectionHeader->SizeOfRawData=pmySectionHeader->Misc.VirtualSize;
?? ??? ?pmySectionHeader->PointerToRawData=pmySectionHeader->VirtualAddress;
?? ??? ?//將修改好的數值寫回
?? ??? ?WriteFile(hFile,(LPVOID)pmySectionHeader,sizeof(IMAGE_SECTION_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?}
?? ?// 恢復指針
?? ?pmySectionHeader -=nSectionCount;
?? ?if (pmySectionHeader != NULL)????????? // 釋放內存
?? ?{
?? ?? free(pmySectionHeader);
?? ?? pmySectionHeader = NULL;
?? ?}
?? ?// 最后不要忘記關閉文件
?? ?CloseHandle(hFile);
?? ?return TRUE;
}
BOOL CopyThePEHead(HWND hDlg,LPCTSTR Dump_Name)
{
??? //此函數的作用是將原來PE文件的PE頭部完整的copy到dump文件中
?? ?HANDLE hFile=CreateFile(GetFilePath(hDlg,ID),GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can open the object file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
?? ??? ?return FALSE;
?? ?}
??? //下面移動到節表前面
?? ?IMAGE_DOS_HEADER myDosHeader;
?? ?DWORD NumberOfBytesReadorWrite;?? ?
?? ?ReadFile(hFile,(LPVOID)&myDosHeader,sizeof(IMAGE_DOS_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew+sizeof(DWORD),NULL,FILE_BEGIN);
??? IMAGE_FILE_HEADER myNtHeader;
?? ?ReadFile(hFile,(LPVOID)&myNtHeader,sizeof(IMAGE_FILE_HEADER),&NumberOfBytesReadorWrite,NULL);
?? ?IMAGE_SECTION_HEADER mySectionHeader;
?? ?SetFilePointer(hFile,myDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS),NULL,FILE_BEGIN);
??? ReadFile(hFile,(LPVOID)&mySectionHeader,sizeof(IMAGE_SECTION_HEADER),&NumberOfBytesReadorWrite,NULL);
??? SetFilePointer(hFile,NULL,NULL,FILE_BEGIN);
?? ?HGLOBAL hMem=0;
?? ?//讀出節表的第一個文件位置,以確PE頭的大小
?? ?//申請同樣大小的空間
?? ?hMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,mySectionHeader.PointerToRawData);
?? ?if(!hMem)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can't get the Memory space!"),TEXT("Error!!!"),MB_OK | MB_ICONSTOP);
?? ??? ?return FALSE;
?? ?}
?? ?//將文件中的PE頭部讀取到申請的空間中
?? ?ReadFile(hFile,hMem,mySectionHeader.PointerToRawData,&NumberOfBytesReadorWrite,NULL);
?? ?CloseHandle(hFile);
?? ?//上面是讀///
??? //下面是寫///
?? ?hFile=CreateFile(Dump_Name,GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==INVALID_HANDLE_VALUE)
?? ?{
?? ??? ?MessageBox(hDlg,TEXT("I can open the dump file..."),TEXT("Error!!"),MB_OK | MB_ICONWARNING);
?? ??? ?return FALSE;
?? ?}
?? ?//下面是將空間中的數據寫到dump文件的頭部
?? ?WriteFile(hFile,hMem,mySectionHeader.PointerToRawData,&NumberOfBytesReadorWrite,NULL);
??? CloseHandle(hFile);
?? ?GlobalFree(hMem);
?? ?return TRUE;
}
?
這里:
1. 獲取當前進程列表使用的是:CreateToolhelp32Snapshot,Process32First,?Process32Next 這三個函數,即創建一個進程快照,然后遍歷它,之前做ring3的進程注入的時候用的也是這個技術,原理不是很難理解,本質上就是一個鏈表的操作。
2. 在進程中確定進程的大小imagesize 使用的是從磁盤的文件PE頭中讀取SizeOfImage這個字段來獲取。這里就有一個很有趣的知識點了,我們先從原始的方法開始講。
2.1 用ReadProcessMemory函數從從當前內存的進程映像工具PE格式的偏移獲取到SizeOfImage的值的,這種方法不太可靠,要是加殼程序修改了映像中的PE頭信息,就要出錯了。
2.2 LordPE使用的方法,采用了對Module32Next來獲取dump的進程的基本信息的。
BOOL?WINAPI?Module32First(?
HANDLE?hSnapshot,?//這是先前的CreateToolhelp32Snapshot函數返回的快照
??LPMODULEENTRY32?lpme?//這個是指向MODULEENTRY32結構的指針
);
下面是MUDULEENTRY32結構:
typedef?struct?tagMODULEENTRY32?{?
??DWORD?dwSize;?
??DWORD?th32ModuleID;?
??DWORD?th32ProcessID;?
??DWORD?GlblcntUsage;?
??DWORD?ProccntUsage;?
??BYTE?*modBaseAddr;?
??DWORD?modBaseSize;????//這個是是我們要獲取的關鍵?
??HMODULE?hModule;?
??TCHAR?szModule[MAX_PATH];?
??TCHAR?szExePath[MAX_PATH];?
??DWORD?dwFlags;
}?MODULEENTRY32,?*PMODULEENTRY32,?*LPMODULEENTRY32;
這種方法會受到anti-dump技術的影響。
因為MUDULEENTRY32是從內核中PEB(進程環境塊)獲取的數據。
下面是PEB的結構。
struct???_PEB?(sizeof=488)?
+000?byte?????InheritedAddressSpace?
+001?byte?????ReadImageFileExecOptions?
+002?byte?????BeingDebugged?
+003?byte?????SpareBool?
+004?void?????*Mutant?
+008?void?????*ImageBaseAddress?
+00c?struct???_PEB_LDR_DATA?*Ldr?
+010?struct???_RTL_USER_PROCESS_PARAMETERS?*ProcessParameters?
+014?void?????*SubSystemData?
+018?void?????*ProcessHeap?
+01c?void?????*FastPebLock?
+020?void?????*FastPebLockRoutine?
+024?void?????*FastPebUnlockRoutine?
+028?uint32???EnvironmentUpdateCount?
+02c?void?????*KernelCallbackTable?
+030?uint32???SystemReserved[2]?
+038?struct???_PEB_FREE_BLOCK?*FreeList?
+03c?uint32???TlsExpansionCounter?
+040?void?????*TlsBitmap?
+044?uint32???TlsBitmapBits[2]?
+04c?void?????*ReadOnlySharedMemoryBase?
+050?void?????*ReadOnlySharedMemoryHeap?
+054?void?????**ReadOnlyStaticServerData?
+058?void?????*AnsiCodePageData?
+05c?void?????*OemCodePageData?
+060?void?????*UnicodeCaseTableData
+064?uint32???NumberOfProcessors?
+068?uint32???NtGlobalFlag?
+070?union????_LARGE_INTEGER?CriticalSectionTimeout?
+070????uint32???LowPart?
+074????int32????HighPart?
+070????struct???__unnamed3?u?
+070???????uint32???LowPart?
+074???????int32????HighPart?
+070????int64????QuadPart?
+078?uint32???HeapSegmentReserve?
+07c?uint32???HeapSegmentCommit?
+080?uint32???HeapDeCommitTotalFreeThreshold?
+084?uint32???HeapDeCommitFreeBlockThreshold?
+088?uint32???NumberOfHeaps?
+08c?uint32???MaximumNumberOfHeaps?
+090?void?????**ProcessHeaps?
+094?void?????*GdiSharedHandleTable?
+098?void?????*ProcessStarterHelper?
+09c?uint32???GdiDCAttributeList?
+0a0?void?????*LoaderLock?
+0a4?uint32???OSMajorVersion?
+0a8?uint32???OSMinorVersion?
+0ac?uint16???OSBuildNumber?
+0ae?uint16???OSCSDVersion?
+0b0?uint32???OSPlatformId?
+0b4?uint32???ImageSubsystem?
+0b8?uint32???ImageSubsystemMajorVersion?
+0bc?uint32???ImageSubsystemMinorVersion?
+0c0?uint32???ImageProcessAffinityMask?
+0c4?uint32???GdiHandleBuffer[34]?
+14c?function?*PostProcessInitRoutine?
+150?void?????*TlsExpansionBitmap?
+154?uint32???TlsExpansionBitmapBits[32]?
+1d4?uint32???SessionId?
+1d8?void?????*AppCompatInfo?
+1dc?struct???_UNICODE_STRING?CSDVersion?
+1dc????uint16???Length?
+1de????uint16???MaximumLength?
+1e0????uint16???*Buffer
我們從FS:[30]就可以獲得這個PEB的首地址。然后在0C處的_PEB_LDR_DATA?*Ldr是一個關鍵通過它,我們能訪問到
typedef?struct?_PEB_LDR_DATA?{
ULONG?Length;
BOOLEAN?Initialized;
PVOID?SsHandle;
LIST_ENTRY?InLoadOrderModuleList;
LIST_ENTRY?InMemoryOrderModuleList;
LIST_ENTRY?InInitializationOrderModuleList;
}?PEB_LDR_DATA,?*PPEB_LDR_DATA;
該結構的后三個成員是指向LDR_MODULE鏈表結構中相應三條雙向鏈表頭的指針,分別是按照加載順序、在內存中的地址順序和初始化順序排列的模塊信息結構的指針。于是通過它,我們能訪問到_LDR_MODULE結構,而這里面包括了本進程的SizeOfImage。
_LDR_MODULE結構如下:
typedef?struct?_LDR_MODULE?{
LIST_ENTRY?InLoadOrderModuleList;
LIST_ENTRY?InMemoryOrderModuleList;
LIST_ENTRY?InInitializationOrderModuleList;
PVOID?BaseAddress;
PVOID?EntryPoint;
ULONG?SizeOfImage;???????????????????//進程的image?size
UNICODE_STRING?FullDllName;
UNICODE_STRING?BaseDllName;
ULONG?Flags;
SHORT?LoadCount;
SHORT?TlsIndex;
LIST_ENTRY?HashTableEntry;
ULONG?TimeDateStamp;
}?LDR_MODULE,?*PLDR_MODULE;
所以,我們得到關鍵的代碼就是:
??//這里的幾個代碼是修改PEB的關鍵
??__asm
??{
????mov?eax,fs:[30h]????????????????//獲得PEB地址
????mov?eax,[eax+0ch]???????????????//?+00c?struct???_PEB_LDR_DATA?*Ldr?
????mov?eax,[eax+0ch]???????????????//?_LDR_MODULE的首地址
????mov?dword?ptr?[eax+20h],1000h???//eax+20是保存image?size的地方
??}
上面的代碼的作用就是把image?size的大小改為了1000h,這樣我們用MODULEENTRY32得到的大小是不準確的。
?
2.3 針對上面的問題,必須使用LordPE的corect image size 技術了,它的原理很簡單,就是從磁盤PE文件頭中讀取真實的image?size來修正獲取到的數據,從而避免了anti-dump的影響,當然,anti-dump技術還有很多,不止這一種,我也要慢慢去摸索。
?
3. 對齊節表問題
學過PE結構的朋友都指導,FileAlignment(磁盤對齊值)和SectionAlignment(內存對齊值)是不一樣的。
FileAlignment:0x200h
SectionAlignment:0x1000h
在PE加載器加載文件映像的時候,就會在對齊值之間的差值中填0,當然我們dump的時候連著這些0也一并dump下來了。這也就是為什么脫殼的程序普遍都比源程序大很多的原理(當然實際情況不止這些,IAT重建也會造成大小擴大)。
另外,還造成了一個問題,就是 RA!=RVA?,RS!=RVS的問題:
因為我們是從內從中直接dump出來的數據,所以這時正常的情況應該是RA=RVA?,RS=RVS,這里可以手動修改,或者編程實現即可,原理上之前學PE的時候寫的PEInfo差不多,主要考察的PE結構的知識。
這是修改后的。
到這里,dump就完成了,還差IAT沒修復。
可以采取手工的方法在文件中找一塊空位,逆向的重構IAT,在修改PE頭。
或者用ImportREC來自動修復。
?
修復完成后,脫殼成功,至此,一個簡單的脫殼工具就出來了。
?
這個學習筆記就當這段時間學習逆向脫殼的總結了,留下了幾個問題沒解決:
1. DLL脫殼后的重定位問題
2. VMP虛擬機殼尋找OEP問題。
留待以后解決了.............
信安的路果然還很長,希望以后能繼續多多學習,思考,總結,分享,向看雪上那些大神看齊。
下一階段準備研究一些緩沖區溢出的內核的知識點,繼續看《0DAY》。離ISCC結束還有10天,繼續加油啦,暑假一定要到北京ISCC決賽去。
?
?
-------------------------------分割線-----------------------
不知道怎么結尾,就這樣了吧,? 跑步去。