1. 程式人生 > >相容32位和64位的劫持DLL方案

相容32位和64位的劫持DLL方案

編寫一個劫持DLL並不難,無非就是模擬原DLL匯出其所有的函式,在假的函式內call/jmp到真的函式即可。現在的問題就在於DLL內是用call還是jmp?

如果用call的話,你得需要知道每一個函式的引數以及呼叫方式,這樣程式碼量會比較多,最重要的是微軟有很多未公開的函式,這就更麻煩了。。。這個方案唯一的好處是同時支援x86和x64。

如果用jmp的話,好處就是不用理會原函式的呼叫方式以及引數,相對於使用call的方案這是非常省心的一件事。壞處就是得使用內聯彙編,因為為了讓原引數順利達到真正的函式,中間函式不能有任何多餘的彙編程式碼,所以就需要這樣寫

void __declspec(naked) Foo()
{
	__asm jmp OldFun
}
可是,vc編譯器並不支援64位的內聯彙編(如果支援的話就不會有此文了)。網上搜了一圈,比較簡單的解決方案就是換成Intel的編譯器,因為他家的編譯器支援x64內聯彙編,但這個東西似乎是收費的,而且Windows平臺最好還是用微軟自家的編譯器最好,所以這個方案可以否定了。


另一個方案也就是最終方案,在工程內引入asm檔案(https://www.cnblogs.com/achillis/p/5369658.html),把彙編寫到這裡而不是使用內聯彙編,這樣就能使用jmp的方式劫持並同時支援32位和64位了。提醒一下,x64用ml64.exe,而x86用ml.exe。

比如我劫持的version.dll,x86.asm檔案內容如下

.MODEL flat,stdcall

extern g_pfnGetFileVersionInfoA: DWORD
extern g_pfnGetFileVersionInfoByHandle: DWORD
extern g_pfnGetFileVersionInfoExW: DWORD
extern g_pfnGetFileVersionInfoSizeA: DWORD
extern g_pfnGetFileVersionInfoSizeExW: DWORD
extern g_pfnGetFileVersionInfoSizeW: DWORD
extern g_pfnGetFileVersionInfoW: DWORD
extern g_pfnVerFindFileA: DWORD
extern g_pfnVerFindFileW: DWORD
extern g_pfnVerInstallFileA: DWORD
extern g_pfnVerInstallFileW: DWORD
extern g_pfnVerLanguageNameA: DWORD
extern g_pfnVerLanguageNameW: DWORD
extern g_pfnVerQueryValueA: DWORD
extern g_pfnVerQueryValueW: DWORD

.code

FakeGetFileVersionInfoA proc
  jmp g_pfnGetFileVersionInfoA
FakeGetFileVersionInfoA endp

FakeGetFileVersionInfoByHandle proc
  jmp g_pfnGetFileVersionInfoByHandle
FakeGetFileVersionInfoByHandle endp

FakeGetFileVersionInfoExW proc
  jmp g_pfnGetFileVersionInfoExW
FakeGetFileVersionInfoExW endp

FakeGetFileVersionInfoSizeA proc
  jmp g_pfnGetFileVersionInfoSizeA
FakeGetFileVersionInfoSizeA endp

FakeGetFileVersionInfoSizeExW proc
  jmp g_pfnGetFileVersionInfoSizeExW
FakeGetFileVersionInfoSizeExW endp

FakeGetFileVersionInfoSizeW proc
  jmp g_pfnGetFileVersionInfoSizeW
FakeGetFileVersionInfoSizeW endp

FakeGetFileVersionInfoW proc
  jmp g_pfnGetFileVersionInfoW
FakeGetFileVersionInfoW endp

FakeVerFindFileA proc
  jmp g_pfnVerFindFileA
FakeVerFindFileA endp

FakeVerFindFileW proc
  jmp g_pfnVerFindFileW
FakeVerFindFileW endp

FakeVerInstallFileA proc
  jmp g_pfnVerInstallFileA
FakeVerInstallFileA endp

FakeVerInstallFileW proc
  jmp g_pfnVerInstallFileW
FakeVerInstallFileW endp

FakeVerLanguageNameA proc
  jmp g_pfnVerLanguageNameA
FakeVerLanguageNameA endp

FakeVerLanguageNameW proc
  jmp g_pfnVerLanguageNameW
FakeVerLanguageNameW endp

FakeVerQueryValueA proc
  jmp g_pfnVerQueryValueA
FakeVerQueryValueA endp

FakeVerQueryValueW proc
  jmp g_pfnVerQueryValueW
FakeVerQueryValueW endp

end
x64.asm也基本相同
extern g_pfnGetFileVersionInfoA: DQ
extern g_pfnGetFileVersionInfoByHandle: DQ
extern g_pfnGetFileVersionInfoExW: DQ
extern g_pfnGetFileVersionInfoSizeA: DQ
extern g_pfnGetFileVersionInfoSizeExW: DQ
extern g_pfnGetFileVersionInfoSizeW: DQ
extern g_pfnGetFileVersionInfoW: DQ
extern g_pfnVerFindFileA: DQ
extern g_pfnVerFindFileW: DQ
extern g_pfnVerInstallFileA: DQ
extern g_pfnVerInstallFileW: DQ
extern g_pfnVerLanguageNameA: DQ
extern g_pfnVerLanguageNameW: DQ
extern g_pfnVerQueryValueA: DQ
extern g_pfnVerQueryValueW: DQ

.code

FakeGetFileVersionInfoA proc
  jmp g_pfnGetFileVersionInfoA
FakeGetFileVersionInfoA endp

FakeGetFileVersionInfoByHandle proc
  jmp g_pfnGetFileVersionInfoByHandle
FakeGetFileVersionInfoByHandle endp

FakeGetFileVersionInfoExW proc
  jmp g_pfnGetFileVersionInfoExW
FakeGetFileVersionInfoExW endp

FakeGetFileVersionInfoSizeA proc
  jmp g_pfnGetFileVersionInfoSizeA
FakeGetFileVersionInfoSizeA endp

FakeGetFileVersionInfoSizeExW proc
  jmp g_pfnGetFileVersionInfoSizeExW
FakeGetFileVersionInfoSizeExW endp

FakeGetFileVersionInfoSizeW proc
  jmp g_pfnGetFileVersionInfoSizeW
FakeGetFileVersionInfoSizeW endp

FakeGetFileVersionInfoW proc
  jmp g_pfnGetFileVersionInfoW
FakeGetFileVersionInfoW endp

FakeVerFindFileA proc
  jmp g_pfnVerFindFileA
FakeVerFindFileA endp

FakeVerFindFileW proc
  jmp g_pfnVerFindFileW
FakeVerFindFileW endp

FakeVerInstallFileA proc
  jmp g_pfnVerInstallFileA
FakeVerInstallFileA endp

FakeVerInstallFileW proc
  jmp g_pfnVerInstallFileW
FakeVerInstallFileW endp

FakeVerLanguageNameA proc
  jmp g_pfnVerLanguageNameA
FakeVerLanguageNameA endp

FakeVerLanguageNameW proc
  jmp g_pfnVerLanguageNameW
FakeVerLanguageNameW endp

FakeVerQueryValueA proc
  jmp g_pfnVerQueryValueA
FakeVerQueryValueA endp

FakeVerQueryValueW proc
  jmp g_pfnVerQueryValueW
FakeVerQueryValueW endp

end
彙編程式碼中引用了全域性變數,也就是真實函式地址,在c++檔案中宣告
#pragma once

#include <Windows.h>

EXTERN_C FARPROC g_pfnGetFileVersionInfoA;
EXTERN_C FARPROC g_pfnGetFileVersionInfoByHandle;
EXTERN_C FARPROC g_pfnGetFileVersionInfoExW;
EXTERN_C FARPROC g_pfnGetFileVersionInfoSizeA;
EXTERN_C FARPROC g_pfnGetFileVersionInfoSizeExW;
EXTERN_C FARPROC g_pfnGetFileVersionInfoSizeW;
EXTERN_C FARPROC g_pfnGetFileVersionInfoW;
EXTERN_C FARPROC g_pfnVerFindFileA;
EXTERN_C FARPROC g_pfnVerFindFileW;
EXTERN_C FARPROC g_pfnVerInstallFileA;
EXTERN_C FARPROC g_pfnVerInstallFileW;
EXTERN_C FARPROC g_pfnVerLanguageNameA;
EXTERN_C FARPROC g_pfnVerLanguageNameW;
EXTERN_C FARPROC g_pfnVerQueryValueA;
EXTERN_C FARPROC g_pfnVerQueryValueW;
這些變數在DLL載入時通過GetProcAddress賦值即可。

最後,在工程中新建一個匯出宣告檔案,內容如下

LIBRARY
EXPORTS
GetFileVersionInfoA=FakeGetFileVersionInfoA @1
GetFileVersionInfoByHandle=FakeGetFileVersionInfoByHandle @2
GetFileVersionInfoExW=FakeGetFileVersionInfoExW @3
GetFileVersionInfoSizeA=FakeGetFileVersionInfoSizeA @4
GetFileVersionInfoSizeExW=FakeGetFileVersionInfoSizeExW @5
GetFileVersionInfoSizeW=FakeGetFileVersionInfoSizeW @6
GetFileVersionInfoW=FakeGetFileVersionInfoW @7
VerFindFileA=FakeVerFindFileA @8
VerFindFileW=FakeVerFindFileW @9
VerInstallFileA=FakeVerInstallFileA @10
VerInstallFileW=FakeVerInstallFileW @11
VerLanguageNameA=FakeVerLanguageNameA @12
VerLanguageNameW=FakeVerLanguageNameW @13
VerQueryValueA=FakeVerQueryValueA @14
VerQueryValueW=FakeVerQueryValueW @15
可以看到,這樣的寫法完全不理會函式呼叫方式及引數,還能支援x64,完美。