地址:http://bbs.pediy.com/showthread.php?t=44318
汇编ring3下实现HOOK API续之备份函数法
【文章标题】汇编ring3下实现HOOK API续之备份函数法
【文章作者】nohacks(非安全,hacker0058)
【作者主页】nohacks.ys168.com
【文章出处】看雪论坛(bbs.pediy.com)
我曾经写过一编文章,名字叫"汇编ring3下实现HOOK API",里面详细介绍了汇编ring3下实现HOOK API的几种方法,文章中着重介
绍了介绍了"改写内存地址JMP法"的方法,这也是比较通用的一种方法,让我们再来回顾一下改写内存地址JMP法的具体方法::
直接跳转,改变API函数的入口或出口的几个字节,使程序跳转到自己的函数,该方法不受程序加壳的限制。这种技术,说起来也不
复杂,就是改变程序流程的技术。在CPU的指令里,有几条指令可以改变程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。理
论上只要改变API入口和出口的任何机器码,都可以HOOK,下面我就说说常用的改写API入口点的方法:
因为工作在Ring3模式下,我们不能直接修改物理内存,只能一个一个打开修改,但具体的方法又分成好几种,我给大家介绍几种操
作思路:
<1>首先改写API首字节,要实现原API的功能需要调用API时先还原被修改的字节,然后再调用原API,调用完后再改回来,这样实现有
点麻烦,但最简单,从理论上说有漏HOOK的可能,因为我们先还原了API,如果在这之前程序调用了API,就有可能逃过HOOK的可能!
(2)把被覆盖的汇编代码保存起来,在替代函数里模拟被被覆盖的功能,然后调用原函数(原地址+被覆盖长度).但这样会产生一个问
题,不同的汇编指令长度是不一样的(比如说我们写入的JMP指令占用5个字节,而我们写入的这5个字节占用的位置不一定正好是一个或
多个完整的指令,有可能需要保存7个字节,才不能打乱程序原有的功能,需要编写一个庞大的判断体系来判断指令长度,网上已经有这
样的汇编程序(Z0MBiE写的LDE32),非常的复杂!
(3)把被HOOK的函数备份一下,调用时在替代函数里调用备份函数.为了避免麻烦,可以直接备份整个DLL缺点就是太牺牲内存!
上期我给大家介绍了上面的是第一种方法,最简单但有漏HOOK的可能,今天我们就来说说第3种方法:备份函数法.
我们来看看具体流程:
1.通过GetModuleInformation取得模块信息,也就是DLL的信息,我们来看看它的参数:
invoke GetModuleInformation,WProcess,ModuleHwnd,addr ModuleInformation,size MODULEINFO
<1> WProcess是进程句柄,在本进程可以由GetCurrentProcess返回
<2> ModuleHwnd是模块句柄,由"invoke GetModuleHandle,DllName"取得
<3> ModuleInformation是模块结构,提供一个变量用来装载模块信息
MODULEINFO struct
lpBaseOfDll dword 0 ;模块的地址
SizeOfImage dword 0 ;大小
EntryPoint dword 0 ;入口
MODULEINFO ends
<4>MODULEINFO的大小
2.通过HeapAlloc分配内存块来保存DLL
invoke HeapAlloc, hMainHeap , 0 , ModuleInformation.SizeOfImage ;分配内存块来保存DLL
调用前通过 GetProcessHeap取得 hMainHeap
3.通过WriteProcessMemory拷贝DLL到分配的内存
invoke WriteProcessMemory,WProcess,lpBuffer, \
ModuleInformation.lpBaseOfDll,ModuleInformation.SizeOfImage,0
4.计算用到的API在备份DLL中的地址
备份API地址=分配空间地址+原API地址-DLL模块的地址
5.HOOK API
这个就不说了,和上期一样,我就不重复了.下面是DLL部分的源码:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
; Programmed by hacker0058, [email protected] ;
; Website: http://nohacks.ys168.com ;
; Blog:http://sina.com.cn.nohacks ;
; 汇编(MASM):进程防杀 v1.0 ;
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
.486
.model flat,stdcall
option casemap:none
include debug.inc
include windows.inc
include psapi.inc
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
includeLib psapi.lib
HOOKAPI struct
a byte 0B8h
PMyapi DWORD 0
d BYTE 0FFh
e BYTE 0E0h
HOOKAPI ends
MODULEINFO struct
lpBaseOfDll dword 0
SizeOfImage dword 0
EntryPoint dword 0
MODULEINFO ends
;子程序声明
HookApi proto :DWORD ,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
WriteApi proto :DWORD ,:DWORD,:DWORD,:DWORD
NowOpenProcess proto :DWORD ,:DWORD,:DWORD
GetApi proto :DWORD,:DWORD
BakDll proto :DWORD,:DWORD
;已初始化数据
.data
hInstance dd 0
WProcess dd 0
Papi1 DWORD ?
WritBak1 HOOKAPI <>
ApiBak1 db 10 dup(?)
DllName1 db "kernel32.dll",0
ApiName1 db "OpenProcess",0
Dllbase1 DWORD ?
NowDllbase1 DWORD ?
;Dllbase2 DWORD ?
;NowDllbase2 DWORD ?
;未初始化数据
.data?
hHook dd ?
hPid dd ?
;hHwnd dd ?
;程序代码段
.code
;****************************************************************
;DLL入口点
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH ;当DLL加载时产生此事件
push hInst
pop hInstance
;invoke GetCommandLine
; mov CommandLine,eax ;取程序命令行
invoke GetCurrentProcess ;取进程伪句柄
mov WProcess ,eax
invoke BakDll,addr DllName1,addr Dllbase1 ;备份"kernel32.dll"
mov NowDllbase1,eax
; invoke BakDll,addr DllName2,addr Dllbase2 ;备份 "ueer32.dll"
; mov NowDllbase2,eax
invoke HookApi,addr DllName1,addr ApiName1,addr Papi1,addr NowOpenProcess,addr ApiBak1,addr WritBak1 ;HOOK OpenProcess
.endif
.if reason==DLL_PROCESS_DETACH
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;还原API
invoke GetProcessHeap
invoke HeapFree ,eax,0,NowDllbase1 ;释放内存
; invoke VirtualFree,NowDllbase1,0,MEM_RELEASE
.endif
mov eax,TRUE
ret
DllEntry Endp
;****************************************************************
GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov eax,TRUE
ret
GetMsgProc endp
;****************************************************************
InstallHook proc dwProcessId:dword
push dwProcessId
pop hPid
invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
UninstallHook proc
invoke UnhookWindowsHookEx,hHook
push eax
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;还原API
pop eax
ret
UninstallHook endp
;*****************************************************************
GetApi proc DllNameAddress:DWORD,ApiNameAddress:DWORD
invoke GetModuleHandle,DllNameAddress ;取DLL模块句柄
.if eax==NULL
invoke LoadLibrary ,DllNameAddress ;加载DLL
.endif
invoke GetProcAddress,eax,ApiNameAddress ;取API地址
mov eax,eax
ret
GetApi endp
;*********************************下面是核心部分*****************
HookApi proc DllName:dword,ApiName:dword ,Papi:dword , MyApi:dword ,ApiBak:dword ,WritBak:dword
LOCAL ApiDz
invoke GetApi,DllName,ApiName ;取API地址
.if eax==0
ret
.endif
mov ApiDz,eax ;下面几行是保存API地址
mov ebx,Papi
mov [ebx], eax
invoke ReadProcessMemory,WProcess,ApiDz,ApiBak,8,NULL ;备份原API的前8字节
.if eax==0
ret
.endif
mov eax,WritBak
assume eax:ptr HOOKAPI
push MyApi
pop [eax].PMyapi ;要替代API的函数地址
.if [eax].PMyapi == 0
mov eax,0
ret
.endif
invoke WriteApi,WProcess,ApiDz, WritBak ,size HOOKAPI ;HOOK API
ret
HookApi endp
WriteApi proc Process:DWORD ,Papi:DWORD,Ptype:DWORD,Psize:DWORD
LOCAL mbi:MEMORY_BASIC_INFORMATION
LOCAL msize:DWORD
;返回页面虚拟信息
invoke VirtualQueryEx,Process, Papi,addr mbi,SIZEOF MEMORY_BASIC_INFORMATION
;修改为可读写模式
invoke VirtualProtectEx,Process, mbi.BaseAddress,8h,PAGE_EXECUTE_READWRITE,addr mbi.Protect
;开始写内存
invoke WriteProcessMemory,Process, Papi, Ptype,Psize ,NULL
PUSH eax
;改回只读模式
invoke VirtualProtectEx,Process,mbi.BaseAddress,8h,PAGE_EXECUTE_READ,addr mbi.Protect
pop eax
ret
WriteApi endp
;*******************************************************************
;替代的API,参数要和原来一样
NowOpenProcess proc dwDesiredAccess:DWORD ,bInheritHandle:DWORD ,dwProcessId:DWORD
LOCAL NowApiBase
mov eax,hPid
.if eax==dwProcessId
mov eax,0
ret
.endif
;计算备份DLL中的API地址
mov eax,Papi1
sub eax,Dllbase1
add eax,NowDllbase1
mov NowApiBase,eax
;调用备份DLL中的API
push dwProcessId
push bInheritHandle
push dwDesiredAccess
call NowApiBase
ret
NowOpenProcess endp
BakDll proc DllName:DWORD,Dllbase:DWORD
LOCAL ModuleHwnd,hMainHeap,lpBuffer
LOCAL ModuleInformation: MODULEINFO
invoke GetModuleHandle,DllName
mov ModuleHwnd,eax
.if ModuleHwnd==0
ret
.endif
invoke GetModuleInformation,WProcess,ModuleHwnd,addr ModuleInformation,size MODULEINFO
.if eax ==0
jmp UninstallHook
ret
.endif
;原DLL的基地址
mov eax,Dllbase
assume eax:ptr
push ModuleInformation.lpBaseOfDll
pop [eax]
invoke GetProcessHeap
mov hMainHeap,eax
invoke HeapAlloc, hMainHeap , 0 , ModuleInformation.SizeOfImage ;分配内存块来保存DLL
;invoke VirtualAlloc, 0,ModuleInformation.SizeOfImage,4096,PAGE_EXECUTE_READWRITE
mov lpBuffer,eax
.if lpBuffer==0
jmp UninstallHook
ret
.endif
;备份DLL
invoke WriteProcessMemory,WProcess,lpBuffer, ModuleInformation.lpBaseOfDll,ModuleInformation.SizeOfImage,0
.if eax==0
jmp UninstallHook
ret
.endif
mov eax,lpBuffer
ret
BakDll endp
;*******************************************************************
End DllEntry
汇编ring3下实现HOOK API续之备份函数法
【文章标题】汇编ring3下实现HOOK API续之备份函数法
【文章作者】nohacks(非安全,hacker0058)
【作者主页】nohacks.ys168.com
【文章出处】看雪论坛(bbs.pediy.com)
我曾经写过一编文章,名字叫"汇编ring3下实现HOOK API",里面详细介绍了汇编ring3下实现HOOK API的几种方法,文章中着重介
绍了介绍了"改写内存地址JMP法"的方法,这也是比较通用的一种方法,让我们再来回顾一下改写内存地址JMP法的具体方法::
直接跳转,改变API函数的入口或出口的几个字节,使程序跳转到自己的函数,该方法不受程序加壳的限制。这种技术,说起来也不
复杂,就是改变程序流程的技术。在CPU的指令里,有几条指令可以改变程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。理
论上只要改变API入口和出口的任何机器码,都可以HOOK,下面我就说说常用的改写API入口点的方法:
因为工作在Ring3模式下,我们不能直接修改物理内存,只能一个一个打开修改,但具体的方法又分成好几种,我给大家介绍几种操
作思路:
<1>首先改写API首字节,要实现原API的功能需要调用API时先还原被修改的字节,然后再调用原API,调用完后再改回来,这样实现有
点麻烦,但最简单,从理论上说有漏HOOK的可能,因为我们先还原了API,如果在这之前程序调用了API,就有可能逃过HOOK的可能!
(2)把被覆盖的汇编代码保存起来,在替代函数里模拟被被覆盖的功能,然后调用原函数(原地址+被覆盖长度).但这样会产生一个问
题,不同的汇编指令长度是不一样的(比如说我们写入的JMP指令占用5个字节,而我们写入的这5个字节占用的位置不一定正好是一个或
多个完整的指令,有可能需要保存7个字节,才不能打乱程序原有的功能,需要编写一个庞大的判断体系来判断指令长度,网上已经有这
样的汇编程序(Z0MBiE写的LDE32),非常的复杂!
(3)把被HOOK的函数备份一下,调用时在替代函数里调用备份函数.为了避免麻烦,可以直接备份整个DLL缺点就是太牺牲内存!
上期我给大家介绍了上面的是第一种方法,最简单但有漏HOOK的可能,今天我们就来说说第3种方法:备份函数法.
我们来看看具体流程:
1.通过GetModuleInformation取得模块信息,也就是DLL的信息,我们来看看它的参数:
invoke GetModuleInformation,WProcess,ModuleHwnd,addr ModuleInformation,size MODULEINFO
<1> WProcess是进程句柄,在本进程可以由GetCurrentProcess返回
<2> ModuleHwnd是模块句柄,由"invoke GetModuleHandle,DllName"取得
<3> ModuleInformation是模块结构,提供一个变量用来装载模块信息
MODULEINFO struct
lpBaseOfDll dword 0 ;模块的地址
SizeOfImage dword 0 ;大小
EntryPoint dword 0 ;入口
MODULEINFO ends
<4>MODULEINFO的大小
2.通过HeapAlloc分配内存块来保存DLL
invoke HeapAlloc, hMainHeap , 0 , ModuleInformation.SizeOfImage ;分配内存块来保存DLL
调用前通过 GetProcessHeap取得 hMainHeap
3.通过WriteProcessMemory拷贝DLL到分配的内存
invoke WriteProcessMemory,WProcess,lpBuffer, \
ModuleInformation.lpBaseOfDll,ModuleInformation.SizeOfImage,0
4.计算用到的API在备份DLL中的地址
备份API地址=分配空间地址+原API地址-DLL模块的地址
5.HOOK API
这个就不说了,和上期一样,我就不重复了.下面是DLL部分的源码:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
; Programmed by hacker0058, [email protected] ;
; Website: http://nohacks.ys168.com ;
; Blog:http://sina.com.cn.nohacks ;
; 汇编(MASM):进程防杀 v1.0 ;
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
.486
.model flat,stdcall
option casemap:none
include debug.inc
include windows.inc
include psapi.inc
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
includeLib psapi.lib
HOOKAPI struct
a byte 0B8h
PMyapi DWORD 0
d BYTE 0FFh
e BYTE 0E0h
HOOKAPI ends
MODULEINFO struct
lpBaseOfDll dword 0
SizeOfImage dword 0
EntryPoint dword 0
MODULEINFO ends
;子程序声明
HookApi proto :DWORD ,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
WriteApi proto :DWORD ,:DWORD,:DWORD,:DWORD
NowOpenProcess proto :DWORD ,:DWORD,:DWORD
GetApi proto :DWORD,:DWORD
BakDll proto :DWORD,:DWORD
;已初始化数据
.data
hInstance dd 0
WProcess dd 0
Papi1 DWORD ?
WritBak1 HOOKAPI <>
ApiBak1 db 10 dup(?)
DllName1 db "kernel32.dll",0
ApiName1 db "OpenProcess",0
Dllbase1 DWORD ?
NowDllbase1 DWORD ?
;Dllbase2 DWORD ?
;NowDllbase2 DWORD ?
;未初始化数据
.data?
hHook dd ?
hPid dd ?
;hHwnd dd ?
;程序代码段
.code
;****************************************************************
;DLL入口点
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH ;当DLL加载时产生此事件
push hInst
pop hInstance
;invoke GetCommandLine
; mov CommandLine,eax ;取程序命令行
invoke GetCurrentProcess ;取进程伪句柄
mov WProcess ,eax
invoke BakDll,addr DllName1,addr Dllbase1 ;备份"kernel32.dll"
mov NowDllbase1,eax
; invoke BakDll,addr DllName2,addr Dllbase2 ;备份 "ueer32.dll"
; mov NowDllbase2,eax
invoke HookApi,addr DllName1,addr ApiName1,addr Papi1,addr NowOpenProcess,addr ApiBak1,addr WritBak1 ;HOOK OpenProcess
.endif
.if reason==DLL_PROCESS_DETACH
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;还原API
invoke GetProcessHeap
invoke HeapFree ,eax,0,NowDllbase1 ;释放内存
; invoke VirtualFree,NowDllbase1,0,MEM_RELEASE
.endif
mov eax,TRUE
ret
DllEntry Endp
;****************************************************************
GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov eax,TRUE
ret
GetMsgProc endp
;****************************************************************
InstallHook proc dwProcessId:dword
push dwProcessId
pop hPid
invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp
UninstallHook proc
invoke UnhookWindowsHookEx,hHook
push eax
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;还原API
pop eax
ret
UninstallHook endp
;*****************************************************************
GetApi proc DllNameAddress:DWORD,ApiNameAddress:DWORD
invoke GetModuleHandle,DllNameAddress ;取DLL模块句柄
.if eax==NULL
invoke LoadLibrary ,DllNameAddress ;加载DLL
.endif
invoke GetProcAddress,eax,ApiNameAddress ;取API地址
mov eax,eax
ret
GetApi endp
;*********************************下面是核心部分*****************
HookApi proc DllName:dword,ApiName:dword ,Papi:dword , MyApi:dword ,ApiBak:dword ,WritBak:dword
LOCAL ApiDz
invoke GetApi,DllName,ApiName ;取API地址
.if eax==0
ret
.endif
mov ApiDz,eax ;下面几行是保存API地址
mov ebx,Papi
mov [ebx], eax
invoke ReadProcessMemory,WProcess,ApiDz,ApiBak,8,NULL ;备份原API的前8字节
.if eax==0
ret
.endif
mov eax,WritBak
assume eax:ptr HOOKAPI
push MyApi
pop [eax].PMyapi ;要替代API的函数地址
.if [eax].PMyapi == 0
mov eax,0
ret
.endif
invoke WriteApi,WProcess,ApiDz, WritBak ,size HOOKAPI ;HOOK API
ret
HookApi endp
WriteApi proc Process:DWORD ,Papi:DWORD,Ptype:DWORD,Psize:DWORD
LOCAL mbi:MEMORY_BASIC_INFORMATION
LOCAL msize:DWORD
;返回页面虚拟信息
invoke VirtualQueryEx,Process, Papi,addr mbi,SIZEOF MEMORY_BASIC_INFORMATION
;修改为可读写模式
invoke VirtualProtectEx,Process, mbi.BaseAddress,8h,PAGE_EXECUTE_READWRITE,addr mbi.Protect
;开始写内存
invoke WriteProcessMemory,Process, Papi, Ptype,Psize ,NULL
PUSH eax
;改回只读模式
invoke VirtualProtectEx,Process,mbi.BaseAddress,8h,PAGE_EXECUTE_READ,addr mbi.Protect
pop eax
ret
WriteApi endp
;*******************************************************************
;替代的API,参数要和原来一样
NowOpenProcess proc dwDesiredAccess:DWORD ,bInheritHandle:DWORD ,dwProcessId:DWORD
LOCAL NowApiBase
mov eax,hPid
.if eax==dwProcessId
mov eax,0
ret
.endif
;计算备份DLL中的API地址
mov eax,Papi1
sub eax,Dllbase1
add eax,NowDllbase1
mov NowApiBase,eax
;调用备份DLL中的API
push dwProcessId
push bInheritHandle
push dwDesiredAccess
call NowApiBase
ret
NowOpenProcess endp
BakDll proc DllName:DWORD,Dllbase:DWORD
LOCAL ModuleHwnd,hMainHeap,lpBuffer
LOCAL ModuleInformation: MODULEINFO
invoke GetModuleHandle,DllName
mov ModuleHwnd,eax
.if ModuleHwnd==0
ret
.endif
invoke GetModuleInformation,WProcess,ModuleHwnd,addr ModuleInformation,size MODULEINFO
.if eax ==0
jmp UninstallHook
ret
.endif
;原DLL的基地址
mov eax,Dllbase
assume eax:ptr
push ModuleInformation.lpBaseOfDll
pop [eax]
invoke GetProcessHeap
mov hMainHeap,eax
invoke HeapAlloc, hMainHeap , 0 , ModuleInformation.SizeOfImage ;分配内存块来保存DLL
;invoke VirtualAlloc, 0,ModuleInformation.SizeOfImage,4096,PAGE_EXECUTE_READWRITE
mov lpBuffer,eax
.if lpBuffer==0
jmp UninstallHook
ret
.endif
;备份DLL
invoke WriteProcessMemory,WProcess,lpBuffer, ModuleInformation.lpBaseOfDll,ModuleInformation.SizeOfImage,0
.if eax==0
jmp UninstallHook
ret
.endif
mov eax,lpBuffer
ret
BakDll endp
;*******************************************************************
End DllEntry