×

inlinehook inline hook

inlinehook(关于inline hook、hook意思的疑问)

admin admin 发表于2022-09-07 02:25:31 浏览151 评论0

抢沙发发表评论

本文目录

关于inline hook、hook意思的疑问


hook 计算机里面一般是指 挂钩某函数, 就是替换掉原来的函数。
inline hook , 是直接在以前的函数替里面修改指令,用一个跳转或者其他指令来达到挂钩的目的。
这是相对普通的hook来说,因为普通的hook只是修改函数的调用地址,而不是在原来的函数体里面做修改。
一般来说 普通的hook比较稳定使用。 inline hook 更加高级一点,一般也跟难以被发现。所以很多人比如病毒制作者都比较推崇inline hook。
-inlinehook

不使用工具查找木马


愿我的答案 能够解决您的烦忧

首先我想说那是不可能的,因为病毒是有潜伏性的,不用软件自己靠找是找不到的
1,如果怕占内存大,推荐你个占内存小的功能还多的。
2,下载腾讯电脑管家“8.4”最新版,对电脑首先进行一个体检,打开所有防火墙避免系统其余文件被感染。
3,打开杀毒页面开始查杀,切记要打开小红伞引擎。
4,如果普通查杀不能解决问题,您可以打开腾讯电脑管家---工具箱---顽固木马专杀- 进行深度
扫描。
5,查杀处理完所有病毒后,立刻重启电脑,再进行一次安全体检,清除多余系统缓存文件,避免二次感染。
如果您对我的答案不满意,可以继续追问或者提出宝贵意见,谢谢

inline HOOK啥意思的


hook 计算机里面一般是指 挂钩某函数, 就是替换掉原来的函数。
inline hook , 是直接在以前的函数替里面修改指令,用一个跳转或者其他指令来达到挂钩的目的。
这是相对普通的hook来说,因为普通的hook只是修改函数的调用地址,而不是在原来的函数体里面做修改。
一般来说 普通的hook比较稳定使用。 inline hook 更加高级一点,一般也跟难以被发现。所以很多人比如病毒制作者都比较推崇inline hook。
-inlinehook

inline hook只能hook在函数头部吗


当执行完我们自定义的hook函数之后,又从被hook函数的首部开始执行,被hook函数一进入就被跳转了。而本文,则要实现在某个函数体内部任意地方进行hook并跳转,执行完我们的函数之后,再回到原来的位置继续向下执行完未执行的逻辑。那么,初看这种方式似乎与前面写的两篇hook没有什么差别,都是hook,都是跳转然后回到被hook的函数。但仔细一想,你会发现本文要实现的方式要比前面两种hook复杂,因为hook的地方是函数体内任意地方,那么回来的时候就不是直接调用被hook的函数了,而是要回到之前hook的地方去。这期间就涉及到hook函数的返回地址问题和被hook函数的返回地址问题。

说了这么多,可能还是有点晕,先不管为什么要这么做,也不管这种hook方式能有什么用途(在本文最后会说明用途),下面我们先写一些代码,在实践中来想一想这种方式有什么用途,并且与之前的两篇hook进行比较。

首先,我们需要一个自定义的hook函数,这个函数也就是被hook函数被hook后跳转到的地方,这个hook函数负责hook与unhook,还可以监视寄存器,监视内存,也可以管理hook的次数,以供我们灵活的hook需求。直接贴代码吧:
[cpp] view plaincopyprint?
#include 《iostream》
#include 《windows.h》

#pragma warning( disable : 4311 )
#pragma warning( disable : 4312 )

#define HOOK_BYTES 5
typedef unsigned int uint;

uint hookAddr = 0;
char old_code[ HOOK_BYTES ];
char new_code[ HOOK_BYTES ];

void printRegisters( void );

bool hook( void )
{
DWORD dwFlag;
if ( VirtualProtect( ( void* )hookAddr, HOOK_BYTES, PAGE_EXECUTE_READWRITE, &dwFlag ) )
{
memcpy( old_code, ( void* )hookAddr, HOOK_BYTES );
memcpy( ( void* )hookAddr, new_code, HOOK_BYTES );
VirtualProtect( ( void* )hookAddr, HOOK_BYTES, dwFlag, &dwFlag );
return true;
}
return false;
}

void unhook( void )
{
DWORD dwFlag;
if ( VirtualProtect( ( void* )hookAddr, HOOK_BYTES, PAGE_EXECUTE_READWRITE, &dwFlag ) )
{
memcpy( ( void* )hookAddr, old_code, HOOK_BYTES );
VirtualProtect( ( void* )hookAddr, HOOK_BYTES, dwFlag, &dwFlag );
}
}

namespace global
{
uint gEAX = 0;
uint gEBX = 0;
uint gECX = 0;
uint gEDX = 0;
uint gESP = 0;
uint gEBP = 0;
uint gESI = 0;
uint gEDI = 0;

uint gRet = 0; // 临时的返回地址
uint gTmp = 0; // 一些临时的值保存
uint gPar = 0; // 被hook函数的正常返回地址
uint gCnt = 1; // 当前hook的次数
uint gMax = 0; // 最大hook次数,为0表示一直hook
bool bEnt = 0; // 是否为第一次进入hook函数
}

void __declspec( naked ) hook_jmp( void )
{
__asm
{
__entry:
pushad
{
cmp global::bEnt, 0 // 如果没有进入则表示需要unhook
je __first

cmp global::gMax, 0 // 如果为0,则一直启用hook逻辑
je __second

mov eax, global::gCnt
cmp eax, global::gMax // 如果当前hook次数没有达到最大次数,则继续
jl __second

mov global::gCnt, 1 // reset state
mov global::bEnt, 0 // reset state
mov global::gMax, 0 // reset state

mov eax, global::gPar // 被hook函数的正常返回地址
mov global::gRet, eax // 准备跳转到被hook函数的上层调用,结束hook
popad
jmp __ret
}

__first:
// 保存相关重要寄存器值
{
popad
mov global::gEAX, eax
mov global::gEBX, ebx
mov global::gECX, ecx
mov global::gEDX, edx
mov global::gESP, esp
mov global::gEBP, ebp
mov global::gESI, esi
mov global::gEDI, edi
}

// 第一次进入,unhook并监视相关状态
pushad
{
mov global::bEnt, 1 // 记录状态

mov edi, global::gEBP // 被hook函数的ebp
mov eax, [ edi + 4 ] // 被hook函数的返回地址(其上层调用地址)
mov global::gPar, eax // 保存返回地址
mov esi, __entry // 将被hook函数的返回地址修改为
mov [ edi + 4 ], esi // 本函数的首地址,以便执行完被hook函数的
// 剩余逻辑之后能够返回到本函数,决定是否
// 还需要hook。

call printRegisters // 打印寄存器值[测试],或者其他
call unhook // unhook

mov eax, hookAddr // 获得被hook的内存地址
mov global::gRet, eax
}
popad

pop global::gTmp // 移除本函数的返回地址,并将hook的地址设置
jmp __ret // 为本函数的返回地址,从而实现跳转

__second:
// 第二次进入, 继续hook, 这次进入是被hook函数ret返回的,没有新的ret地址被压栈
{
mov global::bEnt, 0 // 设置状态
add global::gCnt, 1 // 增加hook计数

call hook // hook

mov eax, global::gPar // 将被hook函数的返回地址设置为本函数的
mov global::gRet, eax // 返回地址,从而实现正常的函数流程
}
popad

__ret:
push global::gRet // 修改本函数的返回地址
ret
}
}

void setHookBytes( uint addr )
{
hookAddr = addr;
new_code[ 0 ] = ( char )0xe8; // call 指令机器码
( uint& )new_code[ 1 ] = ( uint )hook_jmp - addr - 5; // 计算跳转偏移
}

void printRegisters( void )
{
printf( “EAX = 0x%08x/n“, global::gEAX );
printf( “EBX = 0x%08x/n“, global::gEBX );
printf( “ECX = 0x%08x/n“, global::gECX );
printf( “EDX = 0x%08x/n“, global::gEDX );
printf( “ESP = 0x%08x/n“, global::gESP );
printf( “EBP = 0x%08x/n“, global::gEBP );
printf( “ESI = 0x%08x/n“, global::gESI );
printf( “EDI = 0x%08x/n“, global::gEDI );
}
如上,hook_jmp函数即为我们自定义的hook函数,当被hook函数被hook之后,就会跳转到这个函数里,执行相关逻辑,上面我加了很详细的注释。应该很容易看懂。还是先看怎么使用这套方法,再来细说,代码如下:
[cpp] view plaincopyprint?
void testHook( void )
{
printf( “This is a hook test 1./n“ );
printf( “This is a hook test 2./n“ );
printf( “This is a hook test 3./n“ );
printf( “This is a hook test 4./n“ );
printf( “______________________/n“ );
}

int main( void )
{
uint hook_addr = 0x0042ec7b;
setHookBytes( hook_addr );

global::gMax = 2;
if ( hook() )
{
testHook();
testHook();
testHook();
}
system( “pause“ );
return 0;
}
如上,testHook函数即为被hook的函数,在main函数中,0x0042ec7b则为testHook函数里的第二个printf调用的地址,在你的机器上可能不一样。这里只是测试之用。testHook函数具体反汇编代码如下:
[cpp] view plaincopyprint?
void testHook( void )
{
0042EC50 push ebp
0042EC51 mov ebp,esp
0042EC53 sub esp,0C0h
0042EC59 push ebx
0042EC5A push esi
0042EC5B push edi
0042EC5C lea edi,[ebp-0C0h]
0042EC62 mov ecx,30h
0042EC67 mov eax,0CCCCCCCCh
0042EC6C rep stos dword ptr es:[edi]
printf( “This is a hook test 1./n“ );
0042EC6E push offset string “This is a hook test 1./n“ (487E24h)
0042EC73 call @ILT+4550(_printf) (42D1CBh)
0042EC78 add esp,4
printf( “This is a hook test 2./n“ );
0042EC7B push offset string “This is a hook test 2./n“ (487E08h)
0042EC80 call @ILT+4550(_printf) (42D1CBh)
0042EC85 add esp,4
printf( “This is a hook test 3./n“ );
0042EC88 push offset string “This is a hook test 3./n“ (487DECh)
0042EC8D call @ILT+4550(_printf) (42D1CBh)
0042EC92 add esp,4
printf( “This is a hook test 4./n“ );
0042EC95 push offset string “This is a hook test 4./n“ (487DD0h)
0042EC9A call @ILT+4550(_printf) (42D1CBh)
0042EC9F add esp,4
printf( “______________________/n“ );
0042ECA2 push offset string “_____________________./n“ (487DB4h)
0042ECA7 call @ILT+4550(_printf) (42D1CBh)
0042ECAC add esp,4
}
0042ECAF pop edi
0042ECB0 pop esi
0042ECB1 pop ebx
0042ECB2 add esp,0C0h
0042ECB8 cmp ebp,esp
0042ECBA call @ILT+3570(__RTC_CheckEsp) (42CDF7h)
0042ECBF mov esp,ebp
0042ECC1 pop ebp
0042ECC2 ret
我们hook的就是第18行(0042EC7B)那句代码,setHookBytes构建了一个5字节的call语句,0xe8为CALL指令的机器码,后面4个字节是CALL的偏移量(目标地址 - 当前地址 - CALL指令占用的5个字节)。

在main函数中,构建了hook的5个字节之后,设置了hook次数,如main函数那段代码的第15行:global::gMax = 2,则会hook两次。然后是main函数那段代码的第16行,调用hook函数,将5个字节的call指令写入0042EC7B中,并保存了0042EC7B中原来的代码到old_code中。之后,我们便可以调用testHook函数进行测试hook的流程了。最终输出结果为:
This is a hook test 1.
EAX = 0x00000017
EBX = 0x7ffdc000
ECX = 0x8df97741
EDX = 0x00499148
ESP = 0x0012fd84
EBP = 0x0012fe54
ESI = 0x00000000
EDI = 0x0012fe54
This is a hook test 2.
This is a hook test 3.
This is a hook test 4.
______________________
This is a hook test 1.
EAX = 0x00000017
EBX = 0x7ffdc000
ECX = 0x8df97741
EDX = 0x00499148
ESP = 0x0012fd84
EBP = 0x0012fe54
ESI = 0x00000000
EDI = 0x0012fe54
This is a hook test 2.
This is a hook test 3.
This is a hook test 4.
______________________
This is a hook test 1.
This is a hook test 2.
This is a hook test 3.
This is a hook test 4.
______________________

可以看出,前面两次调用testHook函数时,都执行了hook_jmp函数,并调用了printRegisters函数将寄存器打印了出来,之后又回到testHook中,继续输出后面的3句字符串。当两次hook之后,第三次调用testHook时,就不会再输出寄存器了,也没有被hook了。

我们来看几个比较hook_jmp中比较关键的几个地方:
第104到110行:这段汇编代码,主要用于保存testHook函数(被hook函数)的正常的返回地址(main函数里调用testHook的下一句指令的地址)到global::gPar变量中,并将hook_jmp的首地址(也就是__entry标签指示的地址)写入testHook函数的返回地址所在的内存里。这样当unhook并执行完testHook之后又能回到hook_jmp中,进一步判断是否需要下一次hook。如果不需要再hook(已经达到最大hook次数)时,则会执行第79到80行的两句汇编代码,这两句汇编代码的作用是将hook_jmp函数的返回地址设置为testHook函数正常的返回地址,也就是main函数里调用testHook函数的下一句汇编代码的地址(ret指令的原理如果不清楚,请看前两篇hook文章或查阅相关资料)。这样一来,当不再需要hook时,就能顺利的从hook_jmp函数返回后直接跳转到main函数的作用域里。这样整个调用流程就符合原本的调用流程了。

第113行:这句代码是在调用了printRegister函数之后进行unhook操作,将原本的5个字节的代码重新拷贝到testHook函数的相应代码地址的内存里,本例中为testHook函数中第二句printf函数调用的地址。unhook之后,第115到116行的两句代码与第79到80行的两句汇编代码类似,只不过这时是将被hook的内存地址设置为hook_jmp的返回地址,这样就能在第一次进入testHook函数并执行完毕返回时,能够跳转到被hook的地址(hookTest函数里第2句printf调用的地址)继续向下执行剩余的逻辑。

第131到132行:这两句汇编代码与第79到80行的两句汇编代码一致,都是将main函数里相应的代码地址设置为hook_jmp函数的ret返回地址,这样就能直接从hook_jmp跳转到main函数里继续向下执行,这样也就代表testHook被顺利的调用完成。

所以,总结下来,hook_jmp函数会进入两次,第一次用于监视一些数据,本例只监视了相关寄存器,还可以增加监视指定内存地址等等。第一次进入时,会保存被hook函数(testHook函数)的返回地址,并将其修改为hook_jmp函数的首地址,这样做是为了执行完testHook函数之后能够第二次进入hook_jmp函数。那么,第二次进入后,首先是判断是否还需要hook,不需要则直接返回到main函数里,如果还要继续hook,则再次调用hook函数,然后跳转到main函数里。这样就构成了一个严密的调用流程,一切都看起来很和谐的调用,有点类似缓冲区溢出攻击的原理。

hook_jmp函数中需要注意寄存器的保存,否则输出的寄存器值并不是testHook函数执行到hook位置时的寄存器状态,这样就丧失了监视的意义。

原理上其实比较简单,构建稍微细致了一些,与前两篇hook最大的不同就是需要手动修改ret的返回地址,从而达到hook的目的,不像之前的两篇hook,在进入hook函数之后,要回到被hook的函数时,只需要直接call就可以了,并不需要维护ret指令的返回地址。另外,由于本文的hook方式与第一篇的hook方式类似,所以本文的方式并没有支持多线程环境。
好了,本文到此结束,由于水平有限,可能存在bug,还望指教,衷心感谢!
-hook

C++实现inline hook,注入后程序异常退出


你要搞清楚一个概念,DLL被加载后地址是要重定位的,所有的全局变量、函数这些,都会随DLL加载的基址不同,地址会进行对应偏移的。

你WriteProcessMemory那句,往oldFunctionAddr地址写东西,肯定会引起异常的,谁知道被你hook的程序这个地址是啥东西?有没有分配过内存呢?

你要往被注入进程写东西,就必须先用VirtualAllocEx申请内存,你一定要牢记这个概念,所有地址都是动态的,函数、全局变量只有偏移量是固定的。
-inlinehook

免杀如何过杀软的主防


2008年,杀软已迈入了主动防御时代。
注册表监视,文件监视,有害行为判断可以拦截大部分病毒木马的运行。在这种情况下,病毒木马想要运行,就面临2种选择,选择A,绕
过主动防御运行。选择B,干掉杀软的主动防御然后运行。
要绕过主动防御,必须存在一个前提,就是杀软存在防御盲区。比如,杀软监视了注册表,监视了注册表各个启动项,我们无法通过注册
表实现自启动。那么我可以使用其他方法,比如DLL劫持,PE文件感染来实现自启动。
再比如,杀软屏蔽了远程线程的注入方式,那么我们使用普通的钩子进行注入。
但是,不同的杀软,有着不同的防御盲区。一种绕过方法可能在卡巴上行得通,但是在瑞星下就被杀掉了。我们也很少有耐心逐个杀软的
进行测试,因此,选择B,干掉杀软的主动防御才是病毒木马未来的趋势。
早期的主动防御,仅仅恢复SSDT就可以干掉。但是现在有很多杀软进行了深度的inline HOOK,仅仅恢复SSDT就失效了。而且现在还有一些
专门的反木马软件,比如360BOX,360TRAY,超级巡警等等,这些软件都进行了深度的 inline hook。这些软件inline HOOK 的位置互不相同,
我们不太可能一一恢复。而且就算你有足够的耐心,将这些inline hook位置 与相应在磁盘的文件位置进行一一对比,一一恢复。还有另外一
个未知的问题等着你:你的恢复是否会产生蓝屏?杀软是否会检测它自己的HOOK?所以,恢复inline hook的办法可以针对某些杀毒。但是不要
指望用它干掉所有杀毒。
难道真的没有办法了吗?当然不是,有种办法可以让你知道某个进程的名字,就可以终止它的运行。这就是“映像劫持”。不过“映像劫
持”是需要修改注册表的。(映像劫持本身是很简单的,你可以理解为在注册表的某个地方写入进程名)那我们又如何在主动防御下修改注册
表呢?办法很多,最简单的是“关机消息拦截”。下面我简单地介绍一下:
当WINDOWS关机的时候,它会向所有的程序窗口逐个发送一个消息,询问是否可以关机。如果你的程序回答“是”。并且所有的程序窗口都
回答“是”。WINDOWS才开始逐个结束程序。但是,在将要结束一个程序前,WINDOWS还会再进行一次确认。收到肯定回答之后,才关闭该程序
。也就是说,在我们的程序收到这个最终确认消息时,杀毒进程已经在我们的程序之前结束了。(先运行的程序先关闭)因此这时,我们就可
以做我们想做的事。比如,写注册表。
这个方法似乎有点不完美,如果用户突然断电,没有经过正常的关机过程。那我们的程序也就没机会写注册表了。
有没有更好的办法,当然有,而且很多。比如,我们可以进行驱动感染,然后被感染的驱动会加载我们自己的ROOTKIT驱动,进行修改注册
表,恢复SSDT等等操作。而且有种方法可以在不修改注册表的情况下加载驱动(具体方法请去rootkit.com搜索)。
至于让杀软失效,办法也很多。你也可以学习杀软进行deep inline hook,以其人之道还治其人之身。只要你HOOk的地方足够关键。最好
是某个关键的未导出函数。然后稍微过滤下进程名,就可以让杀软完蛋。轻则界面不显示,窗口没任何反应。重则崩溃退出,甚至导致蓝屏。
当然,最期待的结果是杀软没反映。一切都看你如何HOOK了。
其实只要你有足够的想象力,办法是无穷无尽的,RING3下向杀毒窗口发送垃圾消息就可以搞定一批杀软。会导致杀软窗口无响应。过主动
防御加载驱动还有著名的beep大法,beep大法也可以用到其它的一些系统SYS上,而且其本身也有4种办法,防不胜防。
-hook

inline HOOK和SSDT HOOK各自有啥特性的哦


inline hook 是直接在以前的函数替里面修改指令,用一个跳转或者其他指令来达到挂钩的目的。
ssdt是利用api来挂钩的,相当于替换API,用ICESword可以很简单的判断出来。
底下是我在网上看过一篇文章的说的一个比喻,刚好能回答你的问题:
如果说SSDT Hook只是把某位“内核API先生“绑架,然后用我们的“自己人”来接管其工作,而ICESword却可以从其他联系途径找到被绑架的“内核API先生“并“报警”,那么——Inline Hook可以说是给“内核API先生“动了手术,让他成为“我们阵营的一分子“。
呵呵,很幽默的一个比喻
-inlinehook

如果当前call的地址是个jmp怎么修复相对偏移 inline hook


关键点吗……1、恢复现场,要存多少字节根据你填入的jmp占用多少字节而定,jmprel32是5个字节。2、od找0那一段,程序操作的话你可以用malloc申请的内存来,而不一定是exe里的03、VirtualProtect调用可以使代码段可写(否则修改的时候会出现写入异常,程序崩溃)4、jmprel32指令的机器代码是E9rel32一共5个字节。如果嫌计算相对地址麻烦你也可以用jmpmem32,机器指令是FF25mem32(没记错的话……也有可能是FF15)。但是我觉得这样更麻烦5、jmprel32指令中,rel32相对的是执行jmp之后的eip。例如40000:E91122334440005:那么翻译过来就是40000:jmp443722166、不确定要hook的代码位置的指令多长的话,你可能需要一个反汇编库来确定,当然更容易(但是不通用)的方式是人工查看……跳转来跳转去记得一定要跳转到机器代码边界上,比如刚才那个40000:E911223344你不可以跳转到(40000,40005)区间的任何一个地址上,但是可以跳转到40000或40005地址上。
-hook