ntldr代码分析

首先声明这次的分析是我一点一点跟着NTLDR分析做的,这篇文章讲述的很跳跃,说实话一开始我也没怎么看懂,之后又结合了老师给的文档一点点去对照着看懂的。正如老师要求的那样,是要用我自己的思想把这个ntldr解释出来。

这次的任务是用bochs动态调试ntldr代码部分,并结合windows nt4的源码做分析。
ntldr分为两部分 一个是startup,一个是osloader;我们依次分析。
startup.com

(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b          ; ea5be000f0
<bochs:1> b 0x2000:0000         //在ntldr的开始处地址下断点
<bochs:2> c
(0) Breakpoint 1, 0x0000000000020000 in ?? ()
Next at t=6119497
(0) [0x000000020000] 2000:0000 (unk. ctxt): jmp .+469 (0x000201d8)    ; e9d501   
<bochs:3> s
Next at t=6119498
(0) [0x0000000201d8] 2000:01d8 (unk. ctxt): mov bx, 0x2f40            ; bb402f
<bochs:4>
Next at t=6119499
(0) [0x0000000201db] 2000:01db (unk. ctxt): shr bx, 0x04              ; c1eb04
<bochs:5>
Next at t=6119500
(0) [0x0000000201de] 2000:01de (unk. ctxt): mov ax, cs                ; 8cc8
<bochs:6>
Next at t=6119501
(0) [0x0000000201e0] 2000:01e0 (unk. ctxt): add ax, bx                ; 03c3
<bochs:7>
Next at t=6119502
(0) [0x0000000201e2] 2000:01e2 (unk. ctxt): mov ss, ax                ; 8ed0
<bochs:8>
Next at t=6119503
(0) [0x0000000201e4] 2000:01e4 (unk. ctxt): mov sp, 0x1528            ; bc2815
<bochs:9>
Next at t=6119504
(0) [0x0000000201e7] 2000:01e7 (unk. ctxt): push dx                   ; 52
<bochs:10>
Next at t=6119505
(0) [0x0000000201e8] 2000:01e8 (unk. ctxt): mov ds, ax                ; 8ed8
<bochs:11>
Next at t=6119506
(0) [0x0000000201ea] 2000:01ea (unk. ctxt): mov es, ax                ; 8ec0
<bochs:12>
Next at t=6119507
(0) [0x0000000201ec] 2000:01ec (unk. ctxt): movzx edx, ax             ; 660fb7d0
<bochs:13>
Next at t=6119508
(0) [0x0000000201f0] 2000:01f0 (unk. ctxt): shl edx, 0x04             ; 66c1e204
<bochs:14>
Next at t=6119509
(0) [0x0000000201f4] 2000:01f4 (unk. ctxt): add edx, 0x00001d80       ; 6681c2801d0000
<bochs:15>
Next at t=6119510
(0) [0x0000000201fb] 2000:01fb (unk. ctxt): mov dword ptr ds:0x0cbe, edx ; 668916be0c
<bochs:16>
Next at t=6119511
(0) [0x000000020200] 2000:0200 (unk. ctxt): xor bp, bp                ; 33ed
<bochs:17>
Next at t=6119512
(0) [0x000000020202] 2000:0202 (unk. ctxt): movzx ebp, bp             ; 660fb7ed
<bochs:18>
Next at t=6119513
(0) [0x000000020206] 2000:0206 (unk. ctxt): movzx esp, sp             ; 660fb7e4
<bochs:19>
Next at t=6119514
(0) [0x00000002020a] 2000:020a (unk. ctxt): mov word ptr ds:0x15bc, ds ; 8c1ebc15
<bochs:20>
Next at t=6119515
(0) [0x00000002020e] 2000:020e (unk. ctxt): call .+5903 (0x00021920)  ; e80f17  //这里开始开始调用第一个函数SuMain                                                      

这里的call就是调用ntldr中的函数了,要对应nt4原码去看

(0) [0x000000021929] 2000:1929 (unk. ctxt): mov byte ptr ds:0x15c4, al ; a2c415
<bochs:3> s
Next at t=6119521
(0) [0x00000002192c] 2000:192c (unk. ctxt): call .-5534 (0x00020391)  ; e862ea

调用InitializeVideoSubSystem()函数去初始化VideoSubSystem
之后再调用TurnMotorOff()函数来关闭软盘马达 为软盘引导做准备
PatchDiskBaseTable()
调用 BtIsEisaSystem()、BtIsMacSystem()分别判断机器的总线类型是Eisa还是Mac,如果这两种都不是,则默认该机器的总线类型为ISA
BtIsEisaSystem()函数中是通过比较f000:ffd9处是否设置了”ASIE”标志,该标志是在系统加电启动时bios设置的。 在BtIsMcaSystem()函数中是通过调用int 15 0c00并测试es[bx+5]是否为2来判断是否为Mca总线类型。
ConstructMemeoryDescriptors()函数来获取内存容量
在接下来的Sumain函数中,是一个while循环来搜索内存描述符的低内存。在循环体之后Sumain函数调用EnableA20()函数打开A20地址线,为切换到保护模式做准备。调用Relocatex86Structure()函数,重新定位x86的结构,包括GDT、IDT、页目录和第一级页表。 调用EnableProtectPaging()函数,切换到保护模式并允许分页。 调用RelocateLoaderSections()函数重新定位osLoader的位置。

osloader:

(0) [0x0000004013b1] 0008:00000000004013b1 (unk. ctxt): mov edi, edi              ; 8bff
<bochs:3> u /30
004013b1: (                    ): mov edi, edi              ; 8bff
004013b3: (                    ): push ebp                  ; 55
004013b4: (                    ): mov ebp, esp              ; 8bec
004013b6: (                    ): mov eax, 0x00001114       ; b814110000
004013bb: (                    ): call .+123108             ; e8e4e00100

首先调用DoGlobalInitialization()函数,这个我的理解是作为一个主函数来调用各个用来初始化内存子系统的其他函数。

(0) [0x000000401276] 0008:0000000000401276 (unk. ctxt): call .+13669 (0x004047e0) ; e865350000

这里调用的是InitializeMemorySubsystem(0x00401276)来初始化内存子系统。
在初始化部分的最后还需要调用两个函数: MempTurnOnPaging() 设置页表 允许分页
MempCopyGdt() 拷贝GDT、IDT。
接下来回到DoGlobalInitialization(),将BOOT_CONTEXT结构中的信息拷贝出来,最后,该函数还需要调用InitializeMemoryDescriptor()函数读取固件地址空间映射并保留该范围,并将固件空间正式宣布为“地址空间保留”。执行完毕后整个内存子系统也就初始化完毕了。
接下来是我觉得比较关键的部分,利用NtProcessStartup()来判断我们的系统是通过什么方式来运行的。贴出其代码

if (BootContextRecord->FSContextPointer->BootDrive == 0)                     //从A:盘启动 
     else if (BlIsElToritoCDBoot(BootContextRecord->FSContextPointer->BootDrive)) 
                                                                          //从CD启动 
     else { BlGetActivePartition(BootPartitionName);}       //从硬盘启动 

BlGetActivePartition()通过一个循环结构遍历各个活动分区,获取活动分区并读取其中的引导记录,判断是否为引导扇区,直到出现一个引导扇区。
接下来调用BlMemoryInitialize()函数,初始化内存描述符链表、OS loader堆和OS loader参数块.
接下来调用 BlIoInitialize()初始化 OS loader I/O,其中调用的各个文件系统初始化也只是简单的返回 ESUCCESS.
最后由NtProcessStartup()调用 BlStartup()函数,再一次完成控制权的转交。

ntldr代码的最后一部分是BlStartup(),文档中说明这一部分中绝大多数就是在析取boot.ini文件的选项,加载系统映象。因此也没有做解释。我看了一下贴出的代码,是判断活动分区,对电脑各个硬件的初始化,并加载系统内核的过程。也就不多赘述了。

至此,ntldr代码的分析也就全部结束了,文末我还是要多谢上面的文档的作者,也让我体会到作为一个“程序员”,很重要的一点就是经验的交流与资源的共享。很难想象在一开始我没有这篇文档的帮助,我整整一个礼拜毫无头绪,一度想和老师商量跳过这个部分做一些实战。最后上莺哥!在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值