windows消息勾取:
为了偷看或截取来往信息而在中间设置岗哨的行为称为“挂钩”,偷看或者操作信息的行为叫做“勾取”
在安装钩子后,我们可以提前看到消息,可以修改消息本身,还可以对消息实施拦截,阻止消息传递
SetWindowsHookEx():
SetWindowsHookEx()API可以轻松实现消息钩子,定义如下:
1 | HHOOK SetWindowsHookExA( |
HOOKPROC lpfn, 钩子子程的地址指针 。如果dwThreadId参数为0,或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,lpfn可以指向当前进程的一段钩子子程代码。 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。HINSTANCE hMod, 应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果dwThreadId 标识当前进程创建的一个线程, 而且子程代码位于当前进程,hMod必须为NULL。 可以很简单的设定其为本应用程序的实例句柄。DWORD dwThreadId 与安装的钩子子程相关联的线程的标识符。如果为0,钩子子程与所有的线程关联,即为全局钩子。
设置好钩子后,在某个进程生成指定消息时,操作系统会将相关的dll强制注入相应进程。
接下来看看实例分析:
1 | #include "stdio.h" |
这段代码应该都能看懂,下面我们看看dll源代码:
1 | #include "stdio.h" |
1 | LRESULT CALLBACK KeyboardProc( |
code可以是下列值:HC_ACTION:wParam和lParam包含按键消息
HC_NOREMOVE:wParam和lParam包含按键消息,并且按键消息不能从消息队列中移除(一个被PeekMessage函数调用的请求,指定PM_NOREMOVE标志)
wParam:按键的虚拟键值消息,例如:VK_F1
lParam:32位内存,内容描述包括:指定扩展键值,扫描码,上下文,重复次数。
关于返回值:
如果参数1:code小于0,则必须 返回CallNextHookEx(),也就是返回CallNextHookEx()的返回值
如果参数1:code大于等于0,并且钩子处理函数没有处理消息,强烈建议您 返回CallNextHookEx()的返回值,否则当您安装WH_KEYBOARD钩子时,钩子将不会得到通知,并返回错误结果。如果钩子处理的消息,您可以返回一个非0值,防止系统把消息传递给钩子链中的下一个钩子,或 者把消息发送到目标窗口。
回调函数:
某个特定事件发生时指定调用的函数。窗口window过程(Wndproc)就是一个典型的回调函数。
DLL注入:
dll注入指的是向运行中的其他进程强制插入特定的dll文件。dll注入命令其他进程自行调用LoadLibrary()API,加载到用户指定的dll文件。被注入的dll拥有目标进程内存的访问权限,用户可以随意操作(修复BUG,添加功能)
dll注入实现的三种方法:
1.创建远程线程(CreateRemoteThread()API)
2.使用注册表(AppInit_DLLs值)
3.消息勾取(SetWindowsHookEx()API)
练习示例:
1 | #include "windows.h" |
这个dll文件的意思就是创建线程函数,在线程函数中调用URLDownloadToFile()API下载指定网页文件。
下面看看注入文件:
1 | #include "windows.h" |
hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPID)是获取目标进程句柄。通过传来的dwPID获取目标进程句柄,得到访问权限,就可以控制对应进程了。
pRemoteBuf = VirtualAllocEx(hProcess,NULL,dwBufSize,MEM_COMMIT,PAGE_READWRITE)将要注入的dll路径写入目标进程内存。因为任何内存空间都无法写入,所以调用VirtualAllocEx()在目标进程分配一块缓冲区,指定缓冲区大小为dll文件路径长度。
WriteProcessMemory()所写的内存空间也是hProcess句柄所指向的目标进程的内存空间。
因为OS核心dll会被加载到自身固定的地址,dll注入利用的就是window OS的这一特性。所以,导入InjectDll.exe进程中的LoadLibraryW()地址与导入notepad.exe进程中的LoadLibraryW()地址是相同的。
hThread = CreateRemoteThread(hProcess,NULL,0,pThreadProc,pRemoteBuf,0,NULL);在目标进程中运行远程线程。
pThreadProc = notepad.exe进程内存中的LoadLibraryW()地址
pRemoteBuf = notepad.exe进程内存中的“c:\work\myhack.dll”字符串地址。
使用注册表:
将要注入dll路径字符串写入AppInit_DLLs项目,然后把LoadAppInit_DLLs的项目值设为1.重启后,指定dll就会注入所有运行进程。
DLL卸载:
工作原理:将FreeLibrary()地址传递给CreateRemoteThread()的IpStartAddress参数,并把卸载的dll句柄传递给IpParameter参数。
1 | #include "windows.h" |
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,dwPID)获取进程中加载的dll信息。将获取到的hSnapshot句柄传递给Module32First/Mudule32Next()函数后,即可设置MODULEENTRY32结构体相关的模块信息。
hModule = GetModuleHandle(L”kernel32.dll”);
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, “FreeLibrary”);
要使notepad进程自己调用FreeLibrary()API。需要先得到它的地址。
通过修改PE加载DLL:
准备工作:
修改思路:
PE文件中导入的dll信息以结构体的形式存储在IDT中。只要将dll添加到列表尾部就可以。
查看IDT是否有足够空间:
IMAGE_OPPTIONAL_HEADER结构体中导入表RVA即是IDT的RVA。如果内存不够,就移动IDT。
移动IDT的三种方法:
查找文件中的空白区域
增加文件最后一个节区的大小
在文件末尾添加新节区
修改文件:
1.修改导入表的RVA值
2.删除绑定导入表
3.创建IDT
4.设置Name、INT、IAT
5.修改IAT节区属性值
代码注入:
代码注入是一种向目标进程插入独立代码并使之运行的技术,他一般调用CreateRemoteThread()API以远程线程形式运行插入代码,所以也被称为线程注入。
代码注入的优点:
1.占用内存少
2.难以查找痕迹
1 | #include "windows.h" |
看懂上面这串代码,你也就理解代码注入了。
其核心API函数归纳如下:
1 | OpenProcess() |
dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;源代码中函数的顺序在二进制文件中顺序也是一样,又因为函数名称就是函数地址,所以做减法后结果就是ThreadProc()函数的大小。
使用汇编语言注入代码:
下面看一段代码:
1 | #include "windows.h" |
看懂这段代码就懂了。
总结:
你们会发现这一节代码量很多,但是这一节内容很相似,通过看代码你能理解每一小节的区别。