-
-
[原创]HOOK技术 学习总结
-
发表于: 2021-9-26 18:05 10242
-
Hook 的关键就是通过一定的手段埋下“钩子",“钩”住我们关心的重要流程,然后根据需要对执行过程进行干预。
HOOK技术导致了程序执行流程的改变。通过在执行真正的目标函数之前执行事先插入的代码,获得了程序执行过程的决定权——“插入特定代码以干预程序的执行流程”就是 Hook 的奥义。
(通过修改IAT的方式拦截对API的调用,所以称为IAT Hook
)
注意:如果是通过动态加载的就不行了,因为动态加载的dll的API不在iat中,而是动态生成的.
执行地址01002628处的CALL命令后,运行将转移至user32.SetWindowTextW()函数的起始地址(77D0960E)处(1),函数执行完毕后返回(2)。
执行地址01002628处的CALL命令后,运行将转移至user32.SetWindowTextW()函数的起始地址(77D0960E)处(1),函数执行完毕后返回(2)。
hookiat.dll文件中提供了名为MySetWindowTextWO)的钩取函数( 10001000 )。
跟踪进入01001110地址中发现,它的值已经变为10001000,地址10001000是hookiat.MySetWindowTextW()函数的起始地址。也就是说,在保持运行代码不变的前提下,将IAT中保存的API起始地址变为用户函数的起始地址。这就是IAT钩取的基本工作原理。在IAT中找到的是被修改的函数地址,但是程序并不知道这一点,而是直接跑到被修改的地址处执行了
使用IAT Hook的程序,一般会读取PE文件头信息,解析PE结构,查找IAT
局限性:1当程序运用一种叫late-demand binding
技术,函数被调用时才定位地址,这样以来就不能在IAT中定位目标函数地址了.2当目标程序用动态加载(LoadLibrary)时,这种方法也将失效.(如果是通过动态加载的就不行了
因为动态加载的dll的API不在iat中,而是动态生成的.)
Inline Hook根据修改的字节数不同可以分为5字节修改技术和7字节修改技术( HotFix Hook)
Inline Hook
直接修改内存中任意函数的代码,将其劫持至Hook API。同时,它比IAT Hook的适用范围更广,因为只要是内存中有的函数它都能Hook,而后者只能Hook IAT表里存在的函数(有些程序会动态加载函数)。
技术原理
首先把stealth.dll注入目标进程,钩取ntdll.ZwQuerySystemInformation() API。ntdll.ZwQuery-SystemInformation()API起始地址(7C93D92E)的5个字节代码被修改为JMP 10001120((仅修改5个字节代码)。10001120是stealth.MyZwQuerySystemInformation()函数的地址。此时,在procexp.exe代码中调用ntdll.ZwQuerySystemInformation() API,.程序将按如下顺序执行。
(1)在422CF7地址处调用ntdll.ZwQuerySystemInformation() API ( 7C93D92E)。
(2)位于7C93D92E地址处的(修改后的)JMP 10001120指令将执行流转到10001120地址处( hooking函数)。1000116A地址处的CALL unhook()指令用来将ntdll.ZwQuerySystemInformation()API的起始5个字节恢复原值。
(3)位于1000119B地址处的CALL EAX(7C93D92E)指令将调用原来的函数( ntdll.ZwQuery-SystemInformation()API )(由于前面已经“脱钩”,所以可以正常调用执行)。
(4)ntdll.ZwQuerySystemInformation()执行完毕后,由7C93D93A地址处的RETN 10指令返回到stealth.dll代码区域(调用自身的位置)。然后10001212地址处的CALL hook()指令再次钩取ntdll.ZwQuerySystemInformation)API(即将开始的5字节修改为JMP 10001120指令)。
(5)stealth.MyZwQuerySystemInformation()函数执行完毕后,由10001233地址处的RETN 10命令返回到procexp.exe进程的代码区域,继续执行。
==使用该技术,一定要在控制程序流程后修复API。==
代码
从上节对Code Hook方法的讲解中,我们会发现Code Hook存在一个效率的问题,因为每次Code Hook都要进行“挂钩+脱钩”的操作,也就是要对API的前5字节修改两次,这样,当我们要进行全局Hook的时候,系统运行效率会受影响。而且,当一个线程尝试运行某段代码时,若另一个线程正在对该段代码进行“写”操作,这时就会程序冲突,最终引发一些错误。
有没有办法避免这种隐患呢?答案是有的,可以使用HotFix Hook(“热补丁”)方法。
API起始代码有如下两个明显的相似点:
[1]API代码以“MOV EDI,EDI”指令开始。
[2]API代码上方有5个NOP指令。
MOV EDI,EDI用于将EDI的值再次复制给EDI,这没有什么实际意义。也就是说,API起始代码的MOV指令(2个字节)与其上方的5个NOP指令(5个字节)合起来共7个字节的指令没有任何意义。所以我们就可以通过修改这7个字节来实现Hook操作。这种方法因为可以在进程处于运行状态时临时更改进程内存中的库文件,所以微软也常用这种方法来打“热补丁”。
详细内容看《dll注入&代码注入》
SetWinEventHook function (winuser.h) - Win32 apps | Microsoft Docs
The client thread that calls SetWinEventHook must have a message loop in order to receive events.(调用SetWinEventHook的客户端线程必须具有消息循环才能接收事件。)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FCrTomvH-1632650145285)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210926175447773.png)]
dwFlags:
WINEVENT_INCONTEXT | WINEVENT_SKIPOWNPROCESS
WINEVENT_INCONTEXT | WINEVENT_SKIPOWNTHREAD
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD
此外,客户申请可以指定WINEVENT_INCONTEXT,或单独WINEVENT_OUTOFCONTEXT
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3CoWST1u-1632650145286)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210926175413574.png)]
区别:
SetWinEventHook 和 SetWindowsHookEx 都可以指定钩子函数(hook function)来截取别的进程中的消息,但是他们之间有一些不同。
SetWindowsHookEx 有两种钩子函数,一种是全局钩子(global hook),另一种是线程钩子(thread hook)。全局钩子能够截取所有线程的消息,但是全局钩子函数必须存在于一个 dll 中。线程钩子只能截取属于当前进程中的线程的消息,钩子函数不需要放在 dll 中。SetWinEventHook 也有两种钩子函数,一种是进程内钩子(in-context hook),另一种是进程外钩子(out-of-context hook)。进程内钩子函数必须放在 dll 中,将被映射到所有进程中。进程外钩子函数不会被映射到别的进程中,所以也不需要被放到 dll 中。不管进程内或进程外钩子都能截取到所有进程的消息,区别仅是进程内钩子效率更高。
SetWindowsHookEx 和 SetWinEventHook 两种方法截取的消息的类型不一样。SetWindowsHookEx 能截取所有WM 开头的消息。而 SetWinEventHook 截取的消息都是 EVENT 开头的,这些消息所有都是跟对象的状态相关的,所以它无法获取根鼠标键盘相关的消息。
SetWindowsHookEx 设定的全局钩子必须被注入到别的进程中,所以就无法截取到一些有限制的进程的消息,比如命令行窗口(console window)。而 SetWinEventHook 的进程外钩子就没有这个限制。
全称:System Services Descriptor Table(系统服务描述符表)
SSDT表 把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来
SSDT的每个成员叫做系统服务表
SSDT结构:
查看SSDT表
第二张表为0,使用KeServiceDescriptorTable这个公开的导出函数,我们无法看到win32k.sys这张表结构
win32k.sys系统服务表已经可见
实验:ring3 调用ReadVirtualMemory 函数
查看系统服务函数地址:
[函数地址表 + 系统服务号*4] = 内核函数地址 805aa712
查看参数地址
[参数表 + 系统服务号] = 内核函数参数个数(单位:字节)14
查看内核函数反汇编:
SSDT HOOK 原理:
将805aa712改为指向我们函数的地址:MyNtReadVirtualMemory。系统就会直接调用MyNtReadVirtualMemory
而不是原来的 NtReadVirtualMemory 函数。
注意:修改SSDT表,这个表必须可写,但在xp以后的系统中他都是只读的。
修改:
(1) 更改注册表
恢复页面保护:HKLM\SYSTEM\CurrentControlset\Control\Session Manger\Memory Management\EnforceWriteProtection=0
去掉页面保护:HKLM\SYSTEM\CurrentControlset\Control\Session Manger\Memory Management\DisablePagingExecutive=1
(2)改变CR0寄存器的第1位
Windows对内存的分配,是采用的分页管理。其中有个CR0寄存器,如图
其中第1位叫做保护属性位,控制着页的读或写属性。如果为1,则可以读/写/执行;如果为0,则只可以读/执行。
SSDT,IDT的页属性在默认下都是只读,可执行的,但不能写
代码:
(3)通过Memory Descriptor List
只做笔记整理。代码非原创。
/
*
函数说明:
获取进程IAT列表
输入参数:
INode
*
*
iNode : IAT表项结构体二级指针
PNode
*
pNode : 进程结构体指针
unsigned
int
pid : 进程PID
输出参数:
*
/
int
GetIAT(INode
*
*
iNode, PNode
*
pNode, unsigned
int
pid)
{
unsigned char buff[
1025
]
=
{
0
};
/
/
用于临时保存读取的buff
unsigned char nameAddrBuff[
513
]
=
{
0
};
/
/
IAT表项函数名地址列表
unsigned char addrBuff[
513
]
=
{
0
};
/
/
IAT表项函数地址列表
char dllName[NAMESIZE]
=
{
0
};
/
/
IAT表项所属dll名
unsigned char nameBuff[NAMESIZE]
=
{
0
};
/
/
IAT表项函数名
PNode
*
bkPNode
=
pNode;
/
/
初始化进程结构体链表操作指针
INode
*
bkINode
=
NULL;
/
/
定义IAT表项结构体操作指针
INode
*
newINode
=
NULL;
/
/
定义新的IAT表项结构体指针
HANDLE handle
=
NULL;
/
/
初始化进程句柄
LPCVOID addr
=
0
;
/
/
地址指针
int
offset
=
0
;
/
/
保存PE结构偏移
LPDWORD readBuffCount
=
0
;
/
/
保存ReadProcessMemory实际读取的字节数
int
flag
=
0
;
/
/
函数调用标记
int
error
=
0
;
/
/
函数调用出错代码
int
order
=
0
;
/
/
函数在列表中的序号
int
IATaddr
=
0
;
/
/
IAT表的地址
int
descriptorBaseAddr
=
0
;
/
/
IMAGE_IMPORT_DESCRIPTOR结构体首地址
int
dllNameAddr
=
0
;
/
/
dll名地址
int
funcNameAddr
=
0
;
/
/
函数名列表地址
int
funcAddrAddr
=
0
;
/
/
函数地址列表地址
int
funcName
=
0
;
/
/
函数名地址
int
i
=
0
;
/
/
循环计数
int
j
=
0
;
/
/
循环变量
InitINode(iNode);
/
/
初始化IAT表项结构体链表头指针
bkINode
=
*
iNode;
/
/
初始化IAT表项结构体链表操作指针
if
(NULL
=
=
bkPNode)
/
/
如果进程链表为空,则出错退出
{
return
-
1
;
}
for
(;;)
/
/
循环遍历进程结构体中与所给进程PID相符进程结构体
{
if
(pid
=
=
bkPNode
-
>pe32.th32ProcessID)
{
break
;
}
else
{
if
(NULL
=
=
bkPNode
-
>
next
)
{
return
-
1
;
}
else
{
bkPNode
=
bkPNode
-
>
next
;
}
}
}
if
(EnableDebugPriv(SE_DEBUG_NAME))
/
/
获取进程调试权限
{
printf(
"Add Privilege error\n"
);
return
-
1
;
}
handle
=
OpenProcess(PROCESS_ALL_ACCESS,
1
,pid);
/
/
获取进程句柄
if
(handle
=
=
INVALID_HANDLE_VALUE)
{
return
-
1
;
}
addr
=
bkPNode
-
>me32.modBaseAddr;
/
/
获取进程加载基址
flag
=
ReadProcessMemory(handle, addr, buff,
512
, readBuffCount);
/
/
读取进程前
512
字节信息
offset
=
buff[
60
]
+
buff[
61
]
*
256
+
buff[
62
]
*
256
*
256
+
buff[
63
]
*
256
*
256
*
256
;
offset
=
offset
+
0x18
;
offset
=
offset
+
0x60
;
offset
=
offset
+
0x8
;
IATaddr
=
buff[offset]
+
buff[offset
+
1
]
*
256
+
buff[offset
+
2
]
*
256
*
256
+
buff[offset
+
3
]
*
256
*
256
*
256
;
addr
=
bkPNode
-
>me32.modBaseAddr
+
IATaddr;
/
/
根据PE文件结构获取进程IAT表地址
flag
=
ReadProcessMemory(handle, addr, buff,
1024
, readBuffCount);
/
/
读取进程IAT表所在内存的
1024
字节信息
descriptorBaseAddr
=
0
;
for
(order
=
0
;;)
{
/
/
根据IMAGE_INPORT_DESCRIPTOR结构,获取相应dll名地址,函数名地址列表首地址,函数地址列表首地址
funcNameAddr
=
buff[descriptorBaseAddr
+
0
]
+
buff[descriptorBaseAddr
+
1
]
*
256
+
buff[descriptorBaseAddr
+
2
]
*
256
*
256
+
buff[descriptorBaseAddr
+
3
]
*
256
*
256
*
256
;
dllNameAddr
=
buff[descriptorBaseAddr
+
12
]
+
buff[descriptorBaseAddr
+
13
]
*
256
+
buff[descriptorBaseAddr
+
14
]
*
256
*
256
+
buff[descriptorBaseAddr
+
15
]
*
256
*
256
*
256
;
funcAddrAddr
=
buff[descriptorBaseAddr
+
16
]
+
buff[descriptorBaseAddr
+
17
]
*
256
+
buff[descriptorBaseAddr
+
18
]
*
256
*
256
+
buff[descriptorBaseAddr
+
19
]
*
256
*
256
*
256
;
/
/
读取函数名地址列表
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcNameAddr, nameAddrBuff,
512
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcNameAddr failed!!\nError : %d\n"
,error);
return
-
1
;
}
/
/
读取函数地址列表
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcAddrAddr, addrBuff,
512
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcAddrAddr failed!!\nError : %d\n"
,error);
return
-
1
;
}
/
/
读取dll文件名
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
dllNameAddr, nameBuff, NAMESIZE
-
1
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcName failed!!\nError : %d\n"
,error);
return
-
1
;
}
for
(j
=
0
; j < NAMESIZE
-
1
; j
+
+
)
{
if
(
0
=
=
nameBuff[j])
{
break
;
}
else
{
dllName[j]
=
nameBuff[j];
}
}
dllName[j]
=
0
;
for
(i
=
0
;;)
/
/
循环获取IAT表项
{
bkINode
-
>order
=
order;
/
/
函数序号
order
+
+
;
strcpy(bkINode
-
>dllname,dllName);
/
/
函数所属dll名
bkINode
-
>addrOfAddr
=
funcAddrAddr
+
i;
/
/
函数地址所在内存地址
/
/
获取函数名所在内存首地址
funcName
=
nameAddrBuff[i]
+
nameAddrBuff[i
+
1
]
*
256
+
nameAddrBuff[i
+
2
]
*
256
*
256
+
nameAddrBuff[i
+
3
]
*
256
*
256
*
256
;
if
(
0x80000000
=
=
(
0x80000000
&funcName))
/
/
如果函数名所在地址最高位为
1
,则说明是以序号导入的
{
sprintf(bkINode
-
>name,
"Oridinal : %#0 8X"
,
0x7fffffff
&funcName);
bkINode
-
>address
=
funcName;
/
/
这种导入方式我不知道地址是多少
}
else
{
/
/
读取函数名
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcName, nameBuff, NAMESIZE
-
1
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcName failed!!\nError : %d\n"
,error);
return
-
1
;
}
/
/
获得函数名
for
(j
=
0
; j < NAMESIZE
-
1
; j
+
+
)
{
if
(
0
=
=
nameBuff[j
+
2
])
{
break
;
}
else
{
bkINode
-
>name[j]
=
nameBuff[j
+
2
];
}
}
bkINode
-
>name[j]
=
0
;
/
/
获取函数在内存中的地址
bkINode
-
>address
=
addrBuff[i]
+
addrBuff[i
+
1
]
*
256
+
addrBuff[i
+
2
]
*
256
*
256
+
addrBuff[i
+
3
]
*
256
*
256
*
256
;
}
i
=
i
+
4
;
/
/
如果下个函数名地址为
0
,则说明这个dll的导入函数结束了
if
(
0
=
=
nameAddrBuff[i] &&
0
=
=
nameAddrBuff[i
+
1
] &&
0
=
=
nameAddrBuff[i
+
2
] &&
0
=
=
nameAddrBuff[i
+
3
])
{
break
;
}
if
(
512
=
=
i)
/
/
如果函数名地址列表超过
512
字节,则重新获取函数名地址列表和函数地址列表
{
i
=
0
;
funcNameAddr
+
=
512
;
/
/
指针向前移
51
字节
funcAddrAddr
+
=
512
;
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcNameAddr, nameAddrBuff,
512
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcNameAddr failed!!\nError : %d\n"
,error);
return
-
1
;
}
funcName
=
nameAddrBuff[
0
]
+
nameAddrBuff[
1
]
*
256
+
nameAddrBuff[
2
]
*
256
*
256
+
nameAddrBuff[
3
]
*
256
*
256
*
256
;
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcAddrAddr, addrBuff,
512
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcAddrAddr failed!!\nError : %d\n"
,error);
return
-
1
;
}
}
InitINode(&newINode);
bkINode
-
>
next
=
newINode;
bkINode
=
newINode;
newINode
=
NULL;
}
descriptorBaseAddr
+
=
20
;
/
/
如果下一个IMAGE_IMPORT_DESCRIPTOR结构体为空,则退出
if
(
0
=
=
buff[descriptorBaseAddr] &&
0
=
=
buff[descriptorBaseAddr
+
1
] &&
0
=
=
buff[descriptorBaseAddr
+
2
] &&
0
=
=
buff[descriptorBaseAddr
+
3
])
{
break
;
}
InitINode(&newINode);
bkINode
-
>
next
=
newINode;
bkINode
=
newINode;
newINode
=
NULL;
}
CloseHandle(handle);
return
0
;
}
/
*
函数说明:
hooking 某个IAT表中的函数
输入参数:
INode
*
*
iNode : IAT表项结构体二级指针
PNode
*
pNode : 进程结构体指针
int
order : 函数序号
unsigned
int
pid : 进程PID
输出参数:
*
/
int
IATHook(INode
*
iNode, PNode
*
pNode,
int
order, unsigned
int
pid)
{
char addr[
5
]
=
{
0
};
/
/
保存四字节地址信息
INode
*
bkINode
=
iNode;
/
/
初始化IAT表项结构体链表操作指针
HANDLE hProcess;
/
/
进程句柄
DWORD dwHasWrite;
/
/
实际读取的字节数
LPVOID lpRemoteBuf;
/
/
新申请的内存空间指针
int
temp
=
0
;
/
/
临时变量
/
/
数据
char data[]
=
"\x74\x65\x73\x74\x00\xCC\xCC\xCC"
"\xD7\xE9\xB3\xA4\x20\x3A\x20\xBA"
"\xCE\xC4\xDC\xB1\xF3\x20\x32\x30"
"\x31\x33\x33\x30\x32\x35\x33\x30"
"\x30\x32\x30\x0A\xD7\xE9\xD4\xB1"
"\x20\x3A\x20\xCD\xF5\x20\x20\xEC"
"\xB3\x20\x32\x30\x31\x33\x33\x30"
"\x32\x35\x33\x30\x30\x30\x35\x0A"
"\x20\x20\x20\x20\x20\x20\x20\xB5"
"\xCB\xB9\xE3\xF6\xCE\x20\x32\x30"
"\x31\x33\x33\x30\x32\x35\x33\x30"
"\x30\x31\x34\x0A\x20\x20\x20\x20"
"\x20\x20\x20\xB9\xA8\xD3\xF1\xB7"
"\xEF\x20\x32\x30\x31\x33\x33\x30"
"\x32\x35\x33\x30\x30\x32\x31\x00"
;
/
/
shellcode
char shellcode[]
=
"\x9C\x50\x51\x52\x53\x55\x56\x57"
"\x6A\x00\x68\x00\x10\x40\x00\x68"
"\x00\x10\x40\x00\x6A\x00\xB8\xEA"
"\x07\xD5\x77\xFF\xD0\x5F\x5E\x5D"
"\x5B\x5A\x59\x58\x9D\xB8\xEA\x07"
"\xD5\x7C\xFF\xE0"
;
/
/
循环遍历IAT表项结构体链表,寻找与所给函数序号相同的IAT表项结构体
if
(NULL
=
=
iNode)
{
return
-
1
;
}
for
(;;)
{
if
(NULL
=
=
iNode
-
>
next
)
{
if
(iNode
-
>order
=
=
order)
{
break
;
}
else
{
return
-
1
;
}
}
else
{
if
(iNode
-
>order
=
=
order)
{
break
;
}
else
{
iNode
=
iNode
-
>
next
;
}
}
}
/
/
循环遍历IAT表项结构体链表,寻找MessageBoxA的IAT表项结构体
for
(;;)
{
if
(NULL
=
=
bkINode
-
>
next
)
{
if
(
0
=
=
strcmp(bkINode
-
>name,
"MessageBoxA"
))
{
break
;
}
else
{
return
-
1
;
}
}
else
{
if
(
0
=
=
strcmp(bkINode
-
>name,
"MessageBoxA"
))
{
break
;
}
else
{
bkINode
=
bkINode
-
>
next
;
}
}
}
/
/
循环遍历进程结构体链表,寻找与所给函数所属进程PID相同的进程结构体
if
(NULL
=
=
pNode)
{
return
-
1
;
}
for
(;;)
{
if
(pid
=
=
pNode
-
>pe32.th32ProcessID)
{
break
;
}
else
{
if
(NULL
=
=
pNode
-
>
next
)
{
return
-
1
;
}
else
{
pNode
=
pNode
-
>
next
;
}
}
}
if
(EnableDebugPriv(SE_DEBUG_NAME))
/
/
获取调试权限
{
fprintf(stderr,
"Add Privilege error\n"
);
return
-
1
;
}
hProcess
=
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
/
/
获取进程句柄
if
(hProcess
=
=
NULL)
{
fprintf(stderr,
"\n获取进程句柄错误%d"
,GetLastError());
return
-
1
;
}
/
/
申请
120
字节的数据空间,并写入我们需要的数据
lpRemoteBuf
=
VirtualAllocEx(hProcess, NULL,
120
, MEM_COMMIT, PAGE_READWRITE);
if
(WriteProcessMemory(hProcess, lpRemoteBuf, data,
120
, &dwHasWrite))
{
if
(dwHasWrite !
=
120
)
{
VirtualFreeEx(hProcess,lpRemoteBuf,
120
,MEM_COMMIT);
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
temp
=
(
int
)lpRemoteBuf;
/
/
数据所在首地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
11
]
=
addr[
0
];
/
/
"test"
的地址
shellcode[
12
]
=
addr[
1
];
shellcode[
13
]
=
addr[
2
];
shellcode[
14
]
=
addr[
3
];
shellcode[
16
]
=
addr[
0
]
+
8
;
/
/
"所要显示的字符串首地址"
shellcode[
17
]
=
addr[
1
];
shellcode[
18
]
=
addr[
2
];
shellcode[
19
]
=
addr[
3
];
temp
=
(
int
)bkINode
-
>address;
/
/
MessageBoxA的地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
23
]
=
addr[
0
];
shellcode[
24
]
=
addr[
1
];
shellcode[
25
]
=
addr[
2
];
shellcode[
26
]
=
addr[
3
];
temp
=
(
int
)iNode
-
>address;
/
/
原函数的地址,用于jmp回原来的函数
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
38
]
=
addr[
0
];
shellcode[
39
]
=
addr[
1
];
shellcode[
40
]
=
addr[
2
];
shellcode[
41
]
=
addr[
3
];
/
/
申请
44
字节的可读可写可执行的shellcode空间,并写入shellcode
lpRemoteBuf
=
VirtualAllocEx(hProcess, NULL,
44
, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if
(WriteProcessMemory(hProcess, lpRemoteBuf, shellcode,
44
, &dwHasWrite))
{
if
(dwHasWrite !
=
44
)
{
VirtualFreeEx(hProcess,lpRemoteBuf,
44
,MEM_COMMIT);
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
temp
=
(
int
)lpRemoteBuf;
/
/
获取shellcode的首地址,并替换IAT表中相应的函数地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
if
(WriteProcessMemory(hProcess, pNode
-
>me32.modBaseAddr
+
iNode
-
>addrOfAddr, addr,
4
, &dwHasWrite))
{
return
0
;
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
}
CloseHandle(hProcess);
return
-
1
;
}
/
*
函数说明:
inline hooking 某个IAT表中的函数
输入参数:
INode
*
*
iNode : IAT表项结构体二级指针
PNode
*
pNode : 进程结构体指针
int
order : 函数序号
unsigned
int
pid : 进程PID
输出参数:
*
/
int
InlineHook(INode
*
iNode, PNode
*
pNode,
int
order, unsigned
int
pid)
{
char addr[
5
]
=
{
0
};
/
/
用于保存
4
字节的地址
char buff[
6
]
=
{
0
};
/
/
用于保存jmp xxx指令和所要hook的函数起始五个字节
INode
*
bkINode
=
iNode;
/
/
初始化IAT表项结构体链表操作指针
HANDLE hProcess;
/
/
进程句柄
DWORD dwHasWrite;
/
/
实际写入的字节数
LPVOID lpRemoteBuf;
/
/
申请的内存首地址
int
temp
=
0
;
/
/
临时变量
/
/
数据
char data[]
=
"\x74\x65\x73\x74\x00\xCC\xCC\xCC"
"\xD7\xE9\xB3\xA4\x20\x3A\x20\xBA"
"\xCE\xC4\xDC\xB1\xF3\x20\x32\x30"
"\x31\x33\x33\x30\x32\x35\x33\x30"
"\x30\x32\x30\x0A\xD7\xE9\xD4\xB1"
"\x20\x3A\x20\xCD\xF5\x20\x20\xEC"
"\xB3\x20\x32\x30\x31\x33\x33\x30"
"\x32\x35\x33\x30\x30\x30\x35\x0A"
"\x20\x20\x20\x20\x20\x20\x20\xB5"
"\xCB\xB9\xE3\xF6\xCE\x20\x32\x30"
"\x31\x33\x33\x30\x32\x35\x33\x30"
"\x30\x31\x34\x0A\x20\x20\x20\x20"
"\x20\x20\x20\xB9\xA8\xD3\xF1\xB7"
"\xEF\x20\x32\x30\x31\x33\x33\x30"
"\x32\x35\x33\x30\x30\x32\x31\x00"
;
/
/
shellcode
char shellcode[]
=
"\x9C\x50\x51\x52\x53\x55\x56\x57"
"\x6A\x00\x68\x00\x10\x40\x00\x68"
"\x00\x10\x40\x00\x6A\x00\xB8\xEA"
"\x07\xD5\x77\xFF\xD0\x5F\x5E\x5D"
"\x5B\x5A\x59\x58\x9D\x8b\xff\x55\x8b\xec"
/
/
shellco中有所要hooking的函数前五个字节了
"\xe9\x90\x90\x90\x90"
;
/
/
所以后面jmp 回到的是函数的第六个字节
if
(NULL
=
=
iNode)
/
/
如果IAT表项链表为空,则退出
{
return
-
1
;
}
for
(;;)
{
if
(NULL
=
=
iNode
-
>
next
)
{
if
(iNode
-
>order
=
=
order)
{
break
;
}
else
{
return
-
1
;
}
}
else
{
if
(iNode
-
>order
=
=
order)
{
break
;
}
else
{
iNode
=
iNode
-
>
next
;
}
}
}
/
/
获取MessageBoxA的IAT表项结构体
for
(;;)
{
if
(NULL
=
=
bkINode
-
>
next
)
{
if
(
0
=
=
strcmp(bkINode
-
>name,
"MessageBoxA"
))
{
break
;
}
else
{
return
-
1
;
}
}
else
{
if
(
0
=
=
strcmp(bkINode
-
>name,
"MessageBoxA"
))
{
break
;
}
else
{
bkINode
=
bkINode
-
>
next
;
}
}
}
/
/
获取所要hook的函数所属进程结构体
if
(NULL
=
=
pNode)
{
return
-
1
;
}
for
(;;)
{
if
(pid
=
=
pNode
-
>pe32.th32ProcessID)
{
break
;
}
else
{
if
(NULL
=
=
pNode
-
>
next
)
{
return
-
1
;
}
else
{
pNode
=
pNode
-
>
next
;
}
}
}
/
/
获取调试权限
if
(EnableDebugPriv(SE_DEBUG_NAME))
{
fprintf(stderr,
"Add Privilege error\n"
);
return
-
1
;
}
/
/
获取进程句柄
hProcess
=
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if
(hProcess
=
=
NULL)
{
fprintf(stderr,
"\n获取进程句柄错误%d"
,GetLastError());
return
-
1
;
}
/
/
读取所要hook的函数前五个字节
if
(ReadProcessMemory(hProcess, iNode
-
>address, buff,
5
, &dwHasWrite))
{
if
(dwHasWrite !
=
5
)
{
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n读取远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
/
/
如果函数前五个字节不是 mov edi,edi push ebp mov ebp,esp则退出inline hooking
if
(
0
!
=
strcmp(buff,
"\x8b\xff\x55\x8b\xec"
))
{
return
-
1
;
}
/
/
申请
120
字节的数据空间
lpRemoteBuf
=
VirtualAllocEx(hProcess, NULL,
120
, MEM_COMMIT, PAGE_READWRITE);
if
(WriteProcessMemory(hProcess, lpRemoteBuf, data,
120
, &dwHasWrite))
{
if
(dwHasWrite !
=
120
)
{
VirtualFreeEx(hProcess,lpRemoteBuf,
120
,MEM_COMMIT);
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
temp
=
(
int
)lpRemoteBuf;
/
/
获取数据在内存中的首地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
11
]
=
addr[
0
];
/
/
"test"
的首地址
shellcode[
12
]
=
addr[
1
];
shellcode[
13
]
=
addr[
2
];
shellcode[
14
]
=
addr[
3
];
shellcode[
16
]
=
addr[
0
]
+
8
;
/
/
所要显示的字符串首地址
shellcode[
17
]
=
addr[
1
];
shellcode[
18
]
=
addr[
2
];
shellcode[
19
]
=
addr[
3
];
temp
=
(
int
)bkINode
-
>address;
/
/
MessageBoxA的地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
23
]
=
addr[
0
];
shellcode[
24
]
=
addr[
1
];
shellcode[
25
]
=
addr[
2
];
shellcode[
26
]
=
addr[
3
];
/
/
先写入
42
字节的shellcode
lpRemoteBuf
=
VirtualAllocEx(hProcess, NULL,
42
, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if
(WriteProcessMemory(hProcess, lpRemoteBuf, shellcode,
42
, &dwHasWrite))
{
if
(dwHasWrite !
=
42
)
{
VirtualFreeEx(hProcess,lpRemoteBuf,
42
,MEM_COMMIT);
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
temp
=
(
int
)lpRemoteBuf;
/
/
获得shellcode的首地址
temp
=
temp
-
iNode
-
>address
-
5
;
/
/
计算jmp到shellcode的偏移
buff[
0
]
=
0xe9
;
buff[
1
]
=
temp&
0xff
;
buff[
2
]
=
temp>>
8
&
0xff
;
buff[
3
]
=
temp>>
16
&
0xff
;
buff[
4
]
=
temp>>
24
&
0xff
;
/
/
得到jmp xxx的二进制数据并写入函数的其实五个字节
if
(!WriteProcessMemory(hProcess, iNode
-
>address, buff,
5
, &dwHasWrite))
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
}
temp
=
(
int
)lpRemoteBuf;
/
/
获取shellcode的地址
temp
=
temp
+
47
;
/
/
得到shellcode中jmp xx的下条指令的地址
temp
=
iNode
-
>address
-
temp
+
5
;
/
/
得到jmp回原来函数第六个字节的起始地址
buff[
0
]
=
0xe9
;
buff[
1
]
=
temp&
0xff
;
buff[
2
]
=
temp>>
8
&
0xff
;
buff[
3
]
=
temp>>
16
&
0xff
;
buff[
4
]
=
temp>>
24
&
0xff
;
temp
=
(
int
)lpRemoteBuf
+
42
;
/
/
得到jmp xxx在shellcode中的地址,并写入shellcode最后五个字节
if
(WriteProcessMemory(hProcess,temp , buff,
5
, &dwHasWrite))
{
return
0
;
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
}
CloseHandle(hProcess);
return
-
1
;
}
/
*
函数说明:
获取进程IAT列表
输入参数:
INode
*
*
iNode : IAT表项结构体二级指针
PNode
*
pNode : 进程结构体指针
unsigned
int
pid : 进程PID
输出参数:
*
/
int
GetIAT(INode
*
*
iNode, PNode
*
pNode, unsigned
int
pid)
{
unsigned char buff[
1025
]
=
{
0
};
/
/
用于临时保存读取的buff
unsigned char nameAddrBuff[
513
]
=
{
0
};
/
/
IAT表项函数名地址列表
unsigned char addrBuff[
513
]
=
{
0
};
/
/
IAT表项函数地址列表
char dllName[NAMESIZE]
=
{
0
};
/
/
IAT表项所属dll名
unsigned char nameBuff[NAMESIZE]
=
{
0
};
/
/
IAT表项函数名
PNode
*
bkPNode
=
pNode;
/
/
初始化进程结构体链表操作指针
INode
*
bkINode
=
NULL;
/
/
定义IAT表项结构体操作指针
INode
*
newINode
=
NULL;
/
/
定义新的IAT表项结构体指针
HANDLE handle
=
NULL;
/
/
初始化进程句柄
LPCVOID addr
=
0
;
/
/
地址指针
int
offset
=
0
;
/
/
保存PE结构偏移
LPDWORD readBuffCount
=
0
;
/
/
保存ReadProcessMemory实际读取的字节数
int
flag
=
0
;
/
/
函数调用标记
int
error
=
0
;
/
/
函数调用出错代码
int
order
=
0
;
/
/
函数在列表中的序号
int
IATaddr
=
0
;
/
/
IAT表的地址
int
descriptorBaseAddr
=
0
;
/
/
IMAGE_IMPORT_DESCRIPTOR结构体首地址
int
dllNameAddr
=
0
;
/
/
dll名地址
int
funcNameAddr
=
0
;
/
/
函数名列表地址
int
funcAddrAddr
=
0
;
/
/
函数地址列表地址
int
funcName
=
0
;
/
/
函数名地址
int
i
=
0
;
/
/
循环计数
int
j
=
0
;
/
/
循环变量
InitINode(iNode);
/
/
初始化IAT表项结构体链表头指针
bkINode
=
*
iNode;
/
/
初始化IAT表项结构体链表操作指针
if
(NULL
=
=
bkPNode)
/
/
如果进程链表为空,则出错退出
{
return
-
1
;
}
for
(;;)
/
/
循环遍历进程结构体中与所给进程PID相符进程结构体
{
if
(pid
=
=
bkPNode
-
>pe32.th32ProcessID)
{
break
;
}
else
{
if
(NULL
=
=
bkPNode
-
>
next
)
{
return
-
1
;
}
else
{
bkPNode
=
bkPNode
-
>
next
;
}
}
}
if
(EnableDebugPriv(SE_DEBUG_NAME))
/
/
获取进程调试权限
{
printf(
"Add Privilege error\n"
);
return
-
1
;
}
handle
=
OpenProcess(PROCESS_ALL_ACCESS,
1
,pid);
/
/
获取进程句柄
if
(handle
=
=
INVALID_HANDLE_VALUE)
{
return
-
1
;
}
addr
=
bkPNode
-
>me32.modBaseAddr;
/
/
获取进程加载基址
flag
=
ReadProcessMemory(handle, addr, buff,
512
, readBuffCount);
/
/
读取进程前
512
字节信息
offset
=
buff[
60
]
+
buff[
61
]
*
256
+
buff[
62
]
*
256
*
256
+
buff[
63
]
*
256
*
256
*
256
;
offset
=
offset
+
0x18
;
offset
=
offset
+
0x60
;
offset
=
offset
+
0x8
;
IATaddr
=
buff[offset]
+
buff[offset
+
1
]
*
256
+
buff[offset
+
2
]
*
256
*
256
+
buff[offset
+
3
]
*
256
*
256
*
256
;
addr
=
bkPNode
-
>me32.modBaseAddr
+
IATaddr;
/
/
根据PE文件结构获取进程IAT表地址
flag
=
ReadProcessMemory(handle, addr, buff,
1024
, readBuffCount);
/
/
读取进程IAT表所在内存的
1024
字节信息
descriptorBaseAddr
=
0
;
for
(order
=
0
;;)
{
/
/
根据IMAGE_INPORT_DESCRIPTOR结构,获取相应dll名地址,函数名地址列表首地址,函数地址列表首地址
funcNameAddr
=
buff[descriptorBaseAddr
+
0
]
+
buff[descriptorBaseAddr
+
1
]
*
256
+
buff[descriptorBaseAddr
+
2
]
*
256
*
256
+
buff[descriptorBaseAddr
+
3
]
*
256
*
256
*
256
;
dllNameAddr
=
buff[descriptorBaseAddr
+
12
]
+
buff[descriptorBaseAddr
+
13
]
*
256
+
buff[descriptorBaseAddr
+
14
]
*
256
*
256
+
buff[descriptorBaseAddr
+
15
]
*
256
*
256
*
256
;
funcAddrAddr
=
buff[descriptorBaseAddr
+
16
]
+
buff[descriptorBaseAddr
+
17
]
*
256
+
buff[descriptorBaseAddr
+
18
]
*
256
*
256
+
buff[descriptorBaseAddr
+
19
]
*
256
*
256
*
256
;
/
/
读取函数名地址列表
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcNameAddr, nameAddrBuff,
512
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcNameAddr failed!!\nError : %d\n"
,error);
return
-
1
;
}
/
/
读取函数地址列表
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcAddrAddr, addrBuff,
512
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcAddrAddr failed!!\nError : %d\n"
,error);
return
-
1
;
}
/
/
读取dll文件名
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
dllNameAddr, nameBuff, NAMESIZE
-
1
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcName failed!!\nError : %d\n"
,error);
return
-
1
;
}
for
(j
=
0
; j < NAMESIZE
-
1
; j
+
+
)
{
if
(
0
=
=
nameBuff[j])
{
break
;
}
else
{
dllName[j]
=
nameBuff[j];
}
}
dllName[j]
=
0
;
for
(i
=
0
;;)
/
/
循环获取IAT表项
{
bkINode
-
>order
=
order;
/
/
函数序号
order
+
+
;
strcpy(bkINode
-
>dllname,dllName);
/
/
函数所属dll名
bkINode
-
>addrOfAddr
=
funcAddrAddr
+
i;
/
/
函数地址所在内存地址
/
/
获取函数名所在内存首地址
funcName
=
nameAddrBuff[i]
+
nameAddrBuff[i
+
1
]
*
256
+
nameAddrBuff[i
+
2
]
*
256
*
256
+
nameAddrBuff[i
+
3
]
*
256
*
256
*
256
;
if
(
0x80000000
=
=
(
0x80000000
&funcName))
/
/
如果函数名所在地址最高位为
1
,则说明是以序号导入的
{
sprintf(bkINode
-
>name,
"Oridinal : %#0 8X"
,
0x7fffffff
&funcName);
bkINode
-
>address
=
funcName;
/
/
这种导入方式我不知道地址是多少
}
else
{
/
/
读取函数名
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcName, nameBuff, NAMESIZE
-
1
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcName failed!!\nError : %d\n"
,error);
return
-
1
;
}
/
/
获得函数名
for
(j
=
0
; j < NAMESIZE
-
1
; j
+
+
)
{
if
(
0
=
=
nameBuff[j
+
2
])
{
break
;
}
else
{
bkINode
-
>name[j]
=
nameBuff[j
+
2
];
}
}
bkINode
-
>name[j]
=
0
;
/
/
获取函数在内存中的地址
bkINode
-
>address
=
addrBuff[i]
+
addrBuff[i
+
1
]
*
256
+
addrBuff[i
+
2
]
*
256
*
256
+
addrBuff[i
+
3
]
*
256
*
256
*
256
;
}
i
=
i
+
4
;
/
/
如果下个函数名地址为
0
,则说明这个dll的导入函数结束了
if
(
0
=
=
nameAddrBuff[i] &&
0
=
=
nameAddrBuff[i
+
1
] &&
0
=
=
nameAddrBuff[i
+
2
] &&
0
=
=
nameAddrBuff[i
+
3
])
{
break
;
}
if
(
512
=
=
i)
/
/
如果函数名地址列表超过
512
字节,则重新获取函数名地址列表和函数地址列表
{
i
=
0
;
funcNameAddr
+
=
512
;
/
/
指针向前移
51
字节
funcAddrAddr
+
=
512
;
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcNameAddr, nameAddrBuff,
512
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcNameAddr failed!!\nError : %d\n"
,error);
return
-
1
;
}
funcName
=
nameAddrBuff[
0
]
+
nameAddrBuff[
1
]
*
256
+
nameAddrBuff[
2
]
*
256
*
256
+
nameAddrBuff[
3
]
*
256
*
256
*
256
;
flag
=
ReadProcessMemory(handle, bkPNode
-
>me32.modBaseAddr
+
funcAddrAddr, addrBuff,
512
, readBuffCount);
if
(
0
=
=
flag)
{
error
=
GetLastError();
printf(
"Read funcAddrAddr failed!!\nError : %d\n"
,error);
return
-
1
;
}
}
InitINode(&newINode);
bkINode
-
>
next
=
newINode;
bkINode
=
newINode;
newINode
=
NULL;
}
descriptorBaseAddr
+
=
20
;
/
/
如果下一个IMAGE_IMPORT_DESCRIPTOR结构体为空,则退出
if
(
0
=
=
buff[descriptorBaseAddr] &&
0
=
=
buff[descriptorBaseAddr
+
1
] &&
0
=
=
buff[descriptorBaseAddr
+
2
] &&
0
=
=
buff[descriptorBaseAddr
+
3
])
{
break
;
}
InitINode(&newINode);
bkINode
-
>
next
=
newINode;
bkINode
=
newINode;
newINode
=
NULL;
}
CloseHandle(handle);
return
0
;
}
/
*
函数说明:
hooking 某个IAT表中的函数
输入参数:
INode
*
*
iNode : IAT表项结构体二级指针
PNode
*
pNode : 进程结构体指针
int
order : 函数序号
unsigned
int
pid : 进程PID
输出参数:
*
/
int
IATHook(INode
*
iNode, PNode
*
pNode,
int
order, unsigned
int
pid)
{
char addr[
5
]
=
{
0
};
/
/
保存四字节地址信息
INode
*
bkINode
=
iNode;
/
/
初始化IAT表项结构体链表操作指针
HANDLE hProcess;
/
/
进程句柄
DWORD dwHasWrite;
/
/
实际读取的字节数
LPVOID lpRemoteBuf;
/
/
新申请的内存空间指针
int
temp
=
0
;
/
/
临时变量
/
/
数据
char data[]
=
"\x74\x65\x73\x74\x00\xCC\xCC\xCC"
"\xD7\xE9\xB3\xA4\x20\x3A\x20\xBA"
"\xCE\xC4\xDC\xB1\xF3\x20\x32\x30"
"\x31\x33\x33\x30\x32\x35\x33\x30"
"\x30\x32\x30\x0A\xD7\xE9\xD4\xB1"
"\x20\x3A\x20\xCD\xF5\x20\x20\xEC"
"\xB3\x20\x32\x30\x31\x33\x33\x30"
"\x32\x35\x33\x30\x30\x30\x35\x0A"
"\x20\x20\x20\x20\x20\x20\x20\xB5"
"\xCB\xB9\xE3\xF6\xCE\x20\x32\x30"
"\x31\x33\x33\x30\x32\x35\x33\x30"
"\x30\x31\x34\x0A\x20\x20\x20\x20"
"\x20\x20\x20\xB9\xA8\xD3\xF1\xB7"
"\xEF\x20\x32\x30\x31\x33\x33\x30"
"\x32\x35\x33\x30\x30\x32\x31\x00"
;
/
/
shellcode
char shellcode[]
=
"\x9C\x50\x51\x52\x53\x55\x56\x57"
"\x6A\x00\x68\x00\x10\x40\x00\x68"
"\x00\x10\x40\x00\x6A\x00\xB8\xEA"
"\x07\xD5\x77\xFF\xD0\x5F\x5E\x5D"
"\x5B\x5A\x59\x58\x9D\xB8\xEA\x07"
"\xD5\x7C\xFF\xE0"
;
/
/
循环遍历IAT表项结构体链表,寻找与所给函数序号相同的IAT表项结构体
if
(NULL
=
=
iNode)
{
return
-
1
;
}
for
(;;)
{
if
(NULL
=
=
iNode
-
>
next
)
{
if
(iNode
-
>order
=
=
order)
{
break
;
}
else
{
return
-
1
;
}
}
else
{
if
(iNode
-
>order
=
=
order)
{
break
;
}
else
{
iNode
=
iNode
-
>
next
;
}
}
}
/
/
循环遍历IAT表项结构体链表,寻找MessageBoxA的IAT表项结构体
for
(;;)
{
if
(NULL
=
=
bkINode
-
>
next
)
{
if
(
0
=
=
strcmp(bkINode
-
>name,
"MessageBoxA"
))
{
break
;
}
else
{
return
-
1
;
}
}
else
{
if
(
0
=
=
strcmp(bkINode
-
>name,
"MessageBoxA"
))
{
break
;
}
else
{
bkINode
=
bkINode
-
>
next
;
}
}
}
/
/
循环遍历进程结构体链表,寻找与所给函数所属进程PID相同的进程结构体
if
(NULL
=
=
pNode)
{
return
-
1
;
}
for
(;;)
{
if
(pid
=
=
pNode
-
>pe32.th32ProcessID)
{
break
;
}
else
{
if
(NULL
=
=
pNode
-
>
next
)
{
return
-
1
;
}
else
{
pNode
=
pNode
-
>
next
;
}
}
}
if
(EnableDebugPriv(SE_DEBUG_NAME))
/
/
获取调试权限
{
fprintf(stderr,
"Add Privilege error\n"
);
return
-
1
;
}
hProcess
=
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
/
/
获取进程句柄
if
(hProcess
=
=
NULL)
{
fprintf(stderr,
"\n获取进程句柄错误%d"
,GetLastError());
return
-
1
;
}
/
/
申请
120
字节的数据空间,并写入我们需要的数据
lpRemoteBuf
=
VirtualAllocEx(hProcess, NULL,
120
, MEM_COMMIT, PAGE_READWRITE);
if
(WriteProcessMemory(hProcess, lpRemoteBuf, data,
120
, &dwHasWrite))
{
if
(dwHasWrite !
=
120
)
{
VirtualFreeEx(hProcess,lpRemoteBuf,
120
,MEM_COMMIT);
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
temp
=
(
int
)lpRemoteBuf;
/
/
数据所在首地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
11
]
=
addr[
0
];
/
/
"test"
的地址
shellcode[
12
]
=
addr[
1
];
shellcode[
13
]
=
addr[
2
];
shellcode[
14
]
=
addr[
3
];
shellcode[
16
]
=
addr[
0
]
+
8
;
/
/
"所要显示的字符串首地址"
shellcode[
17
]
=
addr[
1
];
shellcode[
18
]
=
addr[
2
];
shellcode[
19
]
=
addr[
3
];
temp
=
(
int
)bkINode
-
>address;
/
/
MessageBoxA的地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
23
]
=
addr[
0
];
shellcode[
24
]
=
addr[
1
];
shellcode[
25
]
=
addr[
2
];
shellcode[
26
]
=
addr[
3
];
temp
=
(
int
)iNode
-
>address;
/
/
原函数的地址,用于jmp回原来的函数
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
38
]
=
addr[
0
];
shellcode[
39
]
=
addr[
1
];
shellcode[
40
]
=
addr[
2
];
shellcode[
41
]
=
addr[
3
];
/
/
申请
44
字节的可读可写可执行的shellcode空间,并写入shellcode
lpRemoteBuf
=
VirtualAllocEx(hProcess, NULL,
44
, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if
(WriteProcessMemory(hProcess, lpRemoteBuf, shellcode,
44
, &dwHasWrite))
{
if
(dwHasWrite !
=
44
)
{
VirtualFreeEx(hProcess,lpRemoteBuf,
44
,MEM_COMMIT);
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
temp
=
(
int
)lpRemoteBuf;
/
/
获取shellcode的首地址,并替换IAT表中相应的函数地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
if
(WriteProcessMemory(hProcess, pNode
-
>me32.modBaseAddr
+
iNode
-
>addrOfAddr, addr,
4
, &dwHasWrite))
{
return
0
;
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
}
CloseHandle(hProcess);
return
-
1
;
}
/
*
函数说明:
inline hooking 某个IAT表中的函数
输入参数:
INode
*
*
iNode : IAT表项结构体二级指针
PNode
*
pNode : 进程结构体指针
int
order : 函数序号
unsigned
int
pid : 进程PID
输出参数:
*
/
int
InlineHook(INode
*
iNode, PNode
*
pNode,
int
order, unsigned
int
pid)
{
char addr[
5
]
=
{
0
};
/
/
用于保存
4
字节的地址
char buff[
6
]
=
{
0
};
/
/
用于保存jmp xxx指令和所要hook的函数起始五个字节
INode
*
bkINode
=
iNode;
/
/
初始化IAT表项结构体链表操作指针
HANDLE hProcess;
/
/
进程句柄
DWORD dwHasWrite;
/
/
实际写入的字节数
LPVOID lpRemoteBuf;
/
/
申请的内存首地址
int
temp
=
0
;
/
/
临时变量
/
/
数据
char data[]
=
"\x74\x65\x73\x74\x00\xCC\xCC\xCC"
"\xD7\xE9\xB3\xA4\x20\x3A\x20\xBA"
"\xCE\xC4\xDC\xB1\xF3\x20\x32\x30"
"\x31\x33\x33\x30\x32\x35\x33\x30"
"\x30\x32\x30\x0A\xD7\xE9\xD4\xB1"
"\x20\x3A\x20\xCD\xF5\x20\x20\xEC"
"\xB3\x20\x32\x30\x31\x33\x33\x30"
"\x32\x35\x33\x30\x30\x30\x35\x0A"
"\x20\x20\x20\x20\x20\x20\x20\xB5"
"\xCB\xB9\xE3\xF6\xCE\x20\x32\x30"
"\x31\x33\x33\x30\x32\x35\x33\x30"
"\x30\x31\x34\x0A\x20\x20\x20\x20"
"\x20\x20\x20\xB9\xA8\xD3\xF1\xB7"
"\xEF\x20\x32\x30\x31\x33\x33\x30"
"\x32\x35\x33\x30\x30\x32\x31\x00"
;
/
/
shellcode
char shellcode[]
=
"\x9C\x50\x51\x52\x53\x55\x56\x57"
"\x6A\x00\x68\x00\x10\x40\x00\x68"
"\x00\x10\x40\x00\x6A\x00\xB8\xEA"
"\x07\xD5\x77\xFF\xD0\x5F\x5E\x5D"
"\x5B\x5A\x59\x58\x9D\x8b\xff\x55\x8b\xec"
/
/
shellco中有所要hooking的函数前五个字节了
"\xe9\x90\x90\x90\x90"
;
/
/
所以后面jmp 回到的是函数的第六个字节
if
(NULL
=
=
iNode)
/
/
如果IAT表项链表为空,则退出
{
return
-
1
;
}
for
(;;)
{
if
(NULL
=
=
iNode
-
>
next
)
{
if
(iNode
-
>order
=
=
order)
{
break
;
}
else
{
return
-
1
;
}
}
else
{
if
(iNode
-
>order
=
=
order)
{
break
;
}
else
{
iNode
=
iNode
-
>
next
;
}
}
}
/
/
获取MessageBoxA的IAT表项结构体
for
(;;)
{
if
(NULL
=
=
bkINode
-
>
next
)
{
if
(
0
=
=
strcmp(bkINode
-
>name,
"MessageBoxA"
))
{
break
;
}
else
{
return
-
1
;
}
}
else
{
if
(
0
=
=
strcmp(bkINode
-
>name,
"MessageBoxA"
))
{
break
;
}
else
{
bkINode
=
bkINode
-
>
next
;
}
}
}
/
/
获取所要hook的函数所属进程结构体
if
(NULL
=
=
pNode)
{
return
-
1
;
}
for
(;;)
{
if
(pid
=
=
pNode
-
>pe32.th32ProcessID)
{
break
;
}
else
{
if
(NULL
=
=
pNode
-
>
next
)
{
return
-
1
;
}
else
{
pNode
=
pNode
-
>
next
;
}
}
}
/
/
获取调试权限
if
(EnableDebugPriv(SE_DEBUG_NAME))
{
fprintf(stderr,
"Add Privilege error\n"
);
return
-
1
;
}
/
/
获取进程句柄
hProcess
=
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if
(hProcess
=
=
NULL)
{
fprintf(stderr,
"\n获取进程句柄错误%d"
,GetLastError());
return
-
1
;
}
/
/
读取所要hook的函数前五个字节
if
(ReadProcessMemory(hProcess, iNode
-
>address, buff,
5
, &dwHasWrite))
{
if
(dwHasWrite !
=
5
)
{
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n读取远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
/
/
如果函数前五个字节不是 mov edi,edi push ebp mov ebp,esp则退出inline hooking
if
(
0
!
=
strcmp(buff,
"\x8b\xff\x55\x8b\xec"
))
{
return
-
1
;
}
/
/
申请
120
字节的数据空间
lpRemoteBuf
=
VirtualAllocEx(hProcess, NULL,
120
, MEM_COMMIT, PAGE_READWRITE);
if
(WriteProcessMemory(hProcess, lpRemoteBuf, data,
120
, &dwHasWrite))
{
if
(dwHasWrite !
=
120
)
{
VirtualFreeEx(hProcess,lpRemoteBuf,
120
,MEM_COMMIT);
CloseHandle(hProcess);
return
-
1
;
}
}
else
{
printf(
"\n写入远程进程内存空间出错%d。"
,GetLastError());
CloseHandle(hProcess);
return
-
1
;
}
temp
=
(
int
)lpRemoteBuf;
/
/
获取数据在内存中的首地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
11
]
=
addr[
0
];
/
/
"test"
的首地址
shellcode[
12
]
=
addr[
1
];
shellcode[
13
]
=
addr[
2
];
shellcode[
14
]
=
addr[
3
];
shellcode[
16
]
=
addr[
0
]
+
8
;
/
/
所要显示的字符串首地址
shellcode[
17
]
=
addr[
1
];
shellcode[
18
]
=
addr[
2
];
shellcode[
19
]
=
addr[
3
];
temp
=
(
int
)bkINode
-
>address;
/
/
MessageBoxA的地址
addr[
0
]
=
temp&
0xff
;
addr[
1
]
=
temp>>
8
&
0xff
;
addr[
2
]
=
temp>>
16
&
0xff
;
addr[
3
]
=
temp>>
24
&
0xff
;
shellcode[
23
]
=
addr[
0
];
shellcode[
24
]
=
addr[
1
];
shellcode[
25
]
=
addr[
2
];
shellcode[
26
]
=
addr[
3
];
/
/
先写入
42
字节的shellcode
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]CVE-2017-11882分析和白象样本分析 8258
- 银狐样本分析 12921
- [原创]CS[1]exe木马分析 7752
- [原创]内核漏洞学习[6]-HEVD-UninitializedStackVariable 27928
- [原创]植物大战僵尸外挂实现 13096