首页
社区
课程
招聘
[原创]一篇文章带你理解HOOK技术
发表于: 2019-5-18 16:35 32045

[原创]一篇文章带你理解HOOK技术

2019-5-18 16:35
32045

         1.这是《一篇文章带你....》系列的第三篇,也是自己的学习总结,很多不懂的地方找的论坛前辈的资料。代码都是自己手敲,边敲边注释,对一些细节做了微调。前两篇是一篇文章带你学会Armadillo脱壳一篇文章带你理解PE三表

         2.几个驱动相关的HOOK,从代码层面上讲理解的不是很深刻。但是手敲了一边除了一些驱动相关的知识,HOOK原理上应该有所领悟。

         3.还是一样,不提供源码下载,驱动相关的HOOK,前辈们都写有源码。不作伸手党。

         4.想来想去,这篇文章已经停滞1个多月了,今天草草结笔。关于HOOK检测部分估计需要鸽了。有空再学习吧。看雪第四版有源码。

         5.这篇文章是新瓶装旧酒,大佬别喷我。下一篇文章预计写注入,代码已经写完了,就剩下总结了,同样的还是新瓶装旧酒。旨在学习。下下一篇我也不知道写什么?加壳?花指令??管他呢。

         6.唉,我什么时候才能成为像各位一样的大佬啊^_^

         IAT是程序中存储导入函数地址的数据结构,如果HOOK了导入函数地址。就可以在函数调用的时候,将函数流程HOOK到我们指定的流程。但是我个人觉得这种方式最好要结合DLL注入的方式,如果单纯的使用HOOK,那么就需要将需要执行的操作的shellcode写入目标进程,如果操作复杂,可能需要的shellcode量特别大,所以我们需要借助DLL注入,这样就将我们需要执行的代码写入进程内部,在HOOK的Detour函数只需要实现LoadLibrary的操作。

         IATHOOK的基本原理就是通过修改程序IAT数据结构,将原始调用API函数地址Target函数地址修改为Detour函数地址。所以IAT_HOOK需要实现以下几个步骤:

         首先需要构造Detour函数,为了堆栈平衡和一些其他原因,最好Detour函数的函数原型和Target函数原型保持一致。

         这个函数的内容可以任意设置,主要执行两个方面的操作,一是执行我们想进行的操作,二是控制Target函数的返回值。为了简单期间,设置MessageBox作为HOOK,标志,MessageBox函数地址可以使用保存的API函数

         第二歩是获取Target函数地址,这一步的目的是为了遍历IAT的时候比较IAT中所存储的Target函数地址。以便找到存放目标函数的IAT地址。

         第三步是获取Target函数的IAT地址,首先需要获取导入表的RVA,这里可以使用函数ImageDirectoryEntryToData获取

         PE装载器已经将PE文件载入内存,可以使用IAT获取函数地址,所以通过FirstThunk指向的IAT遍历Target函数。需要判断DLLNAME是否是Target函数所在的模块,也就是说需要确定IID,毕竟一个IID对应一个DLL

         First指向的是一个IAT结构,存储的API函数地址。所以可以利用PIMAGE_THUNK_DATA(IAT)遍历Target函数。如果找到了最好保存修改的IAT地址和数据

         这里我实现了X64和X86的兼容,但是在实现X86的时候,出现了内存访问异常在VirtuallProtest处,但是在debug模式下程序没奔溃,需要那位大佬可以解答一下

         使用EAT_HOOK需要注意一下两点:第一:EAT存储的是函数地址的偏移,所以在HOOK EAT的时候需要加上基地址,在写入EAT的时候,Detour地址需要减去BaseAddress。第二,EAT不对隐式链接起作用,只对显示链接起作用,也就是说对于那种GetProcAddress的那种调用起作用。

         EAT_HOOK的原理和IAT_HOOK类似,都是通过修改函数地址数据从而HOOK。EAT_HOOK,也需要进行以下步骤:

         首先是获取Target函数RVA,因为EAT存的是函数的RVA,所以,我们需要获取Target函数RVA

         第二歩是获取导出函数数组首地址,在EAT->AddressOfFunctions可以获取导出函数地址数组AddressOfFunctions的首地址

         第三步是在EAT在寻找Target函数的RVA

         第四步是保存EAT地址和Target函数RVA

         EAT保存的是Target函数的RVA,这一定要记住。所以在写入Detour函数的时候,是需要减去BaseAddress的。

         C++虚函数存在的意义是为了方便使用多态性。在实现虚函数Hook的时候需要注意如下问题:1.在构建DetourFun函数的时候,一定要构造DetourClass,因为在调用虚函数的时候使用了Thiscall的函数调用约定,如果直接调用detourfun函数应该使用的标准调用约定,两者不统一,会出错。2.当使用Trampolinefun回调的时候,需要重新实例化一个TrampolineClass。

         第一步:仍然是构造DetourClass类和TrampolineClass类

         第二歩:将Target函数地址保存在TableTrampoline虚表中,方便回调。这时候需要获取两个值,第一个TableTrampoline虚表,第二个TargetFun地址。由于TableTrampoline虚函数表在类的起始位置。所以类的地址就是虚函数表的地址,第二,TargetFun函数地址位于虚函数中,存储在类似于数组的结构,可以用其索引指向获取虚函数地址

         第三步,将Detour函数地址写入到TargetClass的原始虚表TableToHook中

         这一类InlineHook是一类较为特殊的InlineHook,他修改的不是开始的多个字节,而是修改Target函数中call指令的地址。比如说VirtualAlloc函数中调用了VirtualAllocEx函数,这次inlineHook其实就是修改了VirtualAllocEx的地址(调用处的地址),这样做的好处是可以避免被一些Hook检测工具检测。但是这样的缺点是兼容性不是很好,因为一些API的函数可能会因为系统的改变而改变。

         如果比较无误后修改DetourFun

         这种InlineHook修改的是Target函数前5个字节,这种做的好处是能够多版本进行HOOK,因为如果是采用上一种InlineHook,可能内部调用流程随着版本不同而不同,所以不容易Hook。而这种就没有这种缺点。

         使用InlineHook,需要了解到三种函数:

         InlineHook主要的步骤就是修改Target函数的前五个字节。大概有以下几个步骤:

         虽然步骤看着简单,但是里面坑还是很多的。首先是构造构造Detour函数,这里需要注意的是Detour函数声明需要和Target函数保持一致,否则函数返回会异常,而且还要在DetourFun中还要调用TrampolineFun。

         第二歩是构造TrampolineFun函数,Trampoline函数是用于在Detour回调Target函数,在使用Trampoline首先执行Target被修改的三条指令,为了避免调用Target函数堆栈异常。然后使用jmp的方式跳转到Target函数中第四条指令,绕过被修改的指令,这是为了实现永久化

         在正式HOOK开始,还需要填充一些关键的参数,为此,我们构造一个结构体。以便管理参数。

         第三步是获取HookPoint和pfnTrampolineFun的地址,这一步的目的我也不是很清楚,但是我删除这两个指令,程序也是正常的。,接着设置回调点,这是为了在TrampolineFun中,设置跳转。回调点为了被修改指令之后

         第四步是填充我们修改的指令

         第五步:使用ReadProcessMemory保存原指令以便恢复HOOK

         第六步:使用WriteProcessMemory修改前三条指令

         题外话:例程中给的是当前进程HOOK,如果是需要跨进程的话,需要将InlineHook包装成dll,然后使用注入技术注入到目标进程,才能实现HOOK。

         在InlineHook(B)中,我们使用jmp指令跳转到Detour函数,这部分我们使用mov-jmp和push-ret,以及HotPatch的方法跳转到DetourFun。

         首先将跳转分为两种,第一种是一次性跳转,例如jmp,push-retn.mov-jmp等,第二种是HotPatch这种长短跳。对于第一种跳转比较简单,根据Hook的指令不同,可以选择长度为5.6.7不同的Hook指令。象jmp指令对应的是修改5个字节,而push-ret修改6个字节,mov-jmp修改的是七个字节。具体填充到HookPoint的数据如下:

         第二种跳转是长短跳,也就是HotPatch的方法,由于标准函数调用存在两种形式,分别是不存在SEH,和存在SEH的。对于第二种12个字节指令,我们推荐使用Hotpatch的方法,原理如下:因为在API上面存在nop或者int3,这些指令通常是微软用于实现HotPatch的。可以使用长短跳结合的方式占用上方的nop实现Hook,步骤是这样的:1.使用短跳到HookPoint上面5个字节HotPatchCode处, 2.然后使用长跳到DetourFun。

         InlineHook(C)属于InlineHook(B)的进阶版。所以基本步骤也是相同的。

         构造Detour函数和构造TrampolineFun函数,以及填充修改的指令和之前是一致的,第三步是保存原始数据,这样做是为了以后能够将头几条指令填充TrampolineFun

         第四步填充TrampolineFun函数

         第五步是向向HookPoint写入跳转数据,对于一次性跳转和之前是一致的,不再说明,重点是HotPatch。根据原理,在HookPaint处写入\xE8\xF9是跳转到EIP-5处也就是HotPatch处。可以在HookPoint前5个指令写入Hotpatch用于跳转到Detourfun,

         这一节主要讲x64下面的HOOK技术,和之前的x86下HOOK一样,都需要经历一下步骤:这一部分主要讲一下在X64位下面HOOK需要注意的地方。

         第一点,就是在X86下可以直接写入一段shellcode到TrampolineFun。但是在x64下不能内联汇编了,所以申请一块内存用做TrampolineFun的shellcode。然后使用才能填充Trampoline。但是教主给的例程使用第二段代码做重定位,不知道作用是什么,但是使用这段代码在WIN10下HOOK MessageBoxA是不正确的,可能是填充Trampoline出现了意外。或者在填写跳转地址的时候少了一个字节,跳到了在正常的代码上面的一个int 3处,导致异常。

         第二点,就是在X86下面,修改指令的长度最大是7个字节,在X64下变成了14个字节,而且地址长度变成了8个字节。并且使用的指令E9变成了FF25这种长跳转指令。

         VEH技术的主要原理是利用异常处理改变程序指令流程。通过主动抛出异常,使程序触发异常,控制权交给异常处理例程的这一系列操作来实现HOOK。

         这里简单提一下VEH,向量异常处理,基于VEH链表而不是栈,这样的话其作用范围是进程全局,而不是线程。且优先级也高于SEH,这也是VEH_HOOK的优势所在。

          VEH_HOOK通过异常机制实现HOOK,必不可少需要构造异常处理函数,同时也需要人为的构造异常,同时为了实现永久化机制,保证执行原操作需要实现TrampolineFun函数。所以总结VEH_HOOK步骤如下:

          构造TrampolineFun的目的1是为了执行原有流程,2是实现永久化。因为64位不支持内联汇编,所以需要开辟空间来存放shellcode。复制前四个指令,实现堆栈平衡,然后为了永久化,特定将跳转点定在函数开头后四个字节处。

         构造异常处理函数,这个函数就是Detour函数,同时也是我们异常处理的函数,所以他的参数是一个_EXCEPTION_POINTERS结构。根据看雪加密解密所介绍,他是一个陷进帧,用来存放EXCEPTION_RECORD和 CONTEXT_RECORD。EXCEPTION_RECORD保存发生异常的基本信息,如异常类型,发生异常的地址。而二是CONTEXT_RECORD用于保存上下文。

         因为X64是采用了类似于FastCall的调用约定,所以压栈顺序为RCX,RDX,R8,R9。同时也是从右到左的传参方式。所以,修改RDX就可以修改MsgBox的第二个参数。同时,别忘记修改Eip到TrampolineFun。对于x86平台下的,只需要修改栈顶第二个参数就可以了。

         设置异常,这里选择的是简单的断点异常。直接将Target原始代码修改一个字节为0xCC即可!

         SSDT中文全称为系统服务描述符表,其作用是作为R3和R0层的通道,将用户态API函数和内核函数联系起来。用简单的API函数举例子,我们调用了CreateFile,其会调用ZwCreateFile,然后调用NtCreateFile,经过参数和模式的检查,然后调用系统服务分发函数KiSystemService进入内核。在R0中通过传入的系统服务号(函数索引)得到系统服务的地址,然后调用该系统服务即可。

         所以,根据上述,我们可以知道SSDT其实是一个存储系统服务的数组。SSDT_HOOK其实就是在内核层的AddressHook。只不过他修改是系统服务描述符表数据。

         因为SSDT的索引号和系统服务内核地址是一一对应的,所以不需要向普通的AddressHook一一对比函数地址。所以让我们来屡一下执行SSDT的操作。我们有目的向原因开始。如果我们需要执行SSDT_HOOK的话,首先需要修改为与SSDT中的系统服务地址,但又由于系统服务地址是和服务索引是保持对应关系的,所以我们还需要获取索引号。

         根据上面的分析,我们知道首先需要获取服务索引号。但是服务索引号和函数地址对应的,在X86系统中,相对于导出函数偏移量1的地址往后读四个字节就是SSDT服务索引号。但是对于X64位的系统,却是函数地址偏移为4的地址读取四个字节。所以需要得到服务索引号,就需要得到导出函数地址。

         我们现在总结一下得到服务索引的步骤:

         SSDT适用于R0内,在内核层映射文件到内存和在应用层是一致的。只是使用的函数不一样,首先使用InitializeObjectAttributes初始化文件对象,然后使用ZwOpenFile获得映射文件句柄,接着使用ZwCreateSection创建创建一个节对象。最后使用ZwMapViewOfSection,这些都是固定的模板,代码如下。

         将Ntdll映射到内存中,然后就想普通的获取导出函数地址的方式获取对应的函数地址,然后根据公式获取服务索引。

         因为SSDT在x86系统上是由Ntoskrnl.exe导出,导出符号为KeServiceDesriptorTable,我们很容易获取SSDT的地址。只需要获取KeServiceDesriptorTable即可!也就是使用以下语句,同时给出_SERVICE_DESCIPTOR_TABLE结构,可以看到结构体第一个成员是SSDT基址,所以可以使用(PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex];获取函数基地址。

         我们已经找到了目标函数在SSDT的地址,最后,我们只需要在该地址处填写我们构造的函数地址即可!!但是这块内存是有保护属性的,所以我们需要使用MDL方式绕过写保护属性。

         同样的,卸载HOOK,只需要将之前的修改恢复就可以了

         IRP全称是IO请求包,发送到设备驱动程序的大多数请求都打包在IRP中。操作系统组件或驱动程序通过调用IoCallDriver将IRP发送给驱动程序。

         大概的执行流程是这样的:IO管理器创建一个IRP来代表一个IO操作,并且将该IRP传递给正确的驱动程序,当此IO操作完成时再处理该请求包。相对的,驱动程序(上层的虚拟设备驱动或者底层的真实设备驱动)接收一个IRP,执行该IRP指定的操作,然后将IRP传回给IO管理器,告诉它,该操作已经完成,或者应该传给另一个驱动以进行进一步处理。

         IO管理器可以使用一下三个函数创建IRP。但此时,IRP堆栈还没有被初始化,难以进行拦截。然后使用你可以调用IoGetNextIrpStackLocation函数获得该IRP第一个堆栈单元的指针。然后初始化这个堆栈单元。当初始化完成之后,就可以调用IoCallDriver函数把IRP发送到设备驱动程序了。这就可以在中途进行拦截啦。

         根据上述流程,执行IrpHook可以在三个地址进行,第一:在Irp初始化之后,第二:在发往派遣例程过程中,第三,直接修改需要拦截驱动对象派遣例程函数表。

         通过查看 IofCallDriver函数发现,在函数开头存在一个jmp指令。ff2500c85480其中ff25是jmp的机器码,后面的机器码是跳转的绝对地址。可以使用InlineHook直接修改跳转地址即可

         首先讲解一个重要的结构体_OBJECT_HEADER,使用WINDBG用dt _OBJECT_HEADER命令即可显示如下:
mark

        接着,我们来看一下OBJECT_TYPE,同样的使用windbgdt _OBJECT_TYPE即可查看
mark

        对于对象类型结构,主要的层次结构像一个树形或者说目录形。其主要的对象类型比如IoFileObjectType,PsProcessType,*PsThreadType。都是存在于ObjectTypes\Device。所以,只要生成对象就会创建指定的对象类型结构。

        最后讲解一下关于最后一个结构体OBJECT_TYPE_INITIALIZER,使用dt _OBJECT_TYPE_INITIALIZER就可以查看_OBJECT_TYPE_INITIALIZER的数据。在这个结构体中,最后8个函数指针是关乎HOOK的,这些函数能够决定对象的操作,比如说打开,创建,删除等。
mark

        当你调用NtCreateFile->IoCreateFile->ObOpenObjectByName->ObpLookupObjectName->IopParseFile->IopParseDevice
IopParseFile最终也会调用IopParseDevice
ObjectHook其实就是比如你要HOOK 创建打开就是OBJECT_TYPE_INITIALIZER->ParseProcedure,所以ObjectHook的关键就是Hook OBJECT_TYPE_INITIALIZER最后几个关键的函数。

        实现代码如下

        sysenter是由目态进入管态的CPU支持的快速系统调用的一条指令。在此之前,系统的切换是使用int 0x2E系统中断实现的。但是这样做的弊端是操作是非原子的,因为要进行大量的栈切换,需要多次访问内存。所以在后来使用了新的切换指令---sysenter/sysexit

        因为sysenter的原子性,这决定了管态和目态的无论是堆栈还是指令上的切换都是可以通过一条指令来实现,当然,同时,CPU也为其配备了相对应的寄存器。分别是SYSENTER_CS_MSR:0x174,SYSENTER_ESP_MSR:0x175,SYSENTER_EIP_MSR:0x176。并且我们可以通过rdmsr和wrmsr进行读写这三个寄存器。由于CS和EIP可以决定程序的流程,所以我们如何修改了SYSENTER_CS_MSR和SYSENTER_EIP_MSR的数据,将流程劫持到我们想要的路径,这样就实现了一次Hook。

        Hook流程大概是这样的

        但是上面的方法直接修改寄存器数据,这样容易被Hook检测工具检测,一般检测工具对于常见sysenterHook检测基于寄存器的值是否超过本模块范围,对于InlineHook一般检测函数起始数据是否是0xE9,然后检测后面的地址是否超过当前模块范围。如果我们使用FF25这类的转移指令,这样是不容易被察觉的。起始接下来的方法并不是严格意义上的sysenterHook,更像是属于InlineHook。

        但是,rdmsr对于的IP地址并不一定是KiFastCallEntry,按道理来说其地址应该是KiFastCallEntry,但是我的机器上显示的不是!看看哪位师傅可以给解释一下

        这一部分主要讲一下Hook的注意事项和部分大厂关于Hook的面经。部分面经之前讲解了,在这里不做赘述。

        首先是二次HOOK,就是被别人HOOK了之后自己再次HOOK,这里可以提供4种方法,第一可以换个位置HOOK第二就是替换原HOOK,也就是说将别人HOOK的指令修改为自己HOOK的指令。这样应该是比较有效的,但是需要注意的是修改指令数量一定要和对方的一致,或者修改之前将原来的HOOK还原,不然容易产生错误。第三,在Detour函数中HOOK第四,在Target函数中的原来HOOK的地址后面HOOK

        第二是X64下HOOK应该注意什么?首先X64和X86本质区别就是地址总线上的差别,一个是2^64次,一次传输64位数据,一个是2^32次,一次传输32位数据。由此造成的差异就是内存地址大小问题,在32位机器上主要是4个字节,64位机器上就变成了8个字节。这样的话对于指针的使用就需要考虑到两个架构上的兼容性和差异性。例如在32下可以使用ULONG,但是在64位下使用ULONG_PTR。这样就可以有效避免由于编码问题产生的异常(或者统一使用ULONG_PTR)。第二就是PE格式上,由于x86和x64PE结构上存在微小差异,所以在进行AddressHook的时候需要注意。第三可能涉及到跳转的问题

        由于地址长度导致跳转指令长度变化。想mov-jmp就需要利用2+8+2的长度进行跳转,又像push-ret的方法,在32位系统下直接push就是32位数据,但是64位下只能push32位数据,这样的话,只能先push低位数据,然后修改高位数据,例如这样:push 55667788h;mov [esp+4],11223344。再如使用jmp[addr]方法。FF25类型jmp在X86平台下是一种绝对偏移的跳转,但是在x64下也是一种相对偏移的跳转。计算公式为当前EIP+0x6(指令长度)

        检测HOOK:

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
typedef int(WINAPI *PFN_MessageBoxA)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
int WINAPI My_MessageBoxA(
    HWND hWnd,
    LPCTSTR lpText,
    LPCTSTR lpCaption,
    UINT uType
)
    bReturn = OldMessageBox(NULL, "You Are Hooked", "Warning", MB_OK);
    //2.你可以控制API函数的返回值
    BOOL bReturn = FALSE;
    return bReturn;
||#ifdef _WIN64
    ULONG_PTR TargetFunAddr = (ULONG_PTR)GetProcAddress(hModule, szFuncName);
    PULONG_PTR lpAddr = NULL;
    SIZE_T size = sizeof(PULONG_PTR);
||#else
    ULONG32  TargetFunAddr = (ULONG32)GetProcAddress(hModule, szFuncName);
    PULONG32 lpAddr = NULL;
    SIZE_T size = sizeof(PULONG32);
||#endif
//MARKDOWN原因,不能以#开头,所以加个||符号,实际代码不存在||符号
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)    ImageDirectoryEntryToData(hModToHook,
        TRUE,
        IMAGE_DIRECTORY_ENTRY_IMPORT,
        &ulSize);
while (pImportDescriptor->FirstThunk)
{
    //存放DllName
    szModName = (char*)((PBYTE)hModToHook + pImportDescriptor->Name);
    printf("[*]Cur Module Name:%s\n", szModName);
    //比较DLLName与目标DLL是否相同  使用stricmp函数是不区分大小写的
    if (stricmp(szModName, szModuleName) != 0)
    {
        pImportDescriptor++;
        continue;
    }
    ...
}
PIMAGE_THUNK_DATA pThunkData = (PIMAGE_THUNK_DATA)((BYTE*)hModToHook + pImportDescriptor->FirstThunk);
while (pThunkData->u1.Function)
{
    if ((*lpAddr) == TargetFunAddr)
    {
        //保存数据,修改为Detour函数地址
    }
}
//最好保存一下
if (pThunkPointer != NULL)   //保存修改内存的地址
{
    *pThunkPointer = lpAddr;
}
if (pOriginalFuncAddr != NULL)   //保存修改内存的数据,也就是Target函数的地址
{
    *pOriginalFuncAddr = *lpAddr;
}

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 22
支持
分享
最新回复 (21)
雪    币: 12332
活跃值: (5103)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2

多谢分享,学习了
2019-5-18 17:24
0
雪    币: 300
活跃值: (2472)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
mark
2019-5-18 17:51
0
雪    币: 441
活跃值: (186)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
大佬大佬 
2019-5-18 18:39
0
雪    币: 51
活跃值: (137)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
学习了大佬
2019-5-19 10:30
0
雪    币: 3496
活跃值: (749)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
能不能讲点应用啊,不会用,啥场景下可以用hook解决啥问题啊?小白求科普。。。
2019-5-19 14:21
0
雪    币: 498
活跃值: (2319)
能力值: ( LV12,RANK:356 )
在线值:
发帖
回帖
粉丝
7
kxzpy 能不能讲点应用啊,不会用,啥场景下可以用hook解决啥问题啊?小白求科普。。。
可功可防,攻就是改变执行流程,守就是过滤,拦截操作
2019-5-19 22:54
0
雪    币: 1129
活跃值: (2756)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
难得有人总结这么全,感谢分享
2019-6-3 02:15
0
雪    币: 7082
活跃值: (2978)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
感谢分享
2019-6-5 11:51
0
雪    币: 2920
活跃值: (690)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2019-10-8 14:56
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
感谢分享
2019-11-18 19:44
0
雪    币: 38
活跃值: (185)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
12
感谢分享
2019-11-20 17:31
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
能否问一下,使用IAT HOOK后怎么让原来API继续执行?
最后于 2020-3-29 11:35 被熊叔编辑 ,原因:
2020-3-29 11:35
0
雪    币: 498
活跃值: (2319)
能力值: ( LV12,RANK:356 )
在线值:
发帖
回帖
粉丝
14
熊叔 能否问一下,使用IAT HOOK后怎么让原来API继续执行?
你到Detour函数之后把原来的IAT修改回去就好了
2020-3-29 14:11
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
findreamwang 你到Detour函数之后把原来的IAT修改回去就好了
可能我描述的不是很清楚把,我是想修改IAT指向我自己的函数,执行完成后,继续执行原来程序的API调用,而IAT不修改回去。 我是这样完成这个操作的:
简单的让他的参数传递下去执行。但是太繁琐了,如果有很多API需要hook的话,要写死的。。。。博主有什么好办法吗?
2020-3-29 14:53
0
雪    币: 498
活跃值: (2319)
能力值: ( LV12,RANK:356 )
在线值:
发帖
回帖
粉丝
16
熊叔 可能我描述的不是很清楚把,我是想修改IAT指向我自己的函数,执行完成后,继续执行原来程序的API调用,而IAT不修改回去。 我是这样完成这个操作的:简单的让他的参数传递下去执行。但是太繁琐了,如果有 ...
可以,提前获取到你Hook的函数地址,然后call
2020-3-29 19:38
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
findreamwang 可以,提前获取到你Hook的函数地址,然后call
谢谢博主,我去尝试一下。
2020-3-30 18:43
0
雪    币: 545
活跃值: (247)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
18
继续学习
2020-5-19 19:57
0
雪    币: 20
活跃值: (451)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
牛逼
2020-7-4 21:00
0
雪    币: 5
活跃值: (59)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
太牛B了,虽然现在还不懂怎么用,先保存了
2020-8-4 16:15
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
太牛B了,虽然现在还不懂怎么用,先保存了
2020-8-5 10:37
0
雪    币: 19950
活跃值: (4942)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
感谢分享
2020-8-6 06:52
0
游客
登录 | 注册 方可回帖
返回
//