破解技术中的Hook

我们在软件破解学习过程中经常遇到一些时间验证、注册码验证、网络数据抓取、USB狗数据抓取。要完成操作我们可以通过Hook技术来完成时间欺骗,验证码欺骗,数据抓取及欺骗。
注:钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
以上是微软对Hook的定义。通过SetWindowsHookEx 来设置Hook的函数。如果你认为这就是我要讲解的Hook技术,只能说图样图森破!这种Hook本质为Windows系统内核在调用正常流程前的一次函数回调。
今天要讲的Hook技术是程序汇编级的Hook调用。
如一个软件要对时间进行判断,当发现时间过期后自动退出。我们可以修改系统获取时间为一个早期时间,这样程序永远不会发送过期。
首先我们构造一个获取时间的函数。该函数主要通过获取系统时间,并修改系统时间的年份返回给调用者。其中临界区主要用于线程安全。ReleaseBase/ MonitorBase 主要是恢复代码和监控代码。这在下边详细讲解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void WINAPI MonFunc(LPSYSTEMTIME p)
{
	//Thread safety
	EnterCriticalSection(&g_cs);
 
	//Restore the original API before calling it
	ReleaseBase();
	GetSystemTime(p);
	p->wYear = 2014;
 
	MonitorBase();
 
	//You can do anything here, and you can call the UninstallMonitor
	//when you want to leave.
 
	//Thread safety
	LeaveCriticalSection(&g_cs);
	return ;
}
如下所示,在ReleaseBase/ MonitorBase主要完成向内存写入特定的数据。这些数据就是指令代码。一个是修改前的原始指令代码,一个是修改后的新的指令代码。比如程序原来的代码为调用函数B。但我修改函数B开始字节为新字节(功能:跳转到新的函数代码C中)。当程序执行到函数C时,恢复函数B的开始字节,然后进入原来函数B的调用,当函数B执行完毕后返回到C中恢复B开始字节。这样当下次调用函数B时,程序仍然会进入到函数C中执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__inline BOOL MonitorBase(void)
{
	// Modify the heading 6 bytes opcode in target API to jmp instruction,
	// the jmp instruction will lead the EIP to our fake function
	ReadProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aBackup), sizeof(g_aBackup)/ sizeof(g_aBackup[0]), NULL);
	printf("Do Mon\n");
	return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc),  LPVOID(g_aOpcode), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL);
}
 
__inline BOOL ReleaseBase(void)
{
	// Restore the heading 6 bytes opcode of target API.
	printf("Do NoMon\n");
	return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc),
		LPVOID(g_aBackup), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL);
}
这就是在函数体B中一会是进入函数C的代码,一会是执行自己的原本代码。进入函数C的汇编本质就是“jmp xxx”。这些可以在初始化(InstallMonitor)时计算出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Install Monitor
BOOL InstallMonitor(void)
{
	//Get handle of current process
	g_hProc = GetCurrentProcess();
 
	g_aOpcode[0] = 0xE9; //JMP Procudure
	*(DWORD*)(&g_aOpcode[1]) = (DWORD)MonFunc - g_dwApiFunc - 5;
 
	InitializeCriticalSection(&g_cs);
	printf("OP: %.2X %.2x %.2x %.2x %.2x  %.8x %.8x\n",g_aOpcode[0],g_aOpcode[1],g_aOpcode[2],g_aOpcode[3],g_aOpcode[4],(DWORD)MonFunc,g_dwApiFunc);
	//Start monitor
	return MonitorBase();
}
其内存汇编指令变化如下:
原始指令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
funcA:
      Push  xxxx
      Push  xxxx
      Call   B
      Cmp  eaxeax
 
funcB:
      Push ebp
      Mov ebpesp
      "......
funcC:
     "恢复为函数B 
     Push xxxx
     Push xxxx
     Call B
     "设置函数B为跳转到函数C
初始化后:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
funcA:
      Push  xxxx
      Push  xxxx
      Call   B
      Cmp  eaxeax
 
funcB:
      Jmp C
      "......
funcC:
     "恢复为函数B 
     Push xxxx
     Push xxxx
     Call B
     "设置函数B为跳转到函数C
当程序执行函数B时将跳转到函数C执行。执行完 恢复函数 B后,函数B恢复到之前状态。紧接着在函数C中调用函数B,函数B 可正常执行。
执行完后,设置函数B为跳转代码。这样,当程序下一次调用函数B后会继续执行函数C,如此循环。
截获设置时间的完整cpp代码如下。其生成动态库,可以通过远线程注入注入到目标程序中,以达到篡改时间的目的。当然也可以修改目标程序的导入目录,让程序自动加载该动态库。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// InjGetSysTime.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include 
#include 
 
#include 
#include "Shlwapi.h"
#include 
using namespace std;
#pragma comment(lib,"Shlwapi.lib") /*需要加上此行才可以正确link,VC6.0*/
 
 
 
//Handle of current process
HANDLE g_hProc;
 
//Backup of orignal code of target api
BYTE g_aBackup[6];
BYTE g_aOpcode[6];
 
//Critical section, prevent concurrency of calling the monitor
CRITICAL_SECTION g_cs;
 
//Base address of target API in DWORD
DWORD g_dwApiFunc = (DWORD)GetSystemTime;
 
//Hook the target API
__inline BOOL MonitorBase(void)
{
	// Modify the heading 6 bytes opcode in target API to jmp instruction,
	// the jmp instruction will lead the EIP to our fake function
	ReadProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aBackup), sizeof(g_aBackup)/ sizeof(g_aBackup[0]), NULL);
	printf("Do Mon\n");
	return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc),  LPVOID(g_aOpcode), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL);
}
 
//Unhook the target API
__inline BOOL ReleaseBase(void)
{
	// Restore the heading 6 bytes opcode of target API.
	printf("Do NoMon\n");
	return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc),
		LPVOID(g_aBackup), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL);
}
//Pre-declare
BOOL UninstallMonitor(void);
 
//Monitor Function
void WINAPI MonFunc(LPSYSTEMTIME p)
{
	//Thread safety
	EnterCriticalSection(&g_cs);
	//Restore the original API before calling it
	ReleaseBase();
	GetSystemTime(p);
	p->wYear = 2014;
	MonitorBase();
	//You can do anything here, and you can call the UninstallMonitor
	//when you want to leave.
	//Thread safety
	LeaveCriticalSection(&g_cs);
	return ;
}
//Install Monitor
BOOL InstallMonitor(void)
{
	//Get handle of current process
	g_hProc = GetCurrentProcess();
	g_aOpcode[0] = 0xE9; //JMP Procudure
	*(DWORD*)(&g_aOpcode[1]) = (DWORD)MonFunc - g_dwApiFunc - 5;
 
	InitializeCriticalSection(&g_cs);
	printf("OP: %.2X %.2x %.2x %.2x %.2x  %.8x %.8x\n",g_aOpcode[0],g_aOpcode[1],g_aOpcode[2],g_aOpcode[3],g_aOpcode[4],(DWORD)MonFunc,g_dwApiFunc);
	//Start monitor
	return MonitorBase();
}
 
BOOL UninstallMonitor(void)
{
	//Release monitor
	if (!ReleaseBase())
		return FALSE;
	DeleteCriticalSection(&g_cs);
	CloseHandle(g_hProc);
	//Synchronize to main application, release semaphore to free injector
	HANDLE hSema = OpenSemaphore(EVENT_ALL_ACCESS, FALSE, _T("Global\\InjHack"));
	if (hSema == NULL)
		return FALSE;
	return ReleaseSemaphore(hSema, 1, (LPLONG)g_hProc);
}
 extern "C" void __declspec(dllexport)	Test()
{
}
 
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
{
	TCHAR buf[MAX_PATH]={0};
	string str;
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		DisableThreadLibraryCalls(hInstDll);
		GetModuleFileName(hInstDll, buf, MAX_PATH);
		str = buf;
		str = str.substr(0, str.rfind("\\") );
		str += "\\Debug.Info";
		if ( ::PathFileExists( str.c_str() ) )
		{
			::AllocConsole();
			freopen("CONOUT$","w",stdout);
			printf("Prin On\n");
		}
		InstallMonitor();
		break;
	case DLL_PROCESS_DETACH:
		ReleaseBase();
		printf("Exit\n");
		break;
	}
	return TRUE;
}

hook1

注:网络配图

发表评论