基本原理 and 在截取串口数据中的应用 ygm5169@.com2024/8/2212024/8/222Ø利用API Hook技术将我们的程序代码注入其他进程Ø修改系统中的ReadFile、WriteFile、CreateFile和CloseHandle函数2024/8/223Ø1、Windows为了保护进程而不允许进程之间的访问Ø2、使用API Hook 给目标进程设置一个特定消息Hook,在发生这种消息的时候就会自动调用Hook函数Ø3、Hook函数只能存在与DLL中,因为只有DLL可以“大家一起用”Ø4、当Hook函数被调用时,它所在的DLL也一起映射进入了目标进程Ø5、如果我们在DLL中修改了某些系统API,系统就会自动的调用我们自己定义的API,实现既定的功能2024/8/224WinAPI WINMAIN(…){ … SetDIPSHook …}SetDIPSHook{ SetWindowsHookEx(…)}……Call ReadFile;……GetMsgProc(){}1目标进程载入DLPSLIB.dll(寄生程序)同时修改ReadFile跳转到NewReadFileDLPSLIB.dllDLPSLIB.dll23共享内存6SimpleDIP.exe目标进程设定要 将GetMsgProc()注射进 目标进程NewReadFile(){}SetDIPSHook{ SetWindowsHookEx(…)}GetMsgProc(){}NewReadFile(){}ReadFile(){ ……}目标进程载入dll时,dll就已经成为其一部分了45思考:能直思考:能直接跳转吗?接跳转吗?控制进程截获的数据放在这里能够被任何进程访问2024/8/225只要两个函数的参数一样,即堆栈结构一样,就可以相互代替jmp NewReadFileReadFile函数的修改和使用修改ReadFile将ReadFile的第一条指令修改为jmp &NewReadFile使用ReadFile将要写入文件的数据进行修改将ReadFile的第一条指令还原调用ReadFile完成其功能将ReadFile的第一条指令修改为jmp指令2024/8/226……Call ReadFile……NewReadFile(){ 还原ReadFile 调用ReadFile 修改ReadFile ret}ReadFile(){ …… ret}调用自定义函数被劫持函数2024/8/227……Call ReadFile……NewReadFile(){ 还原ReadFile 调用ReadFile 修改ReadFile ret}ReadFile(){ jmp NewReadFile …… ret}调用自定义函数被劫持函数1234123456789截取串口数据,实际上就是利用上述原理修改系统的4个API:CreateFile、 ReadFile、 WriteFile和 CloseHandle, 利 用NewCreateFile截获串口打开时的设备句柄,在NewReadFile和NewWriteFile中用串口设备句柄识别串口的读写操作并对其数据进行观察控制。
2024/8/2282024/8/229NewCreateFile(文件名){ 还原CreateFile;Handle = CreateFile(文件名);修改CreateFile;if(文件名==“COM2”)保存Handle到共享内存变量ComHandle中}2024/8/2210NewWriteFile(文件句柄,数据){if(文件句柄=ComHandle)修改“数据” 还原WriteFile;WriteFile(文件句柄,数据);修改WriteFile;}2024/8/2211NewReadFile(文件句柄,数据){ 还原ReadFile;ReadFile(文件句柄,数据);修改ReadFile;if(文件句柄=ComHandle)修改“数据”}2024/8/2212NewCloseHandle(文件句柄){ 还原CloseHandle;CloseHandle (文件句柄);修改CloseHandle;if(文件句柄=ComHandle)ComHandle=NULL;}上述API Hook在运行中要反复修改被劫持的API在Windows多线程环境下会出现大量的内存错误。
解决思路有二:1、对修改API的过程用临界区等互斥量进行保护;2、开发一种不需要反复修改的技术2024/8/2213API Hook的不足API Hook 采用“拆东墙补西墙”的方式反复修改API,这对系统的稳定性造成极为恶劣的影响!为了防止多个线程竞争使用API,不得不用互斥量对其进行保护,这又严重影响了系统的执行效率很多时候这是不能容忍的对此而开发了一种只需要一次修改被劫持函数的方法——影子函数法2024/8/2214API Hook的影子函数法影子函数被设计为被劫持函数的“影子”——调用影子函数就等于调用被劫持函数只要调用影子函数所执行的指令与被劫持函数的指令完全一样,则其完全等效2024/8/2215……Call ReadFile……NewReadFile(){ call ShadowReadFile();}ReadFile(){ jmp NewReadFile 指令x+1 …… ret}调用自定义函数被劫持函数ShadowyReadFile(){ ReadFile的前x条指令 jmp &(指令x+1) ……}影子函数1234567892024/8/2216NewReadFile(文件句柄,数据){ShadowReadFile(文件句柄,数据);if(文件句柄=ComHandle)修改“数据”}总结2024/8/22172024/8/2218。