如何隱藏導入表

當有人問我這個問題的時候,我真的是一臉懵逼,我只知道編譯的時候儘量少的引用dll可以減少導入表的量。

後來嘗試一個正常的程序,將PE結構的導入表大小和位置全部設置爲0:
該程序只負責調用MessageBox(實際是MessaheBoxW,或者MessageBoxA,但是大家都懂,不作區分):
這裏寫圖片描述

正常的導入表

將導入表RVA和大小設置爲0

然後運行,炸掉了,很正常:
這裏寫圖片描述

看來不會是一個特別簡單的問題。

經過研究,是要通過PEB來自己找了。首先看這個程序依賴的DLL:
這裏寫圖片描述

由於MessageBox是User32.dll導出的,所以,會依賴User32.dll,爲了儘量減少DLL依賴,因此重新寫一段代碼:
重新寫的代碼
這段代碼,設置了入口點,並將編譯器優化關掉。

編譯後重新看依賴DLL:
這裏寫圖片描述
這裏寫圖片描述

好了至此導入表沒了,那我們怎麼調用其它的函數呢?
使用OD加載後,查看可執行模塊:
這裏寫圖片描述

發現默認就有幾個dll。這時候就有一個關鍵的函數LdrLoadDll,該函數聲明如下:

NTSTATUS NTAPI LdrLoadDll(
    IN PWCHAR               PathToFile OPTIONAL,
    IN ULONG                Flags OPTIONAL,
    IN PUNICODE_STRING      ModuleFileName,
    OUT PHANDLE             ModuleHandle);

這個函數和LoadLibrary是一樣的效果。這個函數在ntdll.dll中導出:
這裏寫圖片描述

我們能看到,代碼就算不依賴任何DLL,程序加載後,還是會有幾個DLL會一同加載,而ntdll.dll也在其中;因此思路就有了,只需要程序運行後,找到ntdll.dll然後就能找到LdrLoadDll了。^-^

接下來是找ntdll.dll

運行在應用層的程序,通過fs寄存器可以拿到很多TEB中的數據,其中fs:[0x30]是PEB的地址,在PEB地址偏移處0xC的位置,是一個PEB_LDR_DATA結構的地址,PEB_LDR_DATA結構有幾個LIST_ENTRY類型的變量,LIST_ENTRY是一個雙向鏈表,這個鏈表的每一項,都有加載模塊的相關信息,每一項都指向LDR_DATA_TABLE_ENTRY結構。在LDR_DATA_TABLE_ENTRY結構中能看到幾個有用的信息:

UNICODE_STRING FullDllName;
PVOID DllBase;

FullDllName代表dll的名字,而DllBase就代表dll的加載地址。
由此就能找到我們需要的dll了。

總結一下流程:

Created with Raphaël 2.1.0fs:[0x30]拿到PEB地址PEB偏移0xC位置拿到PEB_LDR_DATA地址PEB_LDR_DATA拿到鏈表首地址通過鏈表拿到LDR_DATA_TABLE_ENTRY地址判斷DLL名字,並獲取DLL加載地址

我在這裏貼上部分相關結構體的定義,你們會發現我貼上來的和MSDN上的不一樣,因爲這些結構體是我研究的時候從帖子上複製下來的,寫博客的時候發現那帖子不見了。。。

typedef struct _PEB_LDR_DATA {
    ULONG Length;
    BOOL Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef struct _LSA_UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;


typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union
    {
        LIST_ENTRY HashLinks;
        struct
        {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union
    {
        ULONG TimeDateStamp;
        PVOID LoadedImports;
    };
    PVOID EntryPointActivationContext;
    PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

我用以下代碼找到了ntdll.dll:
這裏寫圖片描述

找到ntdll.dll後,就要通過導出表來尋找函數的地址。
導出表信息在IMAGE_NT_HEADERS中IMAGE_OPTIONAL_HEADER的IMAGE_DATA_DIRECTORY類型數組的第一個元素中。
然後只要遍歷找到LdrLoadDll的地址,這個地址就是內存中這個函數的地址。

知道了如何加載沒有的DLL,知道如何去DLL裏面找需要函數的地址,就可以不需要導入表了。

代碼編譯完成後,在Win7 x86的環境下能跑起來,Windows 10 x64跑不起來,因爲加載User32.dll的時候,LdrLoadDll返回0xc0000135,暫時我也沒找到其他的解決辦法。

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章