ページ 11

apiをフックしたい

Posted: 2012年4月22日(日) 15:13
by helloworld1853
個人的な興味としてVirtualProtectをフックしたいです。

ここのサイトを参考にしています。http://ruffnex.oc.to/kenji/text/api_hook

自分なりに頑張って見ましたがだめです。

PFNVirtualProtectが定義されていないなど

エラーが出ます。

コード:

/////////////////////////////////////////////////////////////////////
// ex2.cpp
// APIフックサンプルプログラム
// WindowsXP, VC++.NET
/////////////////////////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN
#define STRICT

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

// ImageDirectoryEntryToData
#pragma comment(lib, "Dbghelp.lib")

// ひとつのモジュールに対してAPIフックを行う関数
void 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フックを行う関数
void ReplaceIATEntryInAllMods(
                              PCSTR pszModuleName, 
                              PROC pfnCurrent, 
                              PROC pfnNew) 
{
    // モジュールリストを取得
    HANDLE hModuleSnap = CreateToolhelp32Snapshot(
        TH32CS_SNAPMODULE, GetCurrentProcessId());
    if(hModuleSnap == INVALID_HANDLE_VALUE)
        return;

    MODULEENTRY32 me;
    me.dwSize = sizeof(me);
    BOOL bModuleResult = Module32First(hModuleSnap, &me);
    // それぞれのモジュールに対してReplaceIATEntryInOneModを実行
    while(bModuleResult) {        
        ReplaceIATEntryInOneMod(pszModuleName, pfnCurrent, pfnNew, me.hModule);
        bModuleResult = Module32Next(hModuleSnap, &me);
    }
    CloseHandle(hModuleSnap);
}

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

int WINAPI Hook_VirtualProtect(
				LPVOID lpAddress,    
				DWORD dwSize,      
				DWORD flNewProtect,  
				PDWORD lpflOldProtect)
{
    // オリジナルVirtualProtectを呼び出す
    PROC pfnOrig = GetProcAddress(
        GetModuleHandleA("kernel32.dll"), "MessageBoxA");
    int nResult = ((PFNVirtualProtect) pfnOrig)
        (lpAdddress,dwSize,flNewProtect,lpflOldProtect);
    return nResult;
}

int WINAPI Hook_MessageBoxA(
                            HWND hWnd, 
                            LPCSTR pszText, 
                            LPCSTR pszCaption, 
                            UINT uType)
{
    // オリジナルMessageBoxAを呼び出す
    PROC pfnOrig = GetProcAddress(
        GetModuleHandleA("user32.dll"), "MessageBoxA");
    int nResult = ((PFNMESSAGEBOXA) pfnOrig)
        (hWnd, pszText, ("I am Hook_MessageBoxA"), uType);
    return nResult;
}

int WINAPI Hook_MessageBoxW(
                            HWND hWnd, 
                            LPCSTR pszText, 
                            LPCSTR pszCaption, 
                            UINT uType)
{
    // オリジナルMessageBoxWを呼び出す
    PROC pfnOrig = GetProcAddress(
        GetModuleHandleA("user32.dll"), "MessageBoxW");
    int nResult = ((PFNMESSAGEBOXW) pfnOrig)
        (hWnd, pszText, L("I am Hook_MessageBoxW"), uType);
    return nResult;
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    PROC pfnOrig;

    pfnOrig = ::GetProcAddress(
        GetModuleHandleA("kernel32.dll"), "VirtualProtect");
    ReplaceIATEntryInAllMods(
        "kernel32.dll", pfnOrig, (PROC)Hook_VirtualProtect);

    pfnOrig = ::GetProcAddress(
        GetModuleHandleA("user32.dll"), "MessageBoxA");
    ReplaceIATEntryInAllMods(
        "user32.dll", pfnOrig, (PROC)Hook_MessageBoxA);

    pfnOrig = ::GetProcAddress(
        GetModuleHandleA("user32.dll"), "MessageBoxW");
    ReplaceIATEntryInAllMods(
        "user32.dll", pfnOrig, (PROC)Hook_MessageBoxW);

    MessageBox(GetActiveWindow(), 
        _T("メッセージボックス表示のテスト"), _T("テスト"), MB_OK);

	unsigned long d;
	int a = 0,b = 0,c = 0;
	unsigned long code[32] = {0};
	
	printf("VirtualAlloc : %s\n\n",VirtualProtect(
		code,
		32,
		PAGE_EXECUTE_READWRITE,
		&d) != 0 ? "[OK]" : "[FALSE]");

	printf("INPUT [a,b,c] : ");
	scanf_s("%d,%d,%d",&a,&b,&c);

	code[0]  = 0x0424448B;	// MOV EAX,DWORD PTR SS:[ESP+4]
	code[1]  = 0x08244C8B;	// MOV ECX,DWORD PTR SS:[ESP+8]
	code[2]  = 0x9090C13B;	// CMP EAX,ECX
	code[3]  = 0x9090047E;	// JLE SHORT ; EIP+4
	code[4]  = 0x9090C88B;	// MOV ECX,EAX
	code[5]  = 0x0C24548B;	// MOV EDX,DWORD PTR SS:[ESP+C]
	code[6]  = 0x9090CA3B;	// CMP ECX,EDX
	code[7]  = 0x9090147E;	// JLE SHORT ; EIP+C
	code[8]  = 0x90909056;	// PUSH ESI
	code[9]  = 0x9090F28B;	// MOV ESI,EDX
	code[10] = 0x9090D18B;	// MOV EDX,ECX
	code[11] = 0x9090CE8B;	// MOV ECX,ESI
	code[12] = 0x9090905E;	// POP ESI
	code[13] = 0x90C0AF0F;	// IMUL EAX,EAX
	code[14] = 0x90C9AF0F;	// IMUL ECX,ECX
	code[15] = 0x90D2AF0F;	// IMUL EDX,EDX
	code[16] = 0x9090C103;	// ADD EAX,ECX
	code[17] = 0x9090C22B;	// SUB EAX,EDX
	code[18] = 0x9090D8F7;	// NEG EAX
	code[19] = 0x9090C01B;	// SBB EAX,EAX
	code[20] = 0x90909040;	// INC EAX
	code[21] = 0x909090C3;	// RETN
	
	printf("\nRETURN : %s\n",
		((int(*)(int,int,int))code)(a,b,c) == 0 ? "[FALSE]" : "[OK]");

    return 0;
}
下の方に機械語のコードが並んでいますが、これは三平方の処理をします。

きたないですがよろしくお願いします。

Re: apiをフックしたい

Posted: 2012年4月22日(日) 15:29
by softya(ソフト屋)
PFNVirtualProtectって自分で型宣言しないとダメだと思います。
PFNVIRTUALPROTECTってのがあるけどコレの間違いでは?

[追記]
それと
// オリジナルVirtualProtectを呼び出す
PROC pfnOrig = GetProcAddress(
GetModuleHandleA("kernel32.dll"), "MessageBoxA");
ってオリジナルのVirtualProtectのアドレスを取得していません。

Re: apiをフックしたい

Posted: 2012年4月22日(日) 19:57
by helloworld1853
いろいろ直した結果

見事に動作しました。

ソースコードは最後にのせておきます。

自作セキュリティソフトにAPIフックの技術を用いようとしたのですが、

友達にAPIフックについて相談したときに

APIフックはルートキットを実装したマルウェアにも用いられると

聞いて正直疑問に思ったのですが、

上記のソースコードのどういう点がルートキットになるうるのでしょうか。

ちょっと日本語がおかしいかもしれませんが

この疑問点に対する意見もお願いします。

Re: apiをフックしたい

Posted: 2012年4月22日(日) 21:18
by softya(ソフト屋)
その方面には決して明るくないですが、ファイルアクセスやプログラム実行をフック出来るので色々出来るのでは?とは思います。

Re: apiをフックしたい

Posted: 2012年4月22日(日) 22:09
by helloworld1853
その方面とは?

(僕はAPIフックはあまりセキュリティに役に立たないと受けとりました。)

Re: apiをフックしたい

Posted: 2012年4月22日(日) 22:41
by softya(ソフト屋)
helloworld1853 さんが書きました:その方面とは?

(僕はAPIフックはあまりセキュリティに役に立たないと受けとりました。)
ルートキットなどのセキュリティ全般です。

Re: apiをフックしたい

Posted: 2012年4月22日(日) 23:08
by shiro4ao
helloworld1853 さんが書きました: APIフックはルートキットを実装したマルウェアにも用いられると

聞いて正直疑問に思ったのですが、

上記のソースコードのどういう点がルートキットになるうるのでしょうか。
詳しくはないので想像ですが、
APIフックで、プログラムの作成者が意図したAPIと違う挙動をさせることが可能だからのような気がします。
具体的にはルートキットの機能の1つである「プロセスの隠蔽」が可能です。

たとえば、EnumProcesses()というプロセスを列挙してくれるAPIがあります。
この関数を使って現在動いているプログラムを
列挙してくれるタスクマネージャーのようなソフト(名前をps.exeとしましょう)があったとします。

このps.exeから悪意あるユーザーによって作られたウイルス(名前をvirus.exeにしましょう)が
見えないよう(=まるでそんなウイルスが動いていないように見せかける)にできるのです。

ps.exeのメモリに侵入(ここは長くなるので出来るものだと思ってください。最近の環境でできるかはわかりません。)して、
本物のEnumProcesses()のアドレスを偽物のEnumProcesses()へ書き換えておきます。
すると、ps.exeがEnumProcesses()を呼んだ時には偽EnumProcesses()が呼ばれます。
この偽EnumProcesses()のなかで、プロセスを列挙するときに、virus.exeが当然見つかりますが、
偽EnumProcesses()のなかで、virus.exeが存在しなかったことにしてしまいます。
結果的に、ps.exeでプロセスを確認できるはずなのに、動いているはずのvirus.exeを隠蔽できます。

ほかにも、使用期限をパスしたりできたりできなかったり・・・・・・

ここまではちょっとアヤシイ使い方ですが、逆にいい使い方(?)もできるわけです。
例えば、ShellExecute()というプログラムを走らせる関数があるのですが、
APIフックで代わりに偽ShellExecute()を呼び出させます。
そして偽ShellExecute()のなかで引数を確認して例えば、
コマンドプロンプトやシェル、バッチファイルを起動していたら
「おや?怪しいぞ?」ということで、メッセージボックスを出して、
「本当に大丈夫ですか?」とユーザーへ確認するような挙動です。

まとめると、そんな風に、挙動を勝手に変えられるので危険といえば危険なのです・・・・・

Re: apiをフックしたい

Posted: 2012年4月22日(日) 23:23
by helloworld1853
ありがとうございます。

だいぶ話がそれましたが

ソースコードをはっておきます。

皆様 本当にありがとうございます。

コード:

#define WIN32_LEAN_AND_MEAN
#define STRICT

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

// ImageDirectoryEntryToData
#pragma comment(lib, "Dbghelp.lib")

// ひとつのモジュールに対してAPIフックを行う関数
void 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フックを行う関数
void ReplaceIATEntryInAllMods(
                              PCSTR pszModuleName, 
                              PROC pfnCurrent, 
                              PROC pfnNew) 
{
    // モジュールリストを取得
    HANDLE hModuleSnap = CreateToolhelp32Snapshot(
        TH32CS_SNAPMODULE, GetCurrentProcessId());
    if(hModuleSnap == INVALID_HANDLE_VALUE)
        return;

    MODULEENTRY32 me;
    me.dwSize = sizeof(me);
    BOOL bModuleResult = Module32First(hModuleSnap, &me);
    // それぞれのモジュールに対してReplaceIATEntryInOneModを実行
    while(bModuleResult) {        
        ReplaceIATEntryInOneMod(pszModuleName, pfnCurrent, pfnNew, me.hModule);
        bModuleResult = Module32Next(hModuleSnap, &me);
    }
    CloseHandle(hModuleSnap);
}

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

int WINAPI Hook_VirtualProtect(
				LPVOID lpAddress,    
				DWORD dwSize,      
				DWORD flNewProtect,  
				PDWORD lpflOldProtect)
{
    // オリジナルVirtualProtectを呼び出す
    PROC pfnOrig = GetProcAddress(
        GetModuleHandleA("kernel32.dll"), "VirtualProtect");
    int nResult = ((PFNVIRTUALPROTECT) pfnOrig)
        (lpAddress,dwSize,flNewProtect,lpflOldProtect);
    return nResult;
}

int WINAPI Hook_MessageBoxA(
                            HWND hWnd, 
                            LPCSTR pszText, 
                            LPCSTR pszCaption, 
                            UINT uType)
{
    // オリジナルMessageBoxAを呼び出す
    PROC pfnOrig = GetProcAddress(
        GetModuleHandleA("user32.dll"), "MessageBoxA");
    int nResult = ((PFNMESSAGEBOXA) pfnOrig)
        (hWnd, pszText, ("I am Hook_MessageBoxA"), uType);
    return nResult;
}

int WINAPI Hook_MessageBoxW(
                            HWND hWnd, 
                            LPCSTR pszText, 
                            LPCSTR pszCaption, 
                            UINT uType)
{
    // オリジナルMessageBoxWを呼び出す
    PROC pfnOrig = GetProcAddress(
        GetModuleHandleA("user32.dll"), "MessageBoxW");
    int nResult = ((PFNMESSAGEBOXW) pfnOrig)
        (hWnd, pszText, (LPCSTR)L"I am Hook_MessageBoxW", uType);
    return nResult;
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    PROC pfnOrig;

    pfnOrig = ::GetProcAddress(
        GetModuleHandleA("kernel32.dll"), "VirtualProtect");
    ReplaceIATEntryInAllMods(
        "kernel32.dll", pfnOrig, (PROC)Hook_VirtualProtect);

    pfnOrig = ::GetProcAddress(
        GetModuleHandleA("user32.dll"), "MessageBoxA");
    ReplaceIATEntryInAllMods(
        "user32.dll", pfnOrig, (PROC)Hook_MessageBoxA);

    pfnOrig = ::GetProcAddress(
        GetModuleHandleA("user32.dll"), "MessageBoxW");
    ReplaceIATEntryInAllMods(
        "user32.dll", pfnOrig, (PROC)Hook_MessageBoxW);

    MessageBox(GetActiveWindow(), 
        _T("メッセージボックス表示のテスト"), _T("テスト"), MB_OK);

	unsigned long d;
	int a = 0,b = 0,c = 0;
	unsigned long code[32] = {0};
	
	printf("VirtualAlloc : %s\n\n",VirtualProtect(
		code,
		32,
		PAGE_EXECUTE_READWRITE,
		&d) != 0 ? "[OK]" : "[FALSE]");

	printf("INPUT [a,b,c] : ");
	//scanf_s("%d,%d,%d",&a,&b,&c);
	a=3;
	b=4;
	c=5;

	code[0]  = 0x0424448B;	// MOV EAX,DWORD PTR SS:[ESP+4]
	code[1]  = 0x08244C8B;	// MOV ECX,DWORD PTR SS:[ESP+8]
	code[2]  = 0x9090C13B;	// CMP EAX,ECX
	code[3]  = 0x9090047E;	// JLE SHORT ; EIP+4
	code[4]  = 0x9090C88B;	// MOV ECX,EAX
	code[5]  = 0x0C24548B;	// MOV EDX,DWORD PTR SS:[ESP+C]
	code[6]  = 0x9090CA3B;	// CMP ECX,EDX
	code[7]  = 0x9090147E;	// JLE SHORT ; EIP+C
	code[8]  = 0x90909056;	// PUSH ESI
	code[9]  = 0x9090F28B;	// MOV ESI,EDX
	code[10] = 0x9090D18B;	// MOV EDX,ECX
	code[11] = 0x9090CE8B;	// MOV ECX,ESI
	code[12] = 0x9090905E;	// POP ESI
	code[13] = 0x90C0AF0F;	// IMUL EAX,EAX
	code[14] = 0x90C9AF0F;	// IMUL ECX,ECX
	code[15] = 0x90D2AF0F;	// IMUL EDX,EDX
	code[16] = 0x9090C103;	// ADD EAX,ECX
	code[17] = 0x9090C22B;	// SUB EAX,EDX
	code[18] = 0x9090D8F7;	// NEG EAX
	code[19] = 0x9090C01B;	// SBB EAX,EAX
	code[20] = 0x90909040;	// INC EAX
	code[21] = 0x909090C3;	// RETN
	
	/*
	printf("RETURN :%s\n",
    ((int(*)(int,int,int))(void*)code)(a,b,c) == 0 ? "[FALSE]" : "[OK]");
	 MessageBox(GetActiveWindow(), 
        _T("メッセージボックス表示のテスト"), _T("テスト"), MB_OK);
		*/
	if(((int(*)(int,int,int))(void*)code)(a,b,c) == 0)
	{
		MessageBox(GetActiveWindow(),
		_T("NO"), _T("NO"), MB_OK);
	}
	else
	{
		MessageBox(GetActiveWindow(),
		_T("YES"), _T("YES"), MB_OK);
	}
    return 0;
}