1. 程式人生 > 實用技巧 >PE學習:匯入表注入

PE學習:匯入表注入

最近深感自己基礎不牢,回頭學了一遍PE,順手做了個匯入表注入的小練習

首先準備一個DLL用來測試

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        init();
    case DLL_THREAD_ATTACH:
    
case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
void init() {
    MessageBoxA(0, "Success", "Test", MB_OK);
}

int plus(int a, int b) {
    return a + b;
}

再準備一個exe

.386
.MODEL flat,stdcall
option casemap:none

include        windows.inc
include        gdi32.inc
includelib    gdi32.lib
include        user32.inc
includelib    user32.lib
include        kernel32.inc
includelib    kernel32.lib

.data
? hInstance dd ? hWinMain dd ? .const szClassName db 'Myclass',0 szCaptionMain db 'My first window',0 szText db 'win32 Assembly,simple and powerful !',0 szButton db 'button',0 szButtonText db '&ok',0 .code _ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam local @stPs:PAINTSTRUCT local @stRect:RECT local @hDc mov eax,uMsg .
if eax == WM_PAINT invoke BeginPaint,hWnd,addr @stPs mov @hDc,eax invoke GetClientRect,hWnd,addr @stRect invoke DrawText,@hDc,addr szText,-1,addr @stRect,DT_SINGLELINE or DT_CENTER or DT_VCENTER invoke EndPaint,hWnd,addr @stPs .elseif eax == WM_CLOSE invoke DestroyWindow,hWinMain invoke PostQuitMessage,NULL .else invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .endif xor eax,eax ret _ProcWinMain endp _WinMain proc local @stWndClass:WNDCLASSEX local @stMsg:MSG invoke GetModuleHandle,NULL mov hInstance,eax invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass invoke LoadCursor,0,IDC_ARROW mov @stWndClass.hCursor,eax push hInstance pop @stWndClass.hInstance mov @stWndClass.cbSize,sizeof WNDCLASSEX mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW mov @stWndClass.lpfnWndProc,offset _ProcWinMain mov @stWndClass.hbrBackground,COLOR_WINDOW + 1 mov @stWndClass.lpszClassName,offset szClassName invoke RegisterClassEx,addr @stWndClass invoke CreateWindowEx,WS_EX_CLIENTEDGE,offset szClassName,offset szCaptionMain,\ WS_OVERLAPPEDWINDOW,100,100,600,400,NULL,NULL,hInstance,NULL mov hWinMain,eax invoke ShowWindow,hWinMain,SW_SHOWNORMAL invoke UpdateWindow,hWinMain .while TRUE invoke GetMessage,addr @stMsg,NULL,0,0 .break .if eax == 0 invoke TranslateMessage,addr @stMsg invoke DispatchMessage,addr @stMsg .endw ret _WinMain endp start: call _WinMain invoke ExitProcess,NULL end start

執行效果

要完成匯入表注入,需要以下幾步

1    開啟一個exe
2    新增一個節
3    把匯入表移進去
4    寫入自己的匯入表
5    存檔

關於匯入表的資料網上有很多我就不細說了,這裡主要就放一下程式碼,這個程式碼我寫的很隨意,看著應該比較醜hhhhh

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

FILE* fp = fopen(fileName, "rb");
    if (fp == NULL) {
        printf("open failed\n");
        exit(0);
    }
    fseek(fp, 0, SEEK_END);
    unsigned int len = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    char* buf = (char*)malloc(2 * len);//因為exe要擴充,所以這裡直接擴了一倍,簡單暴力hhh
    if (buf != NULL) {
        memset(buf, '\x00', 2 * len);
        fread(buf, len, 1, fp);
    }
    fclose(fp);
    char* lpnewSec = buf + newSection(buf);//指向新增的節
    inject(buf, lpnewSec, fileName);
    free(buf);
    buf = NULL;

新增節:

unsigned __int32 newSection(char* buf) {
    unsigned __int32 e_lfanew = *(unsigned __int32*)(buf + 0x3c);
    char* FIheader = buf + e_lfanew + 4;
    char* OPheader = FIheader+0x14;
//擴大地址,我直接加了一個檔案對齊的大小 unsigned __int32 SectionAlignment
= *(unsigned __int32*)(OPheader + 0x20); unsigned __int32 SizeOfImage = *(unsigned __int32*)(OPheader + 0x38); *(unsigned __int32*)(buf + 0x38 + (e_lfanew + 0x18)) = SizeOfImage + SectionAlignment; SizeOfImage = *(unsigned __int32*)(OPheader + 0x38); unsigned __int32 SizeoOfHeaders = *(unsigned __int32*)(OPheader + 0x3c); unsigned __int16 NumberOfSections = *(unsigned __int16*)(FIheader + 0x2); __int16 SizeOfOptionalHeader = *(unsigned __int16*)(FIheader + 0x10); char* Sections_addr = OPheader + SizeOfOptionalHeader;
//遍歷節表 unsigned __int32 Misc
= 0; unsigned __int32 VirtualAddress = 0; unsigned __int32 SizeOfRawData = 0; unsigned __int32 PointerToRawData = 0; unsigned __int32 Characteristics = 0; for (int j = 0; j < NumberOfSections; j++) { Misc = *(unsigned __int32*)(Sections_addr + 0x8); VirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc); SizeOfRawData = *(unsigned __int32*)(Sections_addr + 0x10); PointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14); Characteristics |= *(unsigned __int32*)(Sections_addr + 0x24); Sections_addr = Sections_addr + 0x28; } unsigned int check = SizeoOfHeaders - (Sections_addr - buf); //當節表與第一個節的距離小於0x50時不能加節表,可以通過合併節等方式處理,這裡沒管
  
if (check < 0x50) { printf("can't add new Section"); exit(0); } *(unsigned __int16*)(FIheader + 0x2) += 1;//節表長度加一
//加入新節表 unsigned __int32 newSec_addr
= VirtualAddress + SectionAlignment; unsigned __int32 newSec_PointerToRawData = PointerToRawData + SizeOfRawData; *(unsigned __int64*)Sections_addr = 0x000000006362612e;//節名我直接取abc了 *(unsigned __int32*)(Sections_addr + 0x8) = SectionAlignment; *(unsigned __int32*)(Sections_addr + 0xc) = newSec_addr; *(unsigned __int32*)(Sections_addr + 0x10) = SectionAlignment; *(unsigned __int32*)(Sections_addr + 0x14) = newSec_PointerToRawData; *(unsigned __int32*)(Sections_addr + 0x24) = Characteristics; return newSec_PointerToRawData; }

注入

void inject(char* buf, char* newSec,const char* fileName) {
    unsigned __int32 e_lfanew = *(unsigned __int32*)(buf + 0x3c);
    char* FIheader = buf + e_lfanew + 4;
    char* OPheader = FIheader + 0x14;
    unsigned __int16 NumberOfSections = *(unsigned __int16*)(FIheader + 0x2);
    __int16 SizeOfOptionalHeader = *(unsigned __int16*)(FIheader + 0x10);

    char* Sections_addr = OPheader + SizeOfOptionalHeader;
    unsigned __int32 VirtualAddress = 0;
    unsigned __int32 SizeOfRawData = 0;
    unsigned __int32 PointerToRawData = 0;
    for (int j = 0; j < NumberOfSections; j++) {
        VirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc);
        SizeOfRawData = *(unsigned __int32*)(Sections_addr + 0x10);
        PointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14);
        Sections_addr = Sections_addr + 0x28;
    }

    char* lpDirectory = OPheader + SizeOfOptionalHeader - 0x80;
    unsigned __int32 ImportRVA = *(unsigned __int32*)(lpDirectory + 0x8);
    *(unsigned __int32*)(lpDirectory + 0x8) = VirtualAddress;
    unsigned __int32 ImportFOA = RVA_to_FOA(buf, ImportRVA);
    char* importAddr = buf + ImportFOA;
    char* newImport = newSec;
    unsigned __int32 count = 0;
//移動匯入表,沒有動INT和IAT
    while (*(unsigned __int32*)importAddr || *(unsigned __int32*)(importAddr + 0x4) || *(unsigned __int32*)(importAddr + 0x8) || *(unsigned __int32*)(importAddr + 0xc) || *(unsigned __int32*)(importAddr + 0x4)) {
        for (unsigned __int32 i = 0; i < 0x14; i++) {
            *(newImport + i) = *(importAddr + i);
        }
        count += 0x14;
        newImport += 0x14;
        importAddr += 0x14;
    }
    //new import
    *(unsigned __int32*)(newImport + 0x0) = VirtualAddress + count + 0x14;//指向新的INT
    *(unsigned __int32*)(newImport + 0x4) = 0;
    *(unsigned __int32*)(newImport + 0x8) = 0;
    *(unsigned __int32*)(newImport + 0xc) = VirtualAddress + count + 0x2b;//指向注入的DLL名
    *(unsigned __int32*)(newImport + 0x10) = VirtualAddress + count + 0x1c;//指向IAT
    //INT
    *(unsigned __int32*)(newImport + 0x14) = VirtualAddress + count + 0x24;//指向匯入函式名,下面IAT也指向此處
    *(unsigned __int32*)(newImport + 0x18) = 0;
    //IAT
    *(unsigned __int32*)(newImport + 0x1c) = VirtualAddress + count + 0x24;
    *(unsigned __int32*)(newImport + 0x20) = 0;
//這裡必須要有匯入的函式,否則不會載入dll
*(unsigned __int16*)(newImport + 0x24) = 0; *(unsigned __int32*)(newImport + 0x26) = 0x73756c70;//plus *(newImport + 0x2a) = (char)0; //dLL name:testDLL.dll *(unsigned __int32*)(newImport + 0x2b) = 0x74736574; *(unsigned __int32*)(newImport + 0x2f) = 0x2e6c6c44; *(unsigned __int32*)(newImport + 0x33) = 0x006c6c64; FILE* fp = fopen(fileName, "wb"); if (fp == NULL) { printf("open failed\n"); exit(0); } fwrite(buf, SizeOfRawData + PointerToRawData, 1, fp); fclose(fp); }

RVA轉為FOA

unsigned __int32 RVA_to_FOA(char* buf, unsigned __int32 RVA) {
    unsigned __int32 e_lfanew = *(unsigned __int32*)(buf + 0x3c);
    unsigned __int32 SizeoOfHeaders = *(unsigned __int32*)(buf + 0x3c + (e_lfanew + 0x18));
    unsigned __int32 FOA = 0;
//在檔案頭中不拉伸
if (RVA <= SizeoOfHeaders) { FOA = RVA; return FOA; } unsigned __int16 NumberOfSections = *(unsigned __int16*)(buf + e_lfanew + 0x4 + 0x2); __int16 SizeOfOptionalHeader = *(unsigned __int16*)(buf + e_lfanew + 0x4 + 0x10); char* Sections_addr = buf + e_lfanew + 0x18 + SizeOfOptionalHeader; //遍歷節表判斷在那個節中,FOA=RVA-節的RVA+節的FOA unsigned __int32 VirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc); unsigned __int32 PointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14); unsigned __int32 PrevVirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc); unsigned __int32 PrevPointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14); Sections_addr = Sections_addr + 0x28; for (int j = 0; j < NumberOfSections - 1; j++) { VirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc); PointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14); if (PrevVirtualAddress <= RVA && VirtualAddress > RVA) { FOA = PrevPointerToRawData + (RVA - PrevVirtualAddress); return FOA; } PrevVirtualAddress = VirtualAddress; PrevPointerToRawData = PointerToRawData; Sections_addr = Sections_addr + 0x28; } FOA = PrevPointerToRawData + (RVA - PrevVirtualAddress); return FOA; }

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

注入後

執行