挂钩Windows API

来源:互联网 发布:linux下安装jdk环境 编辑:程序博客网 时间:2024/04/27 19:14

===========================[ 挂钩Windows API ]==================

                                    SoBeIt

                       Author:  Holy_Father <holy_father@phreaker.net>
                       Version: 1.1 english
                       Date:    6.10.2002

=====[ 1. 内容 ]=============================================

1. 内容
2. 介绍
3. 挂钩方法
  3.1 运行前挂钩
  3.2 运行时挂钩
    3.2.1 使用IAT挂钩本进程
    3.2.2 改写入口点挂钩本进程
    3.2.3 保存原始函数
    3.2.4 挂钩其它进程
      3.2.4.1 DLL注入
      3.2.4.2 独立的代码
      3.2.4.3 原始修改
4. 结束语


=====[ 2. 介绍 ]====================================================

    这篇文章是有关在OS Windows下挂钩API函数的方法。所有例子都在基于NT技术的Windo
ws版本NT 4.0及以上有效(Windows NT 4.0, Windows 2000, Windows XP)。可能在其它Wind
ows系统也会有效。
    你应该比较熟悉Windows下的进程、汇编器、PE文件结构和一些API函数,才能明白这篇
文章里的内容。
    这里使用"Hooking API"这个术语表示对API的完全修改。当调用被挂钩的API时,我们的
代码能立刻被执行。我将写下完全的挂钩过程。


=====[ 3. 挂钩方法 ]==============================================

    一般来说我们的目的是用我们的代码取代一些函数里的代码。这些问题有时可以在进程
运行前解决。这些大多数时候可以用我们运行的用户级进程来完成,目的可以是修改程序的
行为。举个例子应用程序的破解,比方说有些程序会在启动时需要原光盘,我们想要不用光
盘就启动它。如果我们修改获取驱动类型的函数我们就可以让程序从硬盘启动。
    当我们挂钩系统进程时(比如说服务)这些不可能做到或者我们不打算这么做,或者在这
个例子里我们不知道哪个进程才是目标。这时我们就要用到动态挂钩(在运行时挂钩)的技术
。使用的例子有rootkit或者病毒里的反杀毒软件的技术。


=====[ 3.1 运行前挂钩 ]===========================================

    这里修改我们想要修改函数来自的物理模块(大多数时候是.exe或.dll)。在这里我们至
少有3种可能的做法。
    第一种可能是找到函数的入口点然后重写它的代码。这会因为函数的大小而受限制,但
我们能动态加载其它一些模块(API LoadLibrary),所以应该足够了。
    内核函数(kernel32.dll)是通用的因为Windows中每个进程都有这个模块的拷贝。另一个
好处是如果我们知道哪些模块在某版本中会修改,我们可以在一些API如LoadLibraryA中使用
直接的指针。这是因为kernel模块在内存中地址在相同Windows版本中是固定的。我们同样也
能用动态加载的模块的作用。在这里它的初始化部分在加载进内存后立刻就运行。在新模块
的初始化部分我们不受限制。
    第二种可能是在模块中被代替的函数只是原函数的扩展。然后我们选择要么修改开始的
5个字节为跳转指令或者改写IAT。如果改为跳转指令,那么将会改变指令执行流程转为执行
我们的代码。如果调用了IAT记录被修改的函数,我们的代码能在调用结束后被执行。但模块
的扩展没那么容易,因为我们必须注意DLL首部。
    下一个是修改整个模块。这意味着我们创建自己的模块版本,它能够加载原始的模块并
调用原始的函数,当然我们对这个不感兴趣,但重要的函数都是被更新的。这种方法对于有
的模块过大有几百个导出函数的很不方便。


=====[ 3.2 运行时挂钩 ]==========================================

    在运行前挂钩通常都非常特殊,并且是在内部面向具体的应用程序(或模块)。如果我们
更换了kernel32.dll或ntdll.dll里的函数(只在NT操作系统里),我们就能完美地做到在所有
将要运行的进程中替换这个函数。但说来容易做起来却非常难,因为我们不但得考虑精确性
和需要编写比较完善的新函数或新模块,但主要问题是只有将要运行的进程才能被挂钩(要挂
钩所有进程只能重启电脑)。另一个问题是如何进入这些文件,因为NT操作系统保护了它们。
比较好的解决方法在进程正在运行时挂钩。这需要更多的有关知识,但最后的结果相当不错
。在运行中挂钩只对能够写入它们的内存的进程能成功。为了能写入它自己我们使用API函数
WriteProcessMemory。现在我们开始运行中挂钩我们的进程。


=====[ 3.2.1 使用IAT挂钩本进程 ]===================================

    这里有很多种可能性。首先介绍如何用改写IAT挂钩函数的方法。接下来这张图描述了P
E文件的结构:

     +-------------------------------+     - offset 0
     | MS DOS标志("MZ") 和 DOS块     |
     +-------------------------------+    
     |      PE 标志 ("PE")           |
     +-------------------------------+
     |             .text             |     - 模块代码
     |           程序代码            |
     |                               |
     +-------------------------------+
     |             .data             |     - 已初始化的(全局静态)数据
     |          已初始化的数据       |
     |                               |
     +-------------------------------+
     |            .idata             |     - 导入函数的信息和数据
     |            导入表             |      
     |                               |
     +-------------------------------+
     |            .edata             |     - 导出函数的信息和数据
     |            导出表             |      
     |                               |
     +-------------------------------+
     |           调试符号            |
     +-------------------------------+

    这里对我们比较重要的是.idata部分的导入地址表(IAT)。这个部分包含了导入的相关信
息和导入函数的地址。有一点很重要的是我们必须知道PE文件是如何创建的。当在编程语言
里间接调用任意API(这意味着我们是用函数的名字来调用它,而不是用它的地址),编译器并
不直接把调用连接到模块,而是用jmp指令连接调用到IAT,IAT在系统把进程调入内存时时会
由进程载入器填满。这就是我们可以在两个不同版本的Windows里使用相同的二进制代码的原
因,虽然模块可能会加载到不同的地址。进程载入器会在程序代码里调用所使用的IAT里填入
直接跳转的jmp指令。所以我们能在IAT里找到我们想要挂钩的指定函数,我们就能很容易改
变那里的jmp指令并重定向代码到我们的地址。完成之后每次调用都会执行我们的代码了。这
种方法的缺点是经常有很多函数要被挂钩(比方说如果我们要在搜索文件的API中改变程序的
行为我们就得修改函数FindFirstFile和FindNextFile,但我们要知道这些函数都有ANSI和W
IDE版本,所以我们不得不修改FindFirstFileA、FindFirstFileW、FindNextFileA和FileNe
xtFileW的IAT地址。但还有其它类似的函数如FindFirstFileExA和它的WIDE版本FindFirstF
ileExW,也都是由前面提到的函数调用的。我们知道FindFirstFileW调用FindFirstFileExW
,但这是直接调用,而不是使用IAT。再比如说ShellAPI的函数SHGetDesktopFolder也会直接
调用FindFirstFilwW或FindFirstFileExW)。如果我们能获得它们所有,结果就会很完美。
    我们通过使用imagehlp.dll里的ImageDirectoryEntryToData来很容易地找到IAT。

    PVOID ImageDirectoryEntryToData(
        IN LPVOID Base,   
        IN BOOLEAN MappedAsImage,   
        IN USHORT DirectoryEntry,   
        OUT PULONG Size   
    );

在这里Base参数可以用我们程序的Instance(Instance通过调用GetModuleHandle获得):

    hInstance = GetModuleHandleA(NULL);

DirectoryEntry我们可以使用恒量IMAGE_DIRECTORY_ENTRY_IMPORT。

    #define IMAGE_DIRECTORY_ENTRY_IMPORT 1

    函数的结果是指向第一个IAT记录指针。IAT的所有记录是由IMAGE_IMPORT_DESCRIPTOR定
义的结构。所以函数结果是指向IMAGE_IMPORT_DESCRIPTOR的指针。

    typedef struct _IMAGE_THUNK_DATA {
        union {
            PBYTE ForwarderString;
            PDWORD Function;
            DWORD Ordinal;
            PIMAGE_IMPORT_BY_NAME AddressOfData;
        } ;
    } IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA;

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD Characteristics;
            PIMAGE_THUNK_DATA OriginalFirstThunk;
        } ;
        DWORD TimeDateStamp;
        DWORD ForwarderChain;
        DWORD Name;
        PIMAGE_THUNK_DATA FirstThunk;
    } IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;

    IMAGE_IMPORT_DESCRIPTOR里的Name成员变量是模块名字的指针。如果我们想要挂钩某个
函数比如是来自kernel32.dll我们就在导入表里找属于名字kernel32.dll的描述符号。我们
先调用ImageDirectoryEntryToData然后找到名字是"kernel32.dll"的描述符号(可能不只一
个描述符号是这个名字),最后我们在这个模块的记录里所有函数的列表里找到我们想要的函
数(函数地址通过GetProcAddress函数获得)。如果我们找到了就必须用VirtualProtect函数
来改变内存页面的保护属性,然后就可以在内存中的这些部分写入代码了。在改写了地址之
后我们要把保护属性改回来。在调用VirtualProtect之前我们还要先知道有关页面的信息,
这通过VirtualQuery来实现。我们可以加入一些测试以防某些函数会失败(比方说如果第一次
调用VirtualProctect就失败了,我们就没办法继续)。

    PCSTR pszHookModName = "kernel32.dll",pszSleepName = "Sleep";
    HMODULE hKernel = GetModuleHandle(pszHookModName);
    PROC pfnNew = (PROC)0x12345678,       //这里存放新地址
        pfnHookAPIAddr = GetProcAddress(hKernel,pszSleepName);

    ULONG ulSize;
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
        (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(
            hKernel,
            TRUE,
            IMAGE_DIRECTORY_ENTRY_IMPORT,
            &ulSize
        );

    while (pImportDesc->Name)
    {
        PSTR pszModName = (PSTR)((PBYTE) hKernel + pImportDesc->Name);
        if (stricmp(pszModName, pszHookModName) == 0)
        break;  
        pImportDesc++;
    }

    PIMAGE_THUNK_DATA pThunk =
    (PIMAGE_THUNK_DATA)((PBYTE) hKernel + pImportDesc->FirstThunk);

    while (pThunk->u1.Function)
    {
        PROC* ppfn = (PROC*) &pThunk->u1.Function;
        BOOL bFound = (*ppfn == pfnHookAPIAddr);

        if (bFound)
        {
            MEMORY_BASIC_INFORMATION mbi;
            VirtualQuery(
                ppfn,
                &mbi,
                sizeof(MEMORY_BASIC_INFORMATION)
            );
            VirtualProtect(
                mbi.BaseAddress,
                mbi.RegionSize,
                PAGE_READWRITE,
                &mbi.Protect)
            )

            *ppfn = *pfnNew;

            DWORD dwOldProtect;
            VirtualProtect(
                mbi.BaseAddress,
                mbi.RegionSize,
                mbi.Protect,
                &dwOldProtect
            );
            break;
        }
        pThunk++;
    }

调用Sleep(1000)的结果如例子所示:

    00407BD8: 68E8030000    push 0000003E8h
    00407BDD: E812FAFFFF    call Sleep

    Sleep:     ;这是跳转到IAT里的地址
    004075F4: FF25BCA14000    jmp dword ptr [00040A1BCh]

    原始表:
    0040A1BC: 79 67 E8 77 00 00 00 00
     
    新表:
    0040A1BC: 78 56 34 12 00 00 00 00

所以最后会跳转到0x12345678。
         

=====[ 3.2.2 改写入口点挂钩本进程 ]==================

    改写函数入口点开始的一些字节这种方法相当简单。就象改变IAT里的地址一样,我们也
要先修改页面属性。在这里对我们想要挂钩的函数是一开始的5个字节。为了之后的使用我们
用动态分配MEMORY_BASIC_INFORMATION结构。函数的起始地址也是用GetProcAddress来获得
。我们在这个地址里插入指向我们代码的跳转指令。接下来程序调用Sleep(5000)(所以它会
等待5秒钟),然后Sleep函数被挂钩并重定向到new_sleep,最后它再次调用Sleep(5000)。因
为新的函数new_sleep什么都不做并直接返回,所以整个程序只需要5秒钟而不是10秒种。


.386p
.model flat, stdcall

includelib lib/kernel32.lib
Sleep            PROTO :DWORD
GetModuleHandleA    PROTO :DWORD
GetProcAddress        PROTO :DWORD,:DWORD
VirtualQuery        PROTO :DWORD,:DWORD,:DWORD
VirtualProtect        PROTO :DWORD,:DWORD,:DWORD,:DWORD
VirtualAlloc        PROTO :DWORD,:DWORD,:DWORD,:DWORD
VirtualFree        PROTO :DWORD,:DWORD,:DWORD
FlushInstructionCache    PROTO :DWORD,:DWORD,:DWORD
GetCurrentProcess    PROTO
ExitProcess         PROTO :DWORD


.data

kernel_name         db "kernel32.dll",0
sleep_name        db "Sleep",0
old_protect        dd ?

MEMORY_BASIC_INFORMATION_SIZE    equ 28

PAGE_READWRITE        dd 000000004h
PAGE_EXECUTE_READWRITE dd 000000040h
MEM_COMMIT        dd 000001000h
MEM_RELEASE        dd 000008000h


.code
start:
    push    5000
    call    Sleep

do_hook:
    push    offset kernel_name
    call    GetModuleHandleA
    push    offset sleep_name
    push    eax
    call    GetProcAddress
    mov    edi,eax            ;最后获得Sleep地址

    push    PAGE_READWRITE
    push    MEM_COMMIT
    push    MEMORY_BASIC_INFORMATION_SIZE
    push     0
    call    VirtualAlloc
    test    eax,eax
    jz     do_sleep
    mov    esi,eax            ;为MBI结构分配内存

    push    MEMORY_BASIC_INFORMATION_SIZE
    push    esi
    push    edi
    call    VirtualQuery        ;内存页的信息
    test    eax,eax
    jz    free_mem

    call    GetCurrentProcess
    push    5
    push    edi
    push    eax
    call    FlushInstructionCache    ;只是为了确定一下:)

    lea    eax,[esi+014h]
    push    eax
    push    PAGE_EXECUTE_READWRITE
    lea    eax,[esi+00Ch]
    push    [eax]
    push    [esi]
    call    VirtualProtect          ;我们要修改保护属性,这样才能够写入代码
    test    eax,eax
    jz    free_mem       

    mov    byte ptr [edi],0E9h    ;写入跳转指令
    mov    eax,offset new_sleep
    sub    eax,edi
    sub    eax,5
    inc    edi
    stosd                ;这里是跳转地址

    push    offset old_protect
    lea    eax,[esi+014h]
    push    [eax]
    lea    eax,[esi+00Ch]
    push    [eax]
    push    [esi]
    call    VirtualProtect        ;恢复页保护属性

free_mem:
    push    MEM_RELEASE
    push    0
    push    esi
    call    VirtualFree        ;释放内存
do_sleep:
    push    5000
    call    Sleep
    push    0
    call    ExitProcess
new_sleep:               
    ret    004h
end start


第二次调用Sleep的结果是这样:

    004010A4: 6888130000    push 000001388h
    004010A9: E80A000000    call Sleep


    Sleep:     ;这里是跳转到IAT里的地址
    004010B8: FF2514204000    jmp dword ptr [000402014h]

    tabulka:
    00402014: 79 67 E8 77 6C 7D E8 77
     
    Kernel32.Sleep:
    77E86779: E937A95788    jmp 0004010B5h

    new_sleep:
    004010B5: C20400    ret 004h   


=====[ 3.2.3 保存原始函数 ]=====================================

    更多时候我们需要的不仅仅是挂钩函数。比方说也许我们并不想取代给定的函数而只是
想检查一下它的结果,或者也许我们只是想在函数被使用特定的参数来调用时才取代原函数
。比较好的例子有前面提过的通过取代FindXXXFile函数来完成隐藏文件。所以如果我们想要
隐藏指定的文件并且不想被注意的话,就得对其它所有文件只调用没有被修改过的原始函数
。这对使用修改IAT的方法时是很简单的,为调用原始函数我们可以用GetProcAddress获得它
的原始地址,然后直接调用。但修改入口点的方法就会有问题,因为修改了函数入口点的5个
字节,使我们破坏了原函数。所以我们必须保存开始的那些指令。这将用到以下的技术。
    我们知道我们要修改开始的5个字节但不知道里面包含多少条指令以及指令的长度。我们
得为开始那些指令保留足够的内存空间。16个字节应该足够了,因为函数开始时通常没有多
长的指令,很可能根本就用不到16个字节。整个被保留的内存用0x90(0x90=nop)来填满。下
一个5个字节预留给将在之后填入的跳转指令。

old_hook:        db 090h,090h,090h,090h,090h,090h,090h,090h
            db 090h,090h,090h,090h,090h,090h,090h,090h
            db 0E9h,000h,000h,000h,000h


    现在我们已准备好拷贝开始的指令。为获得指令长度的代码相当麻烦,这就是我们得使
用已完成的引擎的原因。它是由Z0MBiE写的。传入参数是我们要获得长度的指令的地址。输
出参数在eax里。


; LDE32, Length-Disassembler Engine, 32-bit, (x) 1999-2000 Z0MBiE
; special edition for REVERT tool

; version 1.05

C_MEM1                  equ     0001h       ; |
C_MEM2                  equ     0002h       ; |may be used simultaneously
C_MEM4                  equ     0004h       ; |
C_DATA1                 equ     0100h       ; |
C_DATA2                 equ     0200h       ; |may be used simultaneously
C_DATA4                 equ     0400h       ; |
C_67                    equ     0010h       ; used with C_PREFIX
C_MEM67                 equ     0020h       ; C_67 ? C_MEM2 : C_MEM4
C_66                    equ     1000h       ; used with C_PREFIX
C_DATA66                equ     2000h       ; C_66 ? C_DATA2 : C_DATA4
C_PREFIX                equ     0008h       ; prefix. take opcode again
C_MODRM                 equ     4000h       ; MODxxxR/M
C_DATAW0                equ     8000h       ; opc&1 ? C_DATA66 : C_DATA1

                        p386
                        model   flat
                        locals  @@

                        .code

public                  disasm_main
public                  _disasm_main
public                  @disasm_main
public                  DISASM_MAIN

disasm_main:
_disasm_main:
@disasm_main:
DISASM_MAIN:


; __fastcall            EAX
; __cdecl               [ESP+4]

;这是我的第一处修改,它只是这个函数的声明
get_instr_len:

                        mov     ecx, [esp+4]    ; ECX = opcode ptr

                        xor     edx, edx        ; 标志
                        xor     eax, eax

@@prefix:               and     dl, not C_PREFIX

                        mov     al, [ecx]
                        inc     ecx

                        or      edx, table_1[eax*4]

                        test    dl, C_PREFIX
                        jnz     @@prefix

                        cmp     al, 0F6h
                        je      @@test
                        cmp     al, 0F7h
                        je      @@test

                        cmp     al, 0CDh
                        je      @@int

                        cmp     al, 0Fh
                        je      @@0F
@@cont:
                        test    dh, C_DATAW0 shr 8
                        jnz     @@dataw0
@@dataw0done:
                        test    dh, C_MODRM shr 8
                        jnz     @@modrm
@@exitmodrm:
                        test    dl, C_MEM67
                        jnz     @@mem67
@@mem67done:
                        test    dh, C_DATA66 shr 8
                        jnz     @@data66
@@data66done:
                        mov     eax, ecx
                        sub     eax, [esp+4]

                        and     edx,C_MEM1+C_MEM2+C_MEM4+C_DATA1+C_DATA2+C_DATA4
                        add     al, dl
                        add     al, dh

;这里是我的第二处修改,只有在原始版本这里是retn
@@exit:                 ret     00004h  

@@test:                 or      dh, C_MODRM shr 8
                        test    byte ptr [ecx], 00111000b  ; F6/F7 -- test
                        jnz     @@cont
                        or      dh, C_DATAW0 shr 8
                        jmp     @@cont

@@int:                  or      dh, C_DATA1 shr 8
                        cmp     byte ptr [ecx], 20h
                        jne     @@cont
                        or      dh, C_DATA4 shr 8
                        jmp     @@cont

@@0F:                   mov     al, [ecx]
                        inc     ecx
                        or      edx, table_0F[eax*4]

                        cmp     edx, -1
                        jne     @@cont

@@error:                mov     eax, edx
                        jmp     @@exit

@@dataw0:               xor     dh, C_DATA66 shr 8
                        test    al, 00000001b
                        jnz     @@dataw0done
                        xor     dh, (C_DATA66+C_DATA1) shr 8
                        jmp     @@dataw0done

@@mem67:                xor     dl, C_MEM2
                        test    dl, C_67
                        jnz     @@mem67done
                        xor     dl, C_MEM4+C_MEM2
                        jmp     @@mem67done

@@data66:               xor     dh, C_DATA2 shr 8
                        test    dh, C_66 shr 8
                        jnz     @@data66done
                        xor     dh, (C_DATA4+C_DATA2) shr 8
                        jmp     @@data66done

@@modrm:                mov     al, [ecx]
                        inc     ecx

                        mov     ah, al  ; ah=mod, al=rm

                        and     ax, 0C007h
                        cmp     ah, 0C0h
                        je      @@exitmodrm

                        test    dl, C_67
                        jnz     @@modrm16

@@modrm32:              cmp     al, 04h
                        jne     @@a

                        mov     al, [ecx]       ; sib
                        inc     ecx
                        and     al, 07h

@@a:                    cmp     ah, 40h
                        je      @@mem1
                        cmp     ah, 80h
                        je      @@mem4

                        cmp     ax, 0005h
                        jne     @@exitmodrm

@@mem4:                 or      dl, C_MEM4
                        jmp     @@exitmodrm

@@mem1:                 or      dl, C_MEM1
                        jmp     @@exitmodrm

@@modrm16:              cmp     ax, 0006h
                        je      @@mem2
                        cmp     ah, 40h
                        je      @@mem1
                        cmp     ah, 80h
                        jne     @@exitmodrm

@@mem2:                 or      dl, C_MEM2
                        jmp     @@exitmodrm

                        endp

                        .data

;0F      -- 在代码中分析,不需要标志(也就是标志(flag)必须为0)
;F6,F7   -- --//-- (ttt=000 -- 3 字节, 否则为2字节)
;CD      -- --//-- (如果为 CD 20 为6字节, 否则为2字节)

table_1                 label   dword   ; 一般的指令

dd C_MODRM              ; 00
dd C_MODRM              ; 01
dd C_MODRM              ; 02
dd C_MODRM              ; 03
dd C_DATAW0             ; 04
dd C_DATAW0             ; 05
dd 0                    ; 06
dd 0                    ; 07
dd C_MODRM              ; 08
dd C_MODRM              ; 09
dd C_MODRM              ; 0A
dd C_MODRM              ; 0B
dd C_DATAW0             ; 0C
dd C_DATAW0             ; 0D
dd 0                    ; 0E
dd 0                    ; 0F
dd C_MODRM              ; 10
dd C_MODRM              ; 11
dd C_MODRM              ; 12
dd C_MODRM              ; 13
dd C_DATAW0             ; 14
dd C_DATAW0             ; 15
dd 0                    ; 16
dd 0                    ; 17
dd C_MODRM              ; 18
dd C_MODRM              ; 19
dd C_MODRM              ; 1A
dd C_MODRM              ; 1B
dd C_DATAW0             ; 1C
dd C_DATAW0             ; 1D
dd 0                    ; 1E
dd 0                    ; 1F
dd C_MODRM              ; 20
dd C_MODRM              ; 21
dd C_MODRM              ; 22
dd C_MODRM              ; 23
dd C_DATAW0             ; 24
dd C_DATAW0             ; 25
dd C_PREFIX             ; 26
dd 0                    ; 27
dd C_MODRM              ; 28
dd C_MODRM              ; 29
dd C_MODRM              ; 2A
dd C_MODRM              ; 2B
dd C_DATAW0             ; 2C
dd C_DATAW0             ; 2D
dd C_PREFIX             ; 2E
dd 0                    ; 2F
dd C_MODRM              ; 30
dd C_MODRM              ; 31
dd C_MODRM              ; 32
dd C_MODRM              ; 33
dd C_DATAW0             ; 34
dd C_DATAW0             ; 35
dd C_PREFIX             ; 36
dd 0                    ; 37
dd C_MODRM              ; 38
dd C_MODRM              ; 39
dd C_MODRM              ; 3A
dd C_MODRM              ; 3B
dd C_DATAW0             ; 3C
dd C_DATAW0             ; 3D
dd C_PREFIX             ; 3E
dd 0                    ; 3F
dd 0                    ; 40
dd 0                    ; 41
dd 0                    ; 42
dd 0                    ; 43
dd 0                    ; 44
dd 0                    ; 45
dd 0                    ; 46
dd 0                    ; 47
dd 0                    ; 48
dd 0                    ; 49
dd 0                    ; 4A
dd 0                    ; 4B
dd 0                    ; 4C
dd 0                    ; 4D
dd 0                    ; 4E
dd 0                    ; 4F
dd 0                    ; 50
dd 0                    ; 51
dd 0                    ; 52
dd 0                    ; 53
dd 0                    ; 54
dd 0                    ; 55
dd 0                    ; 56
dd 0                    ; 57
dd 0                    ; 58
dd 0                    ; 59
dd 0                    ; 5A
dd 0                    ; 5B
dd 0                    ; 5C
dd 0                    ; 5D
dd 0                    ; 5E
dd 0                    ; 5F
dd 0                    ; 60
dd 0                    ; 61
dd C_MODRM              ; 62
dd C_MODRM              ; 63
dd C_PREFIX             ; 64
dd C_PREFIX             ; 65
dd C_PREFIX+C_66        ; 66
dd C_PREFIX+C_67        ; 67
dd C_DATA66             ; 68
dd C_MODRM+C_DATA66     ; 69
dd C_DATA1              ; 6A
dd C_MODRM+C_DATA1      ; 6B
dd 0                    ; 6C
dd 0                    ; 6D
dd 0                    ; 6E
dd 0                    ; 6F
dd C_DATA1              ; 70
dd C_DATA1              ; 71
dd C_DATA1              ; 72
dd C_DATA1              ; 73
dd C_DATA1              ; 74
dd C_DATA1              ; 75
dd C_DATA1              ; 76
dd C_DATA1              ; 77
dd C_DATA1              ; 78
dd C_DATA1              ; 79
dd C_DATA1              ; 7A
dd C_DATA1              ; 7B
dd C_DATA1              ; 7C
dd C_DATA1              ; 7D
dd C_DATA1              ; 7E
dd C_DATA1              ; 7F
dd C_MODRM+C_DATA1      ; 80
dd C_MODRM+C_DATA66     ; 81
dd C_MODRM+C_DATA1      ; 82
dd C_MODRM+C_DATA1      ; 83
dd C_MODRM              ; 84
dd C_MODRM              ; 85
dd C_MODRM              ; 86
dd C_MODRM              ; 87
dd C_MODRM              ; 88
dd C_MODRM              ; 89
dd C_MODRM              ; 8A
dd C_MODRM              ; 8B
dd C_MODRM              ; 8C
dd C_MODRM              ; 8D
dd C_MODRM              ; 8E
dd C_MODRM              ; 8F
dd 0                    ; 90
dd 0                    ; 91
dd 0                    ; 92
dd 0                    ; 93
dd 0                    ; 94
dd 0                    ; 95
dd 0                    ; 96
dd 0                    ; 97
dd 0                    ; 98
dd 0                    ; 99
dd C_DATA66+C_MEM2      ; 9A
dd 0                    ; 9B
dd 0                    ; 9C
dd 0                    ; 9D
dd 0                    ; 9E
dd 0                    ; 9F
dd C_MEM67              ; A0
dd C_MEM67              ; A1
dd C_MEM67              ; A2
dd C_MEM67              ; A3
dd 0                    ; A4
dd 0                    ; A5
dd 0                    ; A6
dd 0                    ; A7
dd C_DATA1              ; A8
dd C_DATA66             ; A9
dd 0                    ; AA
dd 0                    ; AB
dd 0                    ; AC
dd 0                    ; AD
dd 0                    ; AE
dd 0                    ; AF
dd C_DATA1              ; B0
dd C_DATA1              ; B1
dd C_DATA1              ; B2
dd C_DATA1              ; B3
dd C_DATA1              ; B4
dd C_DATA1              ; B5
dd C_DATA1              ; B6
dd C_DATA1              ; B7
dd C_DATA66             ; B8
dd C_DATA66             ; B9
dd C_DATA66             ; BA
dd C_DATA66             ; BB
dd C_DATA66             ; BC
dd C_DATA66             ; BD
dd C_DATA66             ; BE
dd C_DATA66             ; BF
dd C_MODRM+C_DATA1      ; C0
dd C_MODRM+C_DATA1      ; C1
dd C_DATA2              ; C2
dd 0                    ; C3
dd C_MODRM              ; C4
dd C_MODRM              ; C5
dd C_MODRM+C_DATA1      ; C6
dd C_MODRM+C_DATA66     ; C7
dd C_DATA2+C_DATA1      ; C8
dd 0                    ; C9
dd C_DATA2              ; CA
dd 0                    ; CB
dd 0                    ; CC
dd 0                    ; CD
dd 0                    ; CE
dd 0                    ; CF
dd C_MODRM              ; D0
dd C_MODRM              ; D1
dd C_MODRM              ; D2
dd C_MODRM              ; D3
dd C_DATA1              ; D4
dd C_DATA1              ; D5
dd 0                    ; D6
dd 0                    ; D7
dd C_MODRM              ; D8
dd C_MODRM              ; D9
dd C_MODRM              ; DA
dd C_MODRM              ; DB
dd C_MODRM              ; DC
dd C_MODRM              ; DD
dd C_MODRM              ; DE
dd C_MODRM              ; DF
dd C_DATA1              ; E0
dd C_DATA1              ; E1
dd C_DATA1              ; E2
dd C_DATA1              ; E3
dd C_DATA1              ; E4
dd C_DATA1              ; E5
dd C_DATA1              ; E6
dd C_DATA1              ; E7
dd C_DATA66             ; E8
dd C_DATA66             ; E9
dd C_DATA66+C_MEM2      ; EA
dd C_DATA1              ; EB
dd 0                    ; EC
dd 0                    ; ED
dd 0                    ; EE
dd 0                    ; EF
dd C_PREFIX             ; F0
dd 0                    ; F1
dd C_PREFIX             ; F2
dd C_PREFIX             ; F3
dd 0                    ; F4
dd 0                    ; F5
dd 0                    ; F6
dd 0                    ; F7
dd 0                    ; F8
dd 0                    ; F9
dd 0                    ; FA
dd 0                    ; FB
dd 0                    ; FC
dd 0                    ; FD
dd C_MODRM              ; FE
dd C_MODRM              ; FF

table_0F                label   dword   ; 0F为前缀的指令

dd C_MODRM              ; 00
dd C_MODRM              ; 01
dd C_MODRM              ; 02
dd C_MODRM              ; 03
dd -1                   ; 04
dd -1                   ; 05
dd 0                    ; 06
dd -1                   ; 07
dd 0                    ; 08
dd 0                    ; 09
dd 0                    ; 0A
dd 0                    ; 0B
dd -1                   ; 0C
dd -1                   ; 0D
dd -1                   ; 0E
dd -1                   ; 0F
dd -1                   ; 10
dd -1                   ; 11
dd -1                   ; 12
dd -1                   ; 13
dd -1                   ; 14
dd -1                   ; 15
dd -1                   ; 16
dd -1                   ; 17
dd -1                   ; 18
dd -1                   ; 19
dd -1                   ; 1A
dd -1                   ; 1B
dd -1                   ; 1C
dd -1                   ; 1D
dd -1                   ; 1E
dd -1                   ; 1F
dd -1                   ; 20
dd -1                   ; 21
dd -1                   ; 22
dd -1                   ; 23
dd -1                   ; 24
dd -1                   ; 25
dd -1                   ; 26
dd -1                   ; 27
dd -1                   ; 28
dd -1                   ; 29
dd -1                   ; 2A
dd -1                   ; 2B
dd -1                   ; 2C
dd -1                   ; 2D
dd -1                   ; 2E
dd -1                   ; 2F
dd -1                   ; 30
dd -1                   ; 31
dd -1                   ; 32
dd -1                   ; 33
dd -1                   ; 34
dd -1                   ; 35
dd -1                   ; 36
dd -1                   ; 37
dd -1                   ; 38
dd -1                   ; 39
dd -1                   ; 3A
dd -1                   ; 3B
dd -1                   ; 3C
dd -1                   ; 3D
dd -1                   ; 3E
dd -1                   ; 3F
dd -1                   ; 40
dd -1                   ; 41
dd -1                   ; 42
dd -1                   ; 43
dd -1                   ; 44
dd -1                   ; 45
dd -1                   ; 46
dd -1                   ; 47
dd -1                   ; 48
dd -1                   ; 49
dd -1                   ; 4A
dd -1                   ; 4B
dd -1                   ; 4C
dd -1                   ; 4D
dd -1                   ; 4E
dd -1                   ; 4F
dd -1                   ; 50
dd -1                   ; 51
dd -1                   ; 52
dd -1                   ; 53
dd -1                   ; 54
dd -1                   ; 55
dd -1                   ; 56
dd -1                   ; 57
dd -1                   ; 58
dd -1                   ; 59
dd -1                   ; 5A
dd -1                   ; 5B
dd -1                   ; 5C
dd -1                   ; 5D
dd -1                   ; 5E
dd -1                   ; 5F
dd -1                   ; 60
dd -1                   ; 61
dd -1                   ; 62
dd -1                   ; 63
dd -1                   ; 64
dd -1                   ; 65
dd -1                   ; 66
dd -1                   ; 67
dd -1                   ; 68
dd -1                   ; 69
dd -1                   ; 6A
dd -1                   ; 6B
dd -1                   ; 6C
dd -1                   ; 6D
dd -1                   ; 6E
dd -1                   ; 6F
dd -1                   ; 70
dd -1                   ; 71
dd -1                   ; 72
dd -1                   ; 73
dd -1                   ; 74
dd -1                   ; 75
dd -1                   ; 76
dd -1                   ; 77
dd -1                   ; 78
dd -1                   ; 79
dd -1                   ; 7A
dd -1                   ; 7B
dd -1                   ; 7C
dd -1                   ; 7D
dd -1                   ; 7E
dd -1                   ; 7F
dd C_DATA66             ; 80
dd C_DATA66             ; 81
dd C_DATA66             ; 82
dd C_DATA66             ; 83
dd C_DATA66             ; 84
dd C_DATA66             ; 85
dd C_DATA66             ; 86
dd C_DATA66             ; 87
dd C_DATA66             ; 88
dd C_DATA66             ; 89
dd C_DATA66             ; 8A
dd C_DATA66             ; 8B
dd C_DATA66             ; 8C
dd C_DATA66             ; 8D
dd C_DATA66             ; 8E
dd C_DATA66             ; 8F
dd C_MODRM              ; 90
dd C_MODRM              ; 91
dd C_MODRM              ; 92
dd C_MODRM              ; 93
dd C_MODRM              ; 94
dd C_MODRM              ; 95
dd C_MODRM              ; 96
dd C_MODRM              ; 97
dd C_MODRM              ; 98
dd C_MODRM              ; 99
dd C_MODRM              ; 9A
dd C_MODRM              ; 9B
dd C_MODRM              ; 9C
dd C_MODRM              ; 9D
dd C_MODRM              ; 9E
dd C_MODRM              ; 9F
dd 0                    ; A0
dd 0                    ; A1
dd 0                    ; A2
dd C_MODRM              ; A3
dd C_MODRM+C_DATA1      ; A4
dd C_MODRM              ; A5
dd -1                   ; A6
dd -1                   ; A7
dd 0                    ; A8
dd 0                    ; A9
dd 0                    ; AA
dd C_MODRM              ; AB
dd C_MODRM+C_DATA1      ; AC
dd C_MODRM              ; AD
dd -1                   ; AE
dd C_MODRM              ; AF
dd C_MODRM              ; B0
dd C_MODRM              ; B1
dd C_MODRM              ; B2
dd C_MODRM              ; B3
dd C_MODRM              ; B4
dd C_MODRM              ; B5
dd C_MODRM              ; B6
dd C_MODRM              ; B7
dd -1                   ; B8
dd -1                   ; B9
dd C_MODRM+C_DATA1      ; BA
dd C_MODRM              ; BB
dd C_MODRM              ; BC
dd C_MODRM              ; BD
dd C_MODRM              ; BE
dd C_MODRM              ; BF
dd C_MODRM              ; C0
dd C_MODRM              ; C1
dd -1                   ; C2
dd -1                   ; C3
dd -1                   ; C4
dd -1                   ; C5
dd -1                   ; C6
dd -1                   ; C7
dd 0                    ; C8
dd 0                    ; C9
dd 0                    ; CA
dd 0                    ; CB
dd 0                    ; CC
dd 0                    ; CD
dd 0                    ; CE
dd 0                    ; CF
dd -1                   ; D0
dd -1                   ; D1
dd -1                   ; D2
dd -1                   ; D3
dd -1                   ; D4
dd -1                   ; D5
dd -1                   ; D6
dd -1                   ; D7
dd -1                   ; D8
dd -1                   ; D9
dd -1                   ; DA
dd -1                   ; DB
dd -1                   ; DC
dd -1                   ; DD
dd -1                   ; DE
dd -1                   ; DF
dd -1                   ; E0
dd -1                   ; E1
dd -1                   ; E2
dd -1                   ; E3
dd -1                   ; E4
dd -1                   ; E5
dd -1                   ; E6
dd -1                   ; E7
dd -1                   ; E8
dd -1                   ; E9
dd -1                   ; EA
dd -1                   ; EB
dd -1                   ; EC
dd -1                   ; ED
dd -1                   ; EE
dd -1                   ; EF
dd -1                   ; F0
dd -1                   ; F1
dd -1                   ; F2
dd -1                   ; F3
dd -1                   ; F4
dd -1                   ; F5
dd -1                   ; F6
dd -1                   ; F7
dd -1                   ; F8
dd -1                   ; F9
dd -1                   ; FA
dd -1                   ; FB
dd -1                   ; FC
dd -1                   ; FD
dd -1                   ; FE
dd -1                   ; FF

                        end


    现在我们可以获取任意地址的指令长度。我们重复调用这个函数直到读取了5个字节。完
成后把这些字节拷贝到old_hook。我们知道了开始这些指令的长度,所以我们可以在原始函
数的下条指令填入跳转地址。

.386p
.model flat, stdcall

...

.data

kernel_name         db "kernel32.dll",0
sleep_name        db "Sleep",0

...

MEM_RELEASE        dd 000008000h

;16 nops + 一个跳转指令
old_sleep        db 090h,090h,090h,090h,090h,090h,090h,090h,
               090h,090h,090h,090h,090h,090h,090h,090h,
               0E9h,000h,000h,000h,000h