能力值:
( LV13,RANK:400 )
2 楼
mov指令替换成
近跳转 0xFE
上面5个nop换成远跳到补丁函数所在地址。
能力值:
( LV2,RANK:10 )
3 楼
这就是针对标准的hot patch类型的api的hook;
对与非标准的没有给出方法
能力值:
( LV2,RANK:10 )
4 楼
我个人的处理办法
如果函数有Ansi/Unicode两个版本而且只需要监视Ansi版本的话,自己做字符串转换的操作(如果挂钩Unicode自己执行Ansi的话Ansi版本内部会调用Unicode版本形成死循环)
如果函数很简单(比如GetCommandLine直接就返回内存一块固定区域的地址),自己手动实现一遍
很复杂的函数的话就必须要备份函数头部,这样就跟普通的inline hook没有太大差别了,你可以参阅这方面的文章
最近正好写了点和Hot patch相关的东西,还有什么问题可以向我询问
顺带一提在win64平台上没有对应的Hot patch技术,所以如果对可移植性有要求的话(hook肯定是hook别人的程序,而别人可能用的就是64位程序),建议早点换成一个通用的hook方案有助于提高代码的可维护性
能力值:
( LV2,RANK:10 )
5 楼
好吧,我自己说下实现非标准hot patch的api hook的解决方法,针对wsastartup:
wsastartup的入口点如下:
nop
nop
nop
nop
nop
push 14h <---------------wsastartup入口
push ws2_32.76c53c08
call ws2_32.76c51370
我的hook函数的实现:
DWORD ver = 0;
DWORD dat = 0;
DWORD regEIP[2] = {0, 0};
PVOID next_step_addr = NULL;
static void __declspec(naked) __MyHook_WSAStartup()
{
{
_asm { // 将调用WSAStartup时入栈的参数弹出保存
pop regEIP[0];
pop ver;
pop dat;
push regEIP[0];
}
// 获取下一步的地址
next_step_addr = CApiHookWSAStartup::hooker.GetNextStepAddress();
_asm { // 先调用实际的WSAStartup函数,再调用相关的工作函数
call near orig_routine;
call begin_lurker_handler; // 个人的处理函数,希望在real wsastartup返回之后被调用
ret;
orig_routine:
pop regEIP[1];
push dat;
push ver;
push regEIP[1];
push 14h; // 模拟WSAStartup入口点的指令
jmp next_step_addr;
}
}
}
这个实现,正在正常使用,达到了我的目的。但是call有far和near之分,怎么在__MyHook_WSAStartup里面判断WSAStartup被调用的时候是call far的还是call near。希望有兄弟解惑,并给出更好一点的解决方案。
能力值:
( LV2,RANK:10 )
6 楼
不太清楚你是为什么想要知道call是近跳转还是远跳转,这个只对调用者有意义,被调用者是不用管的
如果是我来实现这个函数我大概会采用下面的做法
int WINAPI NewWSAStartup(
_In_ WORD wVersionRequested,
_Out_ LPWSADATA lpWSAData
){
int ReturnValue;
_asm
{
push lpWSAData
push wVersionRequested
push 14h
call next_step_addr
mov ReturnValue,eax
}
//用C干你想干的事情
return ReturnValue;
}
大致就是这个样子,也许有些地方需要修改,不过应该没错
能力值:
( LV2,RANK:10 )
7 楼
_asm
{
push lpWSAData
push wVersionRequested
push 14h <---------------
call next_step_addr <---------------
mov ReturnValue,eax
}
楼上的哥们注意看这两个箭头所指的地方:
一、当call的时候,1)如果是far调用,实际上在push 14h和next_step_addr之间多了两个寄存器入栈(CS和EIP);2)如果是near调用,实际上在push 14h和next_step_addr之间多了一个寄存器入栈(EIP)。
二、不考虑call带来的寄存器入栈的副作用,即便是call next_step_addr成功之后,就已经直接ret到调用wsastartup的下一个指定的地方,也即mov returnvalue, eax不可能被调用
鉴于此,楼上的代码是不能成功运行的。
而我指出的判断call是far或者near,正是要解决call指令的入栈操作的问题。楼上的哥们可以仔细体会下我的解决方案的原理。
能力值:
( LV2,RANK:10 )
8 楼
DWORD ver = 0;
DWORD dat = 0;
DWORD regEIP = 0;
PVOID next_step_addr = NULL;
static void __declspec(naked) __MyHook_WSAStartup()
{
{
_asm { // 将调用WSAStartup时入栈的参数弹出保存
pop regEIP; <---------针对call是far或者near,应该有不同的措施
pop ver;
pop dat;
push regEIP;
}
// 获取下一步的地址
next_step_addr = CApiHookWSAStartup::hooker.GetNextStepAddress();
_asm { // 先调用实际的WSAStartup函数,再调用相关的工作函数
push dat; <----移到这里
push ver; <----移到这里
call near orig_routine;
call begin_lurker_handler; // 个人的处理函数,希望在real wsastartup返回之后被调用
ret;
orig_routine:
push 14h; // 模拟WSAStartup入口点的指令
jmp next_step_addr;
}
}
} 实际上代码可简化成这样
能力值:
( LV13,RANK:400 )
9 楼
在保护模式下的windows程序中flat模型,不存在你说的压段寄存器入栈的问题,事实上在保护模式下段寄存器只是摆设。
能力值:
( LV13,RANK:400 )
10 楼
你所说的非标准指的是什么?
如果你确定目标函数的前五个字可以构成n条完整的指令,那就直接替换前n条指令,吧原始的前五条指令复制到一个内存空间中,然后子在你的hook函数中跳转去执行那5个字节的指令,或者直接把原函数的5个字节的指令硬编码到你的hook函数中。
如果目标函数的前5个字节不能构成完整的n条指令,那就需要你自己去做指令解析,计算出总长度大于5个字节并且能构成n条完整指令的字节数m,然后保存m个字节的内容到一个内存buffer,然后在你的hook函数中跳转去执行那m个字节的指令。 你这个代码写的没有合理性可言。。。等等有空了给你上个徒手hook函数处理大全
能力值:
( LV2,RANK:10 )
11 楼
楼上的可以实际去测试。intel的software developer's manual的6.3 calling procedures using call adn ret对这做了说明
能力值:
( LV2,RANK:10 )
12 楼
这个简单跳转可以这么实现:
static void __declspec(naked) __WSAStartup()
{
{
// 在这里做私人的事情
// 获取下一步的地址
next_step_addr = CApiHookWSAStartup::hooker.GetNextStepAddress();
_asm {
push 14h; // 模拟WSAStartup入口点的指令
jmp next_step_addr;
// 这里无法到达无法到达无法到达无法到达无法到达
ret;
}
}
}
这个跳转是可以正常使用的,但是由于jmp next_step_addr;之后就直接返回到wsastartup调用之后的下一条指令了,所以达不到想要的效果,即:根据实际wsastartup调用结果来判断是否进行私人的处理。
能力值:
( LV13,RANK:400 )
13 楼
知识面要放宽,你看到的只是16位的dos程序的介绍。
windows在平坦内存模式下代码和数据的寻址范围都在4G以内,所以寻址方式全部为NEAR型,何来远跳一说?
你观察一下Call一个系统API之后栈内有没有多出来一个你所谓的段寄存器
去学习学习windows,保护模式,.flat平坦内存模式,关键词就给你列这么多,能3个月内能全部领悟吃透你也很不错了.
能力值:
( LV2,RANK:10 )
14 楼
针对楼上的说法,本人不给回应。先去实际测试再来说问题。
能力值:
( LV13,RANK:400 )
15 楼
如果我写,我会这样写:
DWORD g_pOriginalWSAStartup = 获取原函数地址
void __declspec(naked) WSAStartup_Stub(WORD wVersionRequested, LPWSADATA lpWSAData)
{
_asm
{
mov edi, edi //把原函数的前
push ebp //n跳指令
mov ebp, esp //直接硬编码过来
mov eax, g_pOriginalWSAStartup
add eax, 5
jmp eax
}
} VOID __stdcall fakexxxDispatchMessage(WORD wVersionRequested, LPWSADATA lpWSAData)
{
// 可以对参数做分析处理
// 调用原函数
WSAStartup_Stub(wVersionRequested, lpWSAData);
// 对返回值进行分析处理(如果有的话)
// 返回原函数的返回值,或者你想反回的值
return ;
}
能力值:
( LV13,RANK:400 )
16 楼
期待你的结果。。
我要先睡了。
能力值:
( LV13,RANK:400 )
17 楼
不好意思,上上楼的fake忘了改了
能力值:
( LV2,RANK:10 )
18 楼
感谢楼上的积极参与,可能你对非标准hot patch的api缺乏关注。目测WSAStartup_Stub 这个函数是不能正确运行的,就算正确运行,它也会直接return到wsastartup调用的下一条指令了,根本不会有后续处理的机会。请注意naked关键字。
实际可行的是本人给出的代码,各位可以实际测试。
能力值:
( LV13,RANK:400 )
19 楼
楼主,先把你关于近跳远跳的实验结结果说出来吧,至于我的代码能不能用,无bin无真相,明天放个bin出来咱就知道了。
能力值:
( LV2,RANK:10 )
20 楼
我都说了那个代码只是一个大致的样子,只是证明这个函数不需要像你那样写的那么庞大,我自己都没试着编译这块代码
看了下楼主其他的几个回帖,明显感觉基础有待加强,也懒的多说,花了点时间把代码整理出来,你自己开调试器对照着看吧
只把这里讨论到的函数用C写了一遍
上传的附件:
能力值:
( LV13,RANK:400 )
21 楼
楼主给你bin
// HookWSAStartup.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Windows.h>
#pragma comment (lib, "ws2_32")
typedef int (_stdcall * Type_WSAStartup)(WORD, LPWSADATA);
// 全局变量,原始函数地址
Type_WSAStartup g_pOriginalAddress = NULL;
// 原函数前n个字节的完整指令,至于为什么是7,自己研究研究吧,
// 这里只是写个简单的列子,所以直接硬编码了,如果要通用,就需要进行指令分析,计算长度
#define COMPLETE_INSTRUCTION_LENGTH 7
// stub函数,用于跳转到原函数
int _declspec(naked) PASCAL FAR WSAStartup_stub(
WORD wVersionRequested,
LPWSADATA lpWSAData
)
{
__asm
{
_emit 0;
_emit 0;
_emit 0;
_emit 0;
_emit 0;
_emit 0;
_emit 0;
mov eax, g_pOriginalAddress;
add eax, COMPLETE_INSTRUCTION_LENGTH;
jmp eax
}
}
// hook函数
int PASCAL FAR fake_WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
)
{
int iRet = 0;
MessageBox(NULL, _T("Before original function."), NULL, MB_OK);
iRet = WSAStartup_stub(wVersionRequested, lpWSAData);
MessageBox(NULL, _T("After original function."), NULL, MB_OK);
return iRet;
}
// 下面的函数主要用于hook操作,写的比较乱
#pragma pack(push)
#pragma pack(1)
typedef struct __FIVEB_INSTRUCTION
{
BYTE jmpCode;
DWORD_PTR jmpOffset;
}FIVEB_INSTRUCTION, *PFIVEB_INSTRUCTION;
#pragma pack(pop)
BOOL DoHook(DWORD_PTR pOriginalAddress, DWORD_PTR pStubProc, DWORD_PTR pFakeProc)
{
BOOL bRet = FALSE;
LPVOID targetProc = (LPVOID)pOriginalAddress;
DWORD dwOldProtectTarget = 0;
DWORD dwOldProtectHook = 0;
MEMORY_BASIC_INFORMATION memInfoTarget;
MEMORY_BASIC_INFORMATION memInfoHook;
SIZE_T sBytesDone = 0;
if (VirtualQuery((LPVOID)targetProc, &memInfoTarget, sizeof(memInfoTarget)) > 0 && VirtualQuery((LPVOID)pStubProc, &memInfoHook, sizeof(memInfoHook)) > 0)
{
// change the pages to read/write
bRet = VirtualProtect(memInfoTarget.BaseAddress,
memInfoTarget.RegionSize, PAGE_READWRITE, &dwOldProtectTarget);
bRet = VirtualProtect(memInfoHook.BaseAddress,
memInfoHook.RegionSize, PAGE_READWRITE, &dwOldProtectHook);
bRet = ReadProcessMemory(GetCurrentProcess(),
targetProc,
IntToPtr(pStubProc),
COMPLETE_INSTRUCTION_LENGTH,
&sBytesDone);
if (!bRet)
{
return FALSE;
}
FIVEB_INSTRUCTION jmpins;
jmpins.jmpCode = 0xe9;
jmpins.jmpOffset = pFakeProc - pOriginalAddress - sizeof(jmpins);
bRet = WriteProcessMemory(GetCurrentProcess(),
IntToPtr(pOriginalAddress),
&jmpins,
sizeof(jmpins),
&sBytesDone);
if (!bRet)
{
return FALSE;
}
bRet = VirtualProtect(memInfoTarget.BaseAddress,
memInfoTarget.RegionSize, dwOldProtectTarget, &dwOldProtectTarget);
bRet = VirtualProtect(memInfoHook.BaseAddress,
memInfoHook.RegionSize, dwOldProtectHook, &dwOldProtectHook);
return TRUE;
}
return FALSE;
}
int _tmain(int argc, _TCHAR* argv[])
{
g_pOriginalAddress = (Type_WSAStartup)GetProcAddress(LoadLibrary(_T("ws2_32.dll")), "WSAStartup");
if (NULL == g_pOriginalAddress)
{
return 0;
}
DoHook(PtrToInt(g_pOriginalAddress), PtrToInt(WSAStartup_stub), PtrToInt(fake_WSAStartup));
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup(wVersionRequested, &wsaData);
if ( err != 0 )
{
return 1;
}
return 0;
}
HookWSAStartup.7z
上传的附件:
能力值:
( LV15,RANK:440 )
22 楼
楼主的目的达到啦,嘿嘿
能力值:
( LV2,RANK:10 )
23 楼
写好了程序敢不敢自己运行一下!
bRet = VirtualProtect(memInfoTarget.BaseAddress,
memInfoTarget.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtectTarget);
bRet = VirtualProtect(memInfoHook.BaseAddress,
memInfoHook.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtectHook);
改成这个就好了,出问题的主要是第二个,因为hook stub和当前指令在同一个页里
第二个问题
你这不是hot patch,只是个普通的inline hook而已...好歹主题是hot patch啊...
能力值:
( LV13,RANK:400 )
24 楼
无伤大雅,只是为了说明naked函数不想楼主所想的那样
能力值:
( LV2,RANK:10 )
25 楼
实际上代码是这么写的,谢谢大家讨论
BOOL CApiHookWSAStartup::Hook()
{
return CApiHook::Hook(_T("ws2_32.dll"), "WSAStartup");
}
typedef int (PASCAL FAR *fnWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData);
int PASCAL FAR CApiHookWSAStartup::_WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
{
DWORD_PTR retv = 0;
DWORD_PTR _NextStepAddress = (DWORD_PTR)NextStepAddress; <------------
_asm
{
pushad;
mov eax, dword ptr [lpWSAData];
push eax;
movzx ecx, word ptr [wVersionRequired];
push ecx;
call near real_WSAStartup; <------------------
mov retv, eax;
popad;
jmp func_ok;
real_WSAStartup:
push 14h;
jmp _NextStepAddress;
ret; // 实际上不会执行
func_ok:
;
}
if (retv == 0)
begin_lurker_handler();
//retv = ((fnWSAStartup)NextStepAddress)(wVersionRequired, lpWSAData);
return retv;
}
static int PASCAL FAR __WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
{
return CApiHookWSAStartup::hooker._WSAStartup(wVersionRequired, lpWSAData);
}
PVOID CApiHookWSAStartup::GetMyApiAddress()
{
return (PVOID)__WSAStartup;
}