首页
社区
课程
招聘
[原创]详谈内核三步走Inline Hook实现
发表于: 2009-9-25 18:52 100294

[原创]详谈内核三步走Inline Hook实现

2009-9-25 18:52
100294

本文以发表在黑防09期

详谈内核三步走Inline Hook实现

(一)Inline hook原理
Inline hook通俗的说就是对函数执行流程进行修改,达到控制函数过滤操作的目的。理论上我们可以在函数任何地方把原来指令替换成我们的跳转指令,也确实有些人在inline
的时候做的很深,来躲避inline 的检测,前提是必须对函数的流程和指令非常熟悉,且这种深层次的inlline 不具有通用性,稳定性也是问题。本文讨论的是具有通用性的两类inline的实现。
Inline hook原理:解析函数开头的几条指令,把他们Copy到数组保存起来,然后用一个调用我们的函数的几条指令来替换,如果要执行原函数,则在我们函数处理完毕,再执行我们保存起来的开头几条指令,然后调回我们取指令之后的地址执行。
整个Inline hook的过程就大体这样,中间牵扯到对函数的检查,地址的获取就直接调用函数即可。
本文所要讨论的两类Inline hook都是基于上面原理。

说明三点:
1、堆栈平衡是重中之重,参数压栈也需要格外注意
2、CR0寄存器中的WP位控制处理器是否允许往只读内存页写入,为0禁用保护机制。
3、提高中断级别到DISPATCH_LEVEL,禁止线程切换产生的中断
                     
(二)inline hook应用
Inline hook可分为两类:
(1)inline 导出函数,选择ObReferenceObjectByHandle做例子。
(2)inline 未导出函数,选择KiInsertQueueApc做例子。
导出函数前几个字节可以利用windbg自己查看是什么内容,而未导出函数就需要自己解析指令确定需要hook几个字节,其间还有很多问题需要注意。当大家真正的弄懂了我这篇文章,回头再看inline hook就会觉得inline也不过如此。
下面通过2个例子来讲inline hook的使用(这部分知识网上也有很多,但都很零散不系统,本文部分思路及代码的确参考了网上资源,有抄袭之嫌,希望读者谅解。我一直强调“授人以鱼不如授人以渔”,代码并不重要,关键是思想。)
1、inline hook ObReferenceObjectByHandle保护进程
ObReferenceObjectByHandle属于ntoskrnl.exe导出函数,在内核中调用频繁。
NtCreateProcess创建进程需要调用ObReferenceObjectByHandle,NtTerminateProcess需要调用ObReferenceObjectByHandle,基于这我们就可以利用Hook来保护进程同时屏蔽进程的创建。
效果:已经运行的记事本任务管理器无法结束
流程:
HookObReferenceObjectByHandle------DetourMyObReferenceObjectByHa ndle----------UnHookObReferenceObjectByHandle
核心代码分析如下:
//=======================================inline HOOK ObReferenceObjectByHandle===========================

//ObReferenceObjectByHandle是ntoskrnl.exe导出函数,采用HOOK前五个字节的方式

//字节型数据  unsigned char
ULONG  CR0VALUE;
BYTE  OriginalBytes[5]={0};             //保存原始函数前五个字节           
BYTE JmpAddress[5]={0xE9,0,0,0,0};       //跳转到HOOK函数的地址

extern POBJECT_TYPE *PsProcessType;

NTKERNELAPI NTSTATUS ObReferenceObjectByHandle(
                                                                                          
                                                                                           IN HANDLE  Handle,
                                                                                           IN ACCESS_MASK  DesiredAccess,
                                                                                           IN POBJECT_TYPE  ObjectType  OPTIONAL,
                                                                                           IN KPROCESSOR_MODE  AccessMode,
                                                                                           OUT PVOID  *Object,
                                                                                           OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
                                                                                          
                                                                                           );

//HOOK函数

NTSTATUS DetourMyObReferenceObjectByHandle(
                                                                                  
                                                                                   IN HANDLE  Handle,                                  
                                                                                   IN ACCESS_MASK  DesiredAccess
                                                                                   IN POBJECT_TYPE  ObjectType  OPTIONAL,
                                                                                   IN KPROCESSOR_MODE  AccessMode,
                                                                                   OUT PVOID  *Object,
                                                                                   OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL);

//

//hook流程 HookObReferenceObjectByHandle---DetourMyObReferenceObjectByHandle---UnHookObReferenceObjectByHandle

void  HookObReferenceObjectByHandle()

{
       
        //赋值前面定义的数组
        KIRQL Irql;
        KdPrint(("[ObReferenceObjectByHandle] :0x%x",ObReferenceObjectByHandle));  //地址验证
        //保存函数前五个字节内容
        RtlCopyMemory(OriginalBytes,(BYTE *)ObReferenceObjectByHandle,5);
        //保存新函数五个字节之后偏移
        *(ULONG *)(JmpAddress+1)=(ULONG)DetourMyObReferenceObjectByHandle-((ULONG)ObReferenceObjectByHandle+5);
        //开始inline hook
        //关闭内存写保护
        _asm
               
        {
                push eax
                       
                        mov eax, cr0
                        mov CR0VALUE, eax
                        and eax, 0fffeffffh  
                        mov cr0, eax
                        pop eax
        }
       
        //提升IRQL中断级
        Irql=KeRaiseIrqlToDpcLevel();
        //函数开头五个字节写JMP
        RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,JmpAddress,5);
        //恢复Irql
        KeLowerIrql(Irql);
        //开启内存写保护
       
        __asm
               
        {      
               
                push eax
                       
                        mov eax, CR0VALUE
                       
                        mov cr0, eax
                       
                        pop eax
                       
        }
       
}

_declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle(IN HANDLE  Handle,
                                                                                                                         
                                                                                                                         IN ACCESS_MASK  DesiredAccess,
                                                                                                                         
                                                                                                                         IN POBJECT_TYPE  ObjectType  OPTIONAL,
                                                                                                                         
                                                                                                                         IN KPROCESSOR_MODE  AccessMode,
                                                                                                                         
                                                                                                                         OUT PVOID  *Object,
                                                                                                                         
                                                                                                                         OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL)
                                                                                                                         
{
       
        _asm
               
        {   
               
                mov edi,edi
                        push ebp
                        mov ebp,esp
                        mov eax,ObReferenceObjectByHandle
                        add eax,5
                        jmp eax               
                       
        }
       
}

NTSTATUS DetourMyObReferenceObjectByHandle(
                                                                                  
                                                                                   IN HANDLE  Handle,
                                                                                  
                                                                                   IN ACCESS_MASK  DesiredAccess,
                                                                                  
                                                                                   IN POBJECT_TYPE  ObjectType  OPTIONAL,
                                                                       
                                                                                   IN KPROCESSOR_MODE  AccessMode,
                                                                                  
                                                                                   OUT PVOID  *Object,
                                                                                  
                                                                                   OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL)
                                                                                  
{
       
        NTSTATUS status;
       
        //调用原函数
       
        status=OriginalObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,Object,HandleInformation);
       
        if((status==STATUS_SUCCESS)&&(DesiredAccess==1))
               
        {   
               
                if(ObjectType== *PsProcessType)
                       
                {
                       
                        if( _stricmp((char *)((ULONG)(*Object)+0x174),"notepad.exe")==0)
                               
                        {   
                               
                                ObDereferenceObject(*Object);
                               
                                return STATUS_INVALID_HANDLE;
                               
                        }
                       
                }
               
        }
       
        return status;
       
}

void UnHookObReferenceObjectByHandle()

{
       
        //把五个字节再写回到原函数
       
        KIRQL Irql;
       
    //关闭写保护
       
        _asm
               
        {
               
                push eax
                       
                        mov eax, cr0
                       
                        mov CR0VALUE, eax
                       
                        and eax, 0fffeffffh  
                       
                        mov cr0, eax
                       
                        pop eax
                       
        }
       
    //提升IRQL到Dpc
       
    Irql=KeRaiseIrqlToDpcLevel();
       
        RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,OriginalBytes,5);
       
        KeLowerIrql(Irql);
       
    //开启写保护
       
        __asm
               
        {      
               
                    push eax
                        mov eax, CR0VALUE
                        mov cr0, eax
                       
                        pop eax
                       
        }
}

驱动加载后,结束记事本程序如下:

    (图 一)

详细分析:
1、ObReferenceObjectByHandle分析
NTSTATUS
  ObReferenceObjectByHandle(
    IN HANDLE  Handle,
    IN ACCESS_MASK  DesiredAccess,
    IN POBJECT_TYPE  ObjectType  OPTIONAL,
    IN KPROCESSOR_MODE  AccessMode,
    OUT PVOID  *Object,
    OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
    );
函数原型如上,由句柄获取对象指针,函数返回值:
STATUS_SUCCESS                        调用成功
STATUS_OBJECT_TYPE_MISMATCH        
STATUS_ACCESS_DENIED                 权限不够
STATUS_INVALID_HANDLE                无效句柄         

调用NtTerminateProcess需要调用ObReferenceObjectByHandle,因此我们通过对函数返回值进程修改来达到保护进程。但是NtCreateProcess(最终调用的PspCreateProcess)同样调用这个函数,如果不加区分的话,创建进程同样被禁止了,那么如何区分到底是谁在调用呢。参考WRK,我发现可以通过第二个参数DesiredAccess来判别,创建进程和结束进程第二个参数明显不同,PROCESS_CREATE_PROCESS和PROCESS_TERMINATE,问题就解决了。
PspCreateProcess位于 WRK-v1.2\base\ntos\ps\create.c
调用ObReferenceObjectByHandle代码:
Status = ObReferenceObjectByHandle (ParentProcess,
                                            PROCESS_CREATE_PROCESS,
                                            PsProcessType,
                                            PreviousMode,
                                            &Parent,
                                            NULL);
NtTerminateProcess位于 WRK-v1.2\base\ntos\ps\psdelete.c
调用ObReferenceObjectByHandle代码:
st = ObReferenceObjectByHandle (ProcessHandle,
                                    PROCESS_TERMINATE,
                                    PsProcessType,
                                    KeGetPreviousModeByThread(&Self->Tcb),
                                    &Process,
                                    NULL);
DesiredAccess参数说明:
#define PROCESS_TERMINATE         (0x0001) // winnt
#define PROCESS_CREATE_THREAD     (0x0002) // winnt
#define PROCESS_SET_SESSIONID     (0x0004) // winnt
#define PROCESS_VM_OPERATION      (0x0008) // winnt
#define PROCESS_VM_READ           (0x0010) // winnt
#define PROCESS_VM_WRITE          (0x0020) // winnt
// begin_ntddk begin_wdm begin_ntifs
#define PROCESS_DUP_HANDLE        (0x0040) // winnt
// end_ntddk end_wdm end_ntifs
#define PROCESS_CREATE_PROCESS    (0x0080) // winnt
#define PROCESS_SET_QUOTA         (0x0100) // winnt
#define PROCESS_SET_INFORMATION   (0x0200) // winnt
#define PROCESS_QUERY_INFORMATION (0x0400) // winnt
#define PROCESS_SET_PORT          (0x0800)
#define PROCESS_SUSPEND_RESUME    (0x0800) // winnt

2、函数调用说明
C语言中我们调用一个函数就直接写函数名就可以,但是实际是进行了下面的操作:
把函数参数压入堆栈,压入函数返回地址,调用函数,为新函数开辟堆栈空间申请局部变量,
恢复堆栈保持堆栈平衡
(_stdcall调用方式)汇编代码就是:
Push 参数4
Push 参数3
Push 参数2
Push 参数1
Call  函数  ;call指令同时完成2个操作,一是把返回地址压入堆栈,二跳转到调用函数入口地址

Push  ebp
Mov ebp,esp
Sub  esp, XX  ;开辟栈帧空间
……
Add  esp ,XX
Pop ebp
Retn          ;恢复堆栈平衡
堆栈详细情况:
ESP
局部变量

EBP
返回地址
参数1
参数2
参数3
参数4
堆栈是由高地址到低地址。
参数就通过EBP来去,四字节对齐的

参数4----------------------EBP+0x14
参数3----------------------EBP+0x10
参数2----------------------EBP+0xc
参数1--------------------- EBP+0x8
局部变量则通过Ebp-XX来获取

因此inline的时候要时刻考虑堆栈平衡,破坏了堆栈平衡就会导致函数崩溃。
我通常inline hook的思路就是三步走:
HOOK函数-----DetourMy处理函数----------UnHook函数
处理函数中对返回结果或者中间数据进行修改处理,然后调用原始函数。由于在我们处理的时候原始函数已经被hook了,所以我自己构造了一个原始函数,但是由于参数在我们hook前已经压人堆栈了,所以这里我们不用重新开辟栈帧,因此声名函数类型为_declspec (naked)
。有人就会问那么你调用处理函数的时候,参数不是重复压栈了,这里请注意,我们是通过JMP方式跳转到我们处理函数入口地址的,而不是Call的形式,所以并没有执行上面所说的函数调用过程,参数仍然是原始函数的。也就是说在真个inline hook过程中我们不能破坏原始栈帧的EBP。

关于函数调用很栈帧的相关联系可能比较难理解,我也在尽肯能的用通俗的话来解释清楚,有什么不理解的地方或者个人见解欢迎大家跟我交流。

2、inline hook KiInsertQueueApc对抗插APC杀进程
KiInsertQueueAPc为内核未导出函数,我下面提供的代码可以作为未导出函数inline的通用模板来使用,大家根据自己需要进行修改,基于inline ObReferenceObjectByHandle已经把原理分析了,这部分我就不详加分析,仍然采用的但不走,Hook函数---DetourMy函数---UnHook函数
直接看核心代码:
//===================inline hook KiInsertQueueApc====================
//KiInsertQueueApc为内核未导出函数,可以从导出函数KeInsertQueueApc定位
//修改KiInsertQueueApc开头5字节
//处理函数思路:apc-->kthread---apc_state--eprocess--进程名字
//HookKiInsertQueueApc---DetourMyKiInsertQueueApc---UnHookKiInsertQueueApc
ULONG CR0VALUE;
ULONG g_KiInsertQueueApc;
           
BYTE JmpAddress[5]={0xE9,0,0,0,0};       //跳转到HOOK函数的地址
BYTE  OriginalBytes[5]={0};             //保存原始函数前五个字

VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment);

VOID WPOFF()
{
        _asm
               
        {
               
                push eax
                       
                        mov eax, cr0
                       
                        mov CR0VALUE, eax
                       
                        and eax, 0fffeffffh  
                       
                        mov cr0, eax
                       
                        pop eax
                        cli
                       
        };
       
}

VOID WPON()
{
    __asm
               
        {      
                sti
                push eax
                       
                        mov eax, CR0VALUE
                       
                        mov cr0, eax
                       
                        pop eax
                       
        };
}
//1、获取KiInsertQueueApc地址
ULONG GetFunctionAddr( IN PCWSTR FunctionName)     //PCWSTR常量指针,指向16位UNICODE
{
        UNICODE_STRING UniCodeFunctionName;
        RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
        return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName );   
}

ULONG GetKiInsertQueueApcAddr()
{
        ULONG sp_code1=0x28,sp_code2=0xe8,sp_code3=0xd88a;  //特征码,sp_code3 windbg显示错误,应该为d88a
        ULONG address=0;
        PUCHAR addr;
        PUCHAR p;
        addr=(PUCHAR)GetFunctionAddr(L"KeInsertQueueApc");
        for(p=addr;p<p+PAGE_SIZE;p++)
        {
                if((*(p-1)==sp_code1)&&(*p==sp_code2)&&(*(PUSHORT)(p+5)==sp_code3))
                {
                        address=*(PULONG)(p+1)+(ULONG)(p+5);
                        break;
                }
        }
        KdPrint(("[KeInsertQueueApc] addr %x\n",(ULONG)addr));
    KdPrint(("[KiInsertQueueApc] address %x\n",address));
    return address;
}

VOID HookKiInsertQueueApc()
{   
        KIRQL Irql;
        g_KiInsertQueueApc=GetKiInsertQueueApcAddr();
        KdPrint(("[KiInsertQueueApc] KiInsertQueueApc %x\n",g_KiInsertQueueApc));
    // 保存原函数的前字节内容
    RtlCopyMemory (OriginalBytes, (BYTE*)g_KiInsertQueueApc, 5);
        //新函数对原函数的偏移地址
    *( (ULONG*)(JmpAddress + 1) ) = (ULONG)DetourMyKiInsertQueueApc - (ULONG)g_KiInsertQueueApc - 5;
    // 禁止系统写保护,提升IRQL到DPC
    WPOFF();
    Irql = KeRaiseIrqlToDpcLevel();
    //inline hook函数
        RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, JmpAddress, 5 );
    // 恢复写保护,降低IRQL
    KeLowerIrql(Irql);
    WPON();       
}
//原函数
_declspec (naked) VOID FASTCALL OriginalKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment)
{
        _asm
        {
                //前五个字节
                mov edi,edi
                        push ebp
                        mov ebp,esp
                       
                        mov eax,g_KiInsertQueueApc
                        add eax,5
                        jmp eax
        }
}
//处理函数
//apc--kthread--apc_state--eprocess
VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment)
{
        ULONG thread;
        ULONG process;
        if(MmIsAddressValid((PULONG)((ULONG)Apc+0x008)))    //地址验证 KAPC结构+008--->kthread
                thread=*((PULONG)((ULONG)Apc+0x008));
        else
                return ;
        if(MmIsAddressValid((PULONG)((ULONG)thread+0x044))) //kthread+30-->KAPC_STATE+10-->eprocess
                process=*((PULONG)((ULONG)thread+0x044));
        else
                return ;
    if(MmIsAddressValid((PULONG)((ULONG)process+0x174)))  //eprocess+174---->进程名字
        {
                if((_stricmp((char *)((ULONG)process+0x174),"notepad.exe")==0)&&(Increment==2))
                {
                        return ;

                }
                else
                        OriginalKiInsertQueueApc(Apc,Increment);

        }
        else
                return;
}

//卸载函数
VOID UnHookKiInsertQueueApc()
{
        KIRQL Irql;
    WPOFF();
    Irql = KeRaiseIrqlToDpcLevel();
    //inline hook函数
    RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, OriginalBytes, 5);
    // 恢复写保护,降低IRQL
    KeLowerIrql(Irql);
    WPON();       
}
考虑到大家水平不一,对一些问题我详细如下:
1、特征码的寻找
利用windbg的kernel debug来查找:
uf  KeInsertQueueApc
nt!KeInsertQueueApc+0x3b:
804e6d0a 8b450c          mov     eax,dword ptr [ebp+0Ch]
804e6d0d 8b5514          mov     edx,dword ptr [ebp+14h]
804e6d10 894724          mov     dword ptr [edi+24h],eax
804e6d13 8b4510          mov     eax,dword ptr [ebp+10h]
804e6d16 8bcf            mov     ecx,edi
804e6d18 894728          mov     dword ptr [edi+28h],eax
804e6d1b e8523fffff        call    nt!KiInsertQueueApc (804dac72)
804e6d20 8ad8 (错误)  mov     bl,al
特征码就是sp_code1=0x28 sp_code2=0xe8 sp_code3=0xd88a(windbg显示有误,应该是d88a

这种方法就是通过已导出函数定位未导出函数通常使用的方法,具有通用性。详细见代码。

2、取EPRocess的过程
Apc-----kthread-----apc_state—eprocess
dt  _KAPC             偏移0x008指向KTHREAD
dt  _KTHREAD         偏移0x034指向KAPC_STATE
dt  _KAPC_STATE      偏移0x10指向EPROCESS
dt  _EPROCESS         偏移0x174指向进程名

(三)总结
很多人觉得inline hook比较难,处理起来很麻烦。但是我相信看完我这篇文章,你一定不会这么认为了,inline hook其实只要细心,注意细节跟别的hook没什么两样。本人采用的三步走inline hook做到了把inline简单化,同时有保证了堆栈的平衡。
由于代码采用的硬编码,编译环境是sp3+VMware,请根据自己操作系统自行修改。欢迎读者跟我交流。


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

上传的附件:
收藏
免费 7
支持
分享
最新回复 (75)
雪    币: 254
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
总结下来就是好。

借个位置问个问题,现在学文件系统,有啥好书介绍,Windows NT File System Internals 好像是97年的

是不是就这本比较好啦?
2009-9-25 19:03
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
3
有点长,要是能提供到附件一份下载就好了
2009-9-25 19:04
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
4
已经上传附件
2009-9-25 19:28
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
5
我是看的《寒江独钓》
2009-9-25 19:30
0
雪    币: 522
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
简单的问题复杂化  是我辈的强项  
2009-9-25 20:50
0
雪    币: 306
活跃值: (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
同意楼上观点
2009-9-25 20:53
0
雪    币: 7651
活跃值: (523)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
8
其实有点调试和编程的基础,搞这玩意儿很容易
2009-9-25 22:02
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
9
就是没有啊调试基础啊
2009-9-25 22:50
0
雪    币: 375
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
10
早点发你就可以跟HD同期了。。哈哈
2009-9-26 01:25
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
11
3、提高中断级别到DPC,禁止线程切换
====


2、R0模式下内存是不允许写的,需要去除写保护,设置CR0寄存器
===
我太囧了
2009-9-26 02:10
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
12
被BS了,没办法啊。我菜鸟啊
2009-9-26 09:55
0
雪    币: 5
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
新手,下来研究看看
2009-9-26 10:38
0
雪    币: 7651
活跃值: (523)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
14
被鄙视了就赶紧找找原因,别让你的错误再误导其它人
2009-9-26 15:12
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
15
2009-9-26 17:42
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
16
我修改完了 请继续指导,关于IRQL我不是很理解
2009-9-26 18:00
0
雪    币: 284
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
mobai sysdog老师~
真的应该和HD大神同期发布
学习ing...期待被误导~~
2009-9-26 18:06
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
18
只写新手看的懂的
2009-9-26 19:18
0
雪    币: 1334
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
纵有千般错误,但是作为一个初学者的楼主能写这个文章也是一个极好起点, 每个人都不是生而知之,都是学而知之,  希望楼主能把学习态势长久保持下去,积累是最重要的
2009-9-26 20:50
0
雪    币: 92
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
非常感谢楼主,看了这文章,算是对inline hook比较了解了,自己试试去了,哈哈
2009-9-26 21:20
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
21
才三行字,你就被打击成这样了?
刚开始我还没在意,没想到化学效应那么强大,无法学习,只能膜拜MJ
2009-9-26 21:21
0
雪    币: 92
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
原来这文章我手上这本黑防上有,才注意到。。
2009-9-26 23:08
0
雪    币: 773
活跃值: (442)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
23
最好能给份源代码 谢谢,要不实现太麻烦了
2009-9-26 23:33
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
24
谢谢尽管在某些方面误导新手,但是我相信对刚刚入门想学inline的还是存在可取之处的
2009-9-27 09:16
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
25
inline有很多种写法,但是我觉得我这个非常好理解
2009-9-27 09:20
0
游客
登录 | 注册 方可回帖
返回
//