DLLインジェクションでIATを書き換えることがうまくいかないことについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
TheOnionSoldier

DLLインジェクションでIATを書き換えることがうまくいかないことについて

#1

投稿記事 by TheOnionSoldier » 12年前

1. 自分は今何がしたくて:dllでsend関数を違う挙動にしたい
2. どう取り組んで(作ったプログラムはどれで:下のプログラムがdllになりSENDを変更します
3. どのようなエラーやトラブルで困っていて;メッセージボックスは挙動を変えられるのに同じ方法でSENDは変えられない
4. 自分は何が解らないのか、知りたいのか;SENDの挙動を変えるプログラムがほしい
5. 今のCの知識はどの程度なのか:3ヶ月くらい
OS名・windows7
コンパイラ名・visualstudio2010
ライブラリ名:

コード:

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#ifndef STRICT
#define STRICT
#endif

#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <Dbghelp.h>

// API_Hook_Lib.hに自分がDLLであることを通知
#define I_AM_HOOK_DLL
#include "API_Hook_Lib.h"


/////////////////////////////////////////////////////////////////////
// 制御関数
/////////////////////////////////////////////////////////////////////


// ImageDirectoryEntryToDataを使用するために
#pragma comment(lib, "Dbghelp.lib")

// g_hhookはSetWindowsHookExで使用するフックハンドル
// g_dwCurrentProcessIdは最初にDLLをマッピングしたプロセスのID
// つまりSetWindowsHookExを実行するプロセスのIDとなる
#pragma data_seg("Shared")
HHOOK g_hhook = NULL;
DWORD g_dwCurrentProcessId = 0;
#pragma data_seg()
#pragma comment(linker, "/Section:Shared,rws")


// SetWindowsHookExに渡すプロシージャ関数だが、事実上飾りみたいなもの
static LRESULT WINAPI GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
{
	// CallNextHookExの第1引数は2000/XP以降ならばNULLでもよい
	return(CallNextHookEx(g_hhook, code, wParam, lParam));
}

// SetWindowsHookExで全プロセスにフックを実行
API_HOOK_EXPORT int WINAPI API_Hook_AllApps_Start(DWORD dwThreadId) 
{
	// フックハンドルを確認
	if(g_hhook != NULL)
		return -1;

	// このDLLのハンドルを取得
	MEMORY_BASIC_INFORMATION mbi;
	if(VirtualQuery(API_Hook_AllApps_Start, &mbi, sizeof(mbi)) == 0)
		return -1;
	HMODULE hModule = (HMODULE) mbi.AllocationBase;

	// 全プロセスへフックを実行
	g_hhook = SetWindowsHookEx(
		WH_GETMESSAGE, GetMsgProc, hModule, dwThreadId);
	if(g_hhook == NULL)
		return -1;

	return 0;
}


// 全プロセスのフックを解除
API_HOOK_EXPORT int WINAPI API_Hook_AllApps_Stop() 
{
	// フックハンドルを確認
	if(g_hhook == NULL)
		return -1;

	// UnhookWindowsHookExにてフックを解除
	UnhookWindowsHookEx(g_hhook);
	g_hhook = NULL;
	return 0;
}


/////////////////////////////////////////////////////////////////////
// APIフックを実行するメインルーチン
/////////////////////////////////////////////////////////////////////


class CAPIHook 
{
public:

	// コンストラクタ
	CAPIHook(PSTR, PSTR, PROC);

	// デストラクタ
	~CAPIHook();

	// オリジナルアドレス取得用
	operator PROC() { return(m_pfnOrig); }

private:
	static CAPIHook *sm_pHead;  // ノードのトップのデータ
	CAPIHook *m_pNext;          // 次のノードのデータ

	// 保存領域
	PCSTR m_pszModuleName;      // 関数を持つモジュール名(ANSI)
	PCSTR m_pszFuncName;        // 関数名(ANSI)
	PROC  m_pfnOrig;            // オリジナル関数のアドレス
	PROC  m_pfnHook;            // 置き換わる関数のアドレス

	// APIフックを適応させるメイン関数
	static void WINAPI ReplaceIATEntryInAllMods(PCSTR, PROC, PROC);
	static void WINAPI ReplaceIATEntryInOneMod(PCSTR, PROC, PROC, HMODULE);

	// 新たにロードされたモジュールにAPIフックを対応させる関数
	static void WINAPI FixupNewlyLoadedModule(HMODULE, DWORD);

	// それぞれのAPIに置き換わるフック関数
	static HMODULE WINAPI Hook_LoadLibraryA(PCSTR);
	static HMODULE WINAPI Hook_LoadLibraryW(PCWSTR);
	static HMODULE WINAPI Hook_LoadLibraryExA(PCSTR, HANDLE, DWORD);
	static HMODULE WINAPI Hook_LoadLibraryExW(PCWSTR, HANDLE, DWORD);
	static FARPROC WINAPI Hook_GetProcAddress(HMODULE, PCSTR);

	// それぞれのAPIをフックすることを宣言
	static CAPIHook sm_LoadLibraryA;
	static CAPIHook sm_LoadLibraryW;
	static CAPIHook sm_LoadLibraryExA;
	static CAPIHook sm_LoadLibraryExW;
	static CAPIHook sm_GetProcAddress;
};

// ノードのトップを初期化
CAPIHook *CAPIHook::sm_pHead = NULL;


// コンストラクタ
CAPIHook::CAPIHook(
					PSTR pszModuleName,
					PSTR pszFuncName, 
					PROC pfnHook)
{
	m_pNext  = sm_pHead; // 次のノードのアドレスを代入
	sm_pHead = this;     // このノードのアドレスを代入

	// オリジナル関数のアドレスを取得
	PROC pfnOrig = ::GetProcAddress(
		GetModuleHandleA(pszModuleName), pszFuncName);
	
	// フックに関するデータを保存
	m_pszModuleName      = pszModuleName;
	m_pszFuncName        = pszFuncName;
	m_pfnOrig            = pfnOrig;
	m_pfnHook            = pfnHook;

	// プロセスIDが0ならDLLをマッピングする最初のプロセスと判断
	// そのプロセスIDを共有メモリに保存
	if(g_dwCurrentProcessId == 0)
		g_dwCurrentProcessId = GetCurrentProcessId();

	// 起動元と同じプロセスならフックを行わない
	if(g_dwCurrentProcessId != GetCurrentProcessId())
		ReplaceIATEntryInAllMods(m_pszModuleName, m_pfnOrig, m_pfnHook);
}


// デストラクタ
CAPIHook::~CAPIHook() 
{
	// 起動元と同じプロセスなら解除の必要なし
	if(g_dwCurrentProcessId != GetCurrentProcessId())
		ReplaceIATEntryInAllMods(m_pszModuleName, m_pfnHook, m_pfnOrig);

	// ノードのトップを取得
	CAPIHook *p = sm_pHead;

	// ノードのトップが自分ならば、次のノードをトップに据えて終了
	if(p == this) {
		sm_pHead = p->m_pNext;
		return;
	}
	// もし自分ではないならば、ノードの中から検索して
	// 自分を連結から外す
	while(p->m_pNext != NULL) {
		if (p->m_pNext == this) {
			p->m_pNext = p->m_pNext->m_pNext; 
			break; 
		}
		p = p->m_pNext;
	}
}


// すべてのモジュールに対してAPIフックを行う関数
void CAPIHook::ReplaceIATEntryInAllMods(
										PCSTR pszModuleName, 
										PROC pfnCurrent, 
										PROC pfnNew)
{
	// 自分自身(API_Hook_Lib.dll)のモジュールハンドルを取得
	MEMORY_BASIC_INFORMATION mbi;
	if(VirtualQuery(ReplaceIATEntryInAllMods, &mbi, sizeof(mbi)) == 0)
		return;
	HMODULE hModThisMod = (HMODULE) mbi.AllocationBase;

	// モジュールリストを取得
	HANDLE hModuleSnap = CreateToolhelp32Snapshot(
		TH32CS_SNAPMODULE, GetCurrentProcessId());
	if(hModuleSnap == INVALID_HANDLE_VALUE)
		return;

	MODULEENTRY32 me;
	me.dwSize = sizeof(me);
	BOOL bModuleResult = Module32First(hModuleSnap, &me);
	// それぞれのモジュールに対してReplaceIATEntryInOneModを実行
	// ただし自分自身(API_Hook_Lib.dll)には行わない
	while(bModuleResult) {
		if(me.hModule != hModThisMod)
			ReplaceIATEntryInOneMod(pszModuleName, pfnCurrent, pfnNew, me.hModule);
		bModuleResult = Module32Next(hModuleSnap, &me);
	}
	CloseHandle(hModuleSnap);
}


// ひとつのモジュールに対してAPIフックを行う関数
void CAPIHook::ReplaceIATEntryInOneMod(
									   PCSTR pszModuleName, 
									   PROC pfnCurrent, 
									   PROC pfnNew, 
									   HMODULE hmodCaller) 
{
	// モジュールのインポートセクションのアドレスを取得
	ULONG ulSize;
	PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
	pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(
		hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);

	// インポートセクションを持っていない
	if (pImportDesc == NULL)
		return;

	// インポートディスクリプタを検索
	while(pImportDesc->Name) {
		PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
		if (lstrcmpiA(pszModName, pszModuleName) == 0) 
			break;
		pImportDesc++;
	}

	// このモジュールは呼び出し先から関数をインポートしていない
	if (pImportDesc->Name == 0)
		return;

	// インポートアドレステーブルを取得
	PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) 
		((PBYTE) hmodCaller + pImportDesc->FirstThunk);

	// 新しい関数アドレスに置き換える
	while(pThunk->u1.Function) {
		
		// 関数アドレスのアドレスを取得
		PROC *ppfn = (PROC*) &pThunk->u1.Function;

		// 該当関数であるならば発見!
		BOOL fFound = (*ppfn == pfnCurrent);

		if (fFound) {
			// アドレスが一致したので、インポートセクションのアドレスを書き換える
			DWORD dwDummy;
			VirtualProtect(ppfn, sizeof(ppfn), PAGE_EXECUTE_READWRITE, &dwDummy);
			WriteProcessMemory(
				GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL);
			return;
		}
		pThunk++;
	}
	// ここに処理が移った場合、インポートセクションに該当関数がなかったことになる
	return;
}


/////////////////////////////////////////////////////////////////////
// デフォルトでフックするAPIの処理(CAPIHookクラス内部で管理する)
/////////////////////////////////////////////////////////////////////

// デフォルトでフックする関数のプロトタイプを定義
typedef HMODULE (WINAPI *PLOADLIBRARYA)(PCSTR);
typedef HMODULE (WINAPI *PLOADLIBRARYW)(PCWSTR);
typedef HMODULE (WINAPI *PLOADLIBRARYEXA)(PCSTR, HANDLE, DWORD);
typedef HMODULE (WINAPI *PLOADLIBRARYEXW)(PCWSTR, HANDLE, DWORD);
typedef FARPROC (WINAPI *PGETPROCADDRESS)(HMODULE, PCSTR);

// モジュールを扱うAPIをあらかじめフックしておく
CAPIHook CAPIHook::sm_LoadLibraryA  (
	"Kernel32.dll", "LoadLibraryA", (PROC) CAPIHook::Hook_LoadLibraryA);
CAPIHook CAPIHook::sm_LoadLibraryW  (
	"Kernel32.dll", "LoadLibraryW", (PROC) CAPIHook::Hook_LoadLibraryW);
CAPIHook CAPIHook::sm_LoadLibraryExA(
	"Kernel32.dll", "LoadLibraryExA", (PROC) CAPIHook::Hook_LoadLibraryExA);
CAPIHook CAPIHook::sm_LoadLibraryExW(
	"Kernel32.dll", "LoadLibraryExW", (PROC) CAPIHook::Hook_LoadLibraryExW);
CAPIHook CAPIHook::sm_GetProcAddress(
	"Kernel32.dll", "GetProcAddress", (PROC) CAPIHook::Hook_GetProcAddress);


// 新たにロードされた関数をフックする関数
void CAPIHook::FixupNewlyLoadedModule(HMODULE hMod, DWORD dwFlags)
{
	if ((hMod != NULL) && ((dwFlags & LOAD_LIBRARY_AS_DATAFILE) == 0)) {
		for (CAPIHook *p = sm_pHead; p != NULL; p = p->m_pNext) {
			ReplaceIATEntryInOneMod(
				p->m_pszModuleName, p->m_pfnOrig, p->m_pfnHook, hMod);
		}
	}
}

// 置き換わったLoadLibraryA関数
HMODULE WINAPI CAPIHook::Hook_LoadLibraryA(PCSTR pszModulePath) 
{
	HMODULE hMod = (
		(PLOADLIBRARYA)(PROC) CAPIHook::sm_LoadLibraryA)(pszModulePath);
	FixupNewlyLoadedModule(hMod, 0);
	return hMod;
}


// 置き換わったLoadLibraryW関数
HMODULE WINAPI CAPIHook::Hook_LoadLibraryW(PCWSTR pszModulePath) 
{
	HMODULE hMod = (
		(PLOADLIBRARYW)(PROC) CAPIHook::sm_LoadLibraryW)(pszModulePath);
	FixupNewlyLoadedModule(hMod, 0);
	return hMod;
}

// 置き換わったLoadLibraryExA関数
HMODULE WINAPI CAPIHook::Hook_LoadLibraryExA(
										PCSTR pszModulePath, 
										HANDLE hFile, 
										DWORD dwFlags)
{
	HMODULE hMod = (
		(PLOADLIBRARYEXA)(PROC) CAPIHook::sm_LoadLibraryExA)
		(pszModulePath, hFile, dwFlags);
	FixupNewlyLoadedModule(hMod, dwFlags);
	return hMod;
}

// 置き換わったLoadLibraryExW関数
HMODULE WINAPI CAPIHook::Hook_LoadLibraryExW(
										PCWSTR pszModulePath, 
										HANDLE hFile, 
										DWORD dwFlags) 
{
	HMODULE hMod = (
		(PLOADLIBRARYEXW)(PROC) CAPIHook::sm_LoadLibraryExW)
		(pszModulePath, hFile, dwFlags);
	FixupNewlyLoadedModule(hMod, dwFlags);
	return hMod;
}


// 置き換わったGetProcAddress関数
FARPROC WINAPI CAPIHook::Hook_GetProcAddress(HMODULE hMod, PCSTR pszProcName)
{
	// 本当の関数アドレスを取得
	FARPROC pfn = (
		(PGETPROCADDRESS)(PROC) CAPIHook::sm_GetProcAddress)
		(hMod, pszProcName);

	// もしフックすべき関数であったならば、置き換わった関数のアドレスを渡す
	for (CAPIHook *p = sm_pHead; (pfn != NULL) && (p != NULL); p = p->m_pNext) {
		if (pfn == p->m_pfnOrig)
			return (p->m_pfnHook);
	}
   return pfn;
}


/////////////////////////////////////////////////////////////////////
// この行以降に新たにフックしたいAPIを宣言し、置き換える関数を加える
/////////////////////////////////////////////////////////////////////


// 変数定義
extern CAPIHook g_MessageBoxA;
extern CAPIHook g_MessageBoxW;
extern CAPIHook g_send;


// フックする関数のプロトタイプを定義
typedef int (WINAPI *PFNMESSAGEBOXA)(HWND, PCSTR, PCSTR, UINT);
typedef int (WINAPI *PFNMESSAGEBOXW)(HWND, PCSTR, PCSTR, UINT);
typedef int (WINAPI *PFNSEND)(int, int, PCSTR,int);


// 置き換わる関数定義

void Main_MessageBox(void)
{
	// プロセスのファイル名を取得
	static TCHAR szProcessPath[1024];
	GetModuleFileNameA(NULL, (LPSTR)szProcessPath, sizeof(szProcessPath));

	// メインWindowへ通知するデータを作成
	COPYDATASTRUCT cds;
	cds.lpData = szProcessPath;
	cds.cbData = lstrlen(szProcessPath) + 1;
	cds.dwData = 0;

	// メインWindowを検索
	HWND hWnd = FindWindow(NULL, _T("API Hook Sample"));
	if(hWnd)
		SendMessage(hWnd, WM_COPYDATA, NULL, (LPARAM)&cds);
}

int WINAPI Hook_MessageBoxA(HWND hWnd,PCSTR pszText, PCSTR pszCaption,UINT uType)
{
	// オリジナルMessageBoxAを呼び出す
	int nResult = ((PFNMESSAGEBOXA)(PROC) g_MessageBoxA)
		(hWnd, pszText, pszCaption, uType);
	Main_MessageBox();
	return nResult;
}

int WINAPI Hook_MessageBoxW(HWND hWnd,PCSTR pszText,PCSTR pszCaption,UINT uType)
{
	// オリジナルMessageBoxWを呼び出す
	int nResult = ((PFNMESSAGEBOXW)(PROC) g_MessageBoxW)
		(hWnd, pszText, pszCaption, uType);
	Main_MessageBox();
	return nResult;
}

int WINAPI Hook_SEND(int sock,PCSTR buf, int size,int flag)
{
	
	Main_MessageBox();
	return 0;
}

// フックを実行
CAPIHook g_MessageBoxA(	"User32.dll", "MessageBoxA", (PROC) Hook_MessageBoxA);
CAPIHook g_MessageBoxW("user32.dll", "MessageBoxW", (PROC) Hook_MessageBoxW);

CAPIHook g_send("wsock32.dll", "send", (PROC) Hook_SEND);


“C言語何でも質問掲示板” へ戻る