PE文件和COFF文件格式分析——导出表的应用——通过导出表隐性加载DLL
? ? ? ? 通過導出表隱性加載DLL?導出表?加載DLL?還隱性?是的。如果覺得不可思議,可以先看《PE文件和COFF文件格式分析——導出表》中關于“導出地址表”的詳細介紹。(轉載請指明出于breaksoftware的csdn博客)
? ? ? ? 這兒再廢話幾句,導出地址表,可能保存兩種信息:
? ? ? ? 1 保存的函數入口的RVA
? ? ? ? 2 保存的是指向函數真正實現的所在的DLL名和函數名字符串組合(NTDLL.RtlAddVectoredExceptionHandler)
? ? ? ? 一般情況下,我們遇到的是1這種情況。這種場景沒什么好說的。我也想不到這個有什么好利用的,那么2又如何可以被利用呢?
? ? ? ? Exe調用一個DLL中的方法,有兩種方法:
? ? ? ? 1 在Exe導入表中加入DLL中函數信息,例如我們程序中調用GetProcAddress這類的API就是因為我們程序默認的導入表中包含了Kernel32.dll導出函數GetProcAddress信息。
? ? ? ? 2 在邏輯中通過LoadLibrary動態載入一個DLL,然后通過GetProcAddress獲取函數地址。這樣我們在Exe的導入表中是看不到這個DLL的信息的。
? ? ? ? 那么我是怎么設計”通過導出表隱性加載DLL“方案的呢?
? ? ? ? 我設計了三個文件:DllBase.dll是我們要隱性加載的DLL;DllTop.dll是我們將要修改的DLL文件,MainExe.exe直接加載這個DLL,從而實現隱性加載DllBase.dll并調用它的導出函數。
? ? ? ? DllBase導出兩個函數
LIBRARY "DllBase"
EXPORTSRet1Ret2
? ? ? ? 這兩個函數的實現更簡單
int Ret1() {return 1;
}int Ret2() {return 2;
}? ? ? ??
? ? ? ? DllTop導出三個函數
LIBRARY "DllTop"
EXPORTSRet0Occupying001Occupying002? ? ? ? 其實現是
int Ret0(){return 0;
};void Occupying001(){return;
}void Occupying002(){return;
}?
? ? ?
? ? ? ? ?注意Occupying001和Occupying002這兩個函數,他們的返回類型是void。
? ? ? ? ?注意上圖和代碼,可以發現DllTop.dll和DllBase.dll不存在任何關系。
? ? ? ? ?現在我們要對DllTop.dll文件動手術,我會分別將Occupying001和Occupying002的導出地址指向DllBase.dll中的Ret1和Ret2。我保留TopDll.dll的一個副本為DllTop_Real.dll。具體的修改請看下圖
? ? ? ? 修改后,我們將其命名為TopDll_Modify.dll。
? ? ? ? 新修改的DLL文件,我們用View Denpendencies查看下
? ? ? ? 可以看到修改后的DLL并沒有使DllBase.dll出現在導入表中,我們還是好好的隱藏著。
? ? ? ? 為了做出結果比較,我將在MainExe中分別對DllTop_Real.dll和DllTop_Modify.dll進行加載并調用其函數。
typedef int (WINAPI *PRetN)();void TopDll( LPCSTR lpFileName ){PRetN pRet0 = NULL;PRetN pRet1 = NULL;PRetN pRet2 = NULL;HMODULE hDllTop = NULL;hDllTop = LoadLibraryA( lpFileName );if ( NULL != hDllTop ) {pRet0 = (PRetN)GetProcAddress( hDllTop, "Ret0" );if ( NULL != pRet0 ) {printf("DllTop Ret0:%d\n", pRet0());}pRet1 = (PRetN)GetProcAddress( hDllTop, "Occupying001" );if ( NULL != pRet1 ) {printf("DllTop Occupying001:%d\n", pRet1());}pRet2 = (PRetN)GetProcAddress( hDllTop, "Occupying002" );if ( NULL != pRet2 ) {printf("DllTop Occupying002:%d\n", pRet2());}FreeLibrary( hDllTop);hDllTop = NULL;}
}void BaseDll( LPCSTR lpFileName ) {PRetN pRet0 = NULL;PRetN pRet1 = NULL;PRetN pRet2 = NULL;HMODULE hDllBase = NULL;hDllBase = LoadLibraryA( lpFileName );if ( NULL != hDllBase ) {pRet1 = (PRetN)GetProcAddress( hDllBase, "Ret1" );if ( NULL != pRet1 ) {printf("DllBase Ret1:%d\n", pRet1());}pRet2 = (PRetN)GetProcAddress( hDllBase, "Ret2" );if ( NULL != pRet2 ) {printf("DllBase Ret2:%d\n", pRet2());}FreeLibrary( hDllBase);hDllBase = NULL;}
}int _tmain(int argc, _TCHAR* argv[])
{TopDll( "DllTop_Real.dll" );TopDll( "DllTop_Modify.dll" );BaseDll( "DllBase.dll" );system("pause");return 0;
}
? ? ? ? 其運行結果是
? ? ? ? 看!我們調用DllTop_Real.dll中函數時,由于Occupying001和Occupying002都是無返回值的,所以返回的結果是亂亂的數據。?我們調用DllTop_Modify.dll中函數時,Occupying001和Occupying002分別返回了1和2,這兩個結果是DllBase.dll中Ret1和Ret2的執行結果!這樣我們就是實現了通過導出表隱性加載DLL的方法。是不是很有意思?附上工程地址
? ? ? ? 最后我們看下3個DLL在內存中存在的情況
1
2
3
總結
以上是生活随笔為你收集整理的PE文件和COFF文件格式分析——导出表的应用——通过导出表隐性加载DLL的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PE文件和COFF文件格式分析——导出表
- 下一篇: DllMain中不当操作导致死锁问题的分