分析Windows启动过程(二)

分析Windows启动过程(一)
没看过上一篇别过来.
接着上一篇,
确定了引导用的(活动分区)分区之后到文件偏移0x34的位置
此时bp为DPT记录的偏移
代码如下:

00000034  885600            mov [bp+0x0],dl
00000037  55                push bp
00000038  C6461105          mov byte [bp+0x11],0x5
0000003C  C6461000          mov byte [bp+0x10],0x0

由于上面没有初始化dl或dx有关的代码, 笔者认为这是置bp处的一个字节为0(也就是设置当前分区在内存中的记录为非活动分区)
然后push bp将bp压入栈, 接着把内存中下一个分区记录的开始磁头设置为0x05, 把分区类型设置为0(非活动分区)
然后看代码:

00000040  B441              mov ah,0x41
00000042  BBAA55            mov bx,0x55aa
00000045  CD13              int 0x13

这里是一个int 0x13的中断调用, 根据int-13上的信息, 我们可以找到这个页面:Int 13/AH=41h/BX=55AAh
这个页面的要求和上面的代码一模一样, AH为0x41, BX为0x55AA, 看看他是干嘛的吧.
它的名称是IBM / MS INT 13扩展-安装检查
用于检测一个设备是否安装了这个扩展(可是我还是不明白指定设备用的dl在哪指定)
然后两行代码:

00000047  5D                pop bp
00000048  720F              jc 0x59

这里从堆栈恢复bp(见00000037), 根据上面那个int 0x13操作的描述, 如果不支持扩展则设置CF, 所以如果不支持扩展则跳到0x59
如果支持扩展, 则会经过下面这段代码:

0000004A  81FB55AA          cmp bx,0xaa55
0000004E  7509              jnz 0x59
00000050  F7C10100          test cx,0x1
00000054  7403              jz 0x59
00000056  FE4610            inc byte [bp+0x10]

根据描述, 如果支持扩展并且已安装, 则bx被置为0xaa55, 所以这里前两句判断是否已安装(提示: JNZ == JNE), 没有安装也跳转到0x59
然后比较cx和0x1, 根据说明, cx保存了支持的扩展API, 0x1的二进制是00000001, 这两行里, 如果cx == 0x1则跳转0x59, 所以仅cx != 0b00000001时才会继续, 那么00000001对应什么呢?对应着removable drive controller functions supported, 也就是说, 如果只支持这种功能, 那也没用
如果有用(支持并安装了扩展API)则执行inc byte [bp+0x10], 这句代码把[bp+0x10]作为一个字节 +1, 也就是让引导分区的分区起始相对扇区+0xff
接着代码:

00000059  6660              pushad
0000005B  807E1000          cmp byte [bp+0x10],0x0
0000005F  7426              jz 0x87
00000061  666800000000      push dword 0x0
00000067  66FF7608          push dword [bp+0x8]
0000006B  680000            push word 0x0
0000006E  68007C            push word 0x7c00
00000071  680100            push word 0x1
00000074  681000            push word 0x10
00000077  B442              mov ah,0x42
00000079  8A5600            mov dl,[bp+0x0]
0000007C  8BF4              mov si,sp
0000007E  CD13              int 0x13
00000080  9F                lahf
00000081  83C410            add sp,byte +0x10
00000084  9E                sahf
00000085  EB14              jmp short 0x9b
00000087  B80102            mov ax,0x201
0000008A  BB007C            mov bx,0x7c00
0000008D  8A5600            mov dl,[bp+0x0]
00000090  8A7601            mov dh,[bp+0x1]
00000093  8A4E02            mov cl,[bp+0x2]
00000096  8A6E03            mov ch,[bp+0x3]
00000099  CD13              int 0x13
0000009B  6661              popad

可以将此段代码看作一个过程, pushad保存当前环境, 最后popad恢复环境, 然后看中间的代码.

0000005B  807E1000          cmp byte [bp+0x10],0x0
0000005F  7426              jz 0x87

这两段代码判断是否支持扩展API, 不支持则跳转到0x87
如果不跳转, 则执行这段代码:

00000061  666800000000      push dword 0x0
00000067  66FF7608          push dword [bp+0x8]
0000006B  680000            push word 0x0
0000006E  68007C            push word 0x7c00
00000071  680100            push word 0x1
00000074  681000            push word 0x10
00000077  B442              mov ah,0x42
00000079  8A5600            mov dl,[bp+0x0]
0000007C  8BF4              mov si,sp
0000007E  CD13              int 0x13
00000080  9F                lahf
00000081  83C410            add sp,byte +0x10
00000084  9E                sahf
00000085  EB14              jmp short 0x9b

这段代码将0x00推入栈, 将引导分区的结束柱面推入栈, 推入0x00, 0x7c00, 0x1, 0x10, 然后调用了一个int 0x13的中断, 这个中断的信息在Int 13/AH=42h这里, 是"扩展读取", DL指向读取的设备, si指向一个设备地址包, 这里si指向sp, 也就是栈顶, 所以上面的push实际上是在构造一个disk address packet
从后面看起, 最后面push了0x10, 而且是个word, 很好地处理了一个字节的保留字节
倒数第二个push是0x1, 根据描述02h WORD number of blocks to transfer (max 007Fh for Phoenix EDD), 0x1是指传输块数, 然后是两个word, 这两个word并成一个dword指向缓冲区, 而缓冲区地址就是0x7c00, 现在明白了为什么一开始要复制引导扇区到0x600了吧! 代码顶上两个dword组成一个qword, 根据定义08h QWORD starting absolute block number,
这个qword指向起始绝对块号, 而它的值是分区的分区起始相对扇区号, 也就是说, 读取分区的第一个扇区到0x7c00!!!

00000080  9F                lahf
00000081  83C410            add sp,byte +0x10
00000084  9E                sahf

这三行代码我就不知道是干嘛的了, 知道的说一下, 我觉得像是给PF标志位置位.
最后是jmp short 0x9b也就是跳转到这个过程的末尾


回到上面, 如果不支持扩展API, 那么会怎么样呢?
如果不支持扩展API则会执行:

00000087  B80102            mov ax,0x201
0000008A  BB007C            mov bx,0x7c00
0000008D  8A5600            mov dl,[bp+0x0]
00000090  8A7601            mov dh,[bp+0x1]
00000093  8A4E02            mov cl,[bp+0x2]
00000096  8A6E03            mov ch,[bp+0x3]
00000099  CD13              int 0x13

看到int第一时间就应该查文档, 然而笔者看到那句mov ax, 0x201头瞬间炸了, 这什么操作?
想了想, 这应该等于mov ah, 0x2mov al, 0x01, 看来微软为了节约空间真是不惜所措啊
于是找到了Int 13/AH=02h这个页面
这个调用用于读取扇区到内存
AL是要读取的扇区数, 1个
CH起始柱面号, CL起始扇区号, DH磁头号, DL设备号
Ok, 所以这些数据都是从分区记录里读取的, 而BX指向数据缓冲区, 0x7c00

接着看后面的代码:

0000009D  731C              jnc 0xbb

如果没出错, 那么跳转到0xbb, 因为如果int 0x13出错, CF标志位会被置位
先看错误处理程序:

0000009F  FE4E11            dec byte [bp+0x11]
000000A2  750C              jnz 0xb0
000000A4  807E0080          cmp byte [bp+0x0],0x80
000000A8  0F848A00          jz near 0x136
000000AC  B280              mov dl,0x80
000000AE  EB84              jmp short 0x34
000000B0  55                push bp
000000B1  32E4              xor ah,ah
000000B3  8A5600            mov dl,[bp+0x0]
000000B6  CD13              int 0x13
000000B8  5D                pop bp
000000B9  EB9E              jmp short 0x59

dec byte [bp+0x11]将分区起始相对扇区号 -1
jnz 0xb0如果分区起始相对扇区号 == 0则跳转到0xb0
如果不跳转则执行:

000000A4  807E0080          cmp byte [bp+0x0],0x80
000000A8  0F848A00          jz near 0x136
000000AC  B280              mov dl,0x80
000000AE  EB84              jmp short 0x34

检查设备编号是否0x80, 是则直接跳转到0x136
然后将dl设置为0x80, 之后jmp回到之前刚刚找到活动分区的地方, 我认为启动过程中, 第一次都会先执行到这里, 然后下一次才有可能成功
那如果跳到0x136呢?

00000136  A0B607            mov al,[0x7b6]
00000139  EB03              jmp short 0x13e

打印0x7b6的文字(但我没看出有什么文字在那里)
如果如果分区起始相对扇区号 == 0, 则执行:

000000B0  55                push bp
000000B1  32E4              xor ah,ah
000000B3  8A5600            mov dl,[bp+0x0]
000000B6  CD13              int 0x13
000000B8  5D                pop bp
000000B9  EB9E              jmp short 0x59;

push bppop bp用于保护bp的值
xor ah,ah同等于mov ah, 0, 将ah设置为0
将dl设置为设备编号, 然后int 0x13
对于ah=0的int 0x13调用我已经很熟悉了, 就是重置设备dl
最后一句jmp short 0x59就重新回去执行上面那个尝试读取过程了
意思就是重置一次设备并重试读取
那么如果读取成功了呢?

插一句话: 看到Notepad++的滚动条愈发向下, 笔者更有信心了

000000BB  813EFE7D55AA      cmp word [0x7dfe],0xaa55
000000C1  756E              jnz 0x131
000000C3  FF7600            push word [bp+0x0]
000000C6  E88D00            call 0x156
000000C9  7517              jnz 0xe2

这段代码检查上面读入的引导扇区是否以0xaa55结尾, 也就是是否有效扇区
不是则跳转到0x131
0x131会干嘛呢?

00000131  A0B707            mov al,[0x7b7]
00000134  EB08              jmp short 0x13e

输出0x7b7的文字, 但我一脸懵逼, 那里根本没有文字, 然后调用0x156, 后面讲

000000CB  FA                cli
000000CC  B0D1              mov al,0xd1
000000CE  E664              out 0x64,al
000000D0  E88300            call 0x156
000000D3  B0DF              mov al,0xdf
000000D5  E660              out 0x60,al
000000D7  E87C00            call 0x156
000000DA  B0FF              mov al,0xff
000000DC  E664              out 0x64,al
000000DE  E87500            call 0x156
000000E1  FB                sti

在执行这段代码时, 首尾clisti说明中途需要关闭中断, 否则会有问题
我一开始以为是进入保护模式, 发现原来不是, 只是打开A20地址线, 但是我见过的书都没有这么长的代码, 到Wiki搜索之后才发现是A20地址线, Wiki上说, 有些BIOS默认打开A20, 但Windows并没有测试A20是否已打开, 直接尝试打开
Wiki给出这样的代码用于打开A20:

enable_A20:
        cli
        call    a20wait
        mov     al,0xAD
        out     0x64,al
        call    a20wait
        mov     al,0xD0
        out     0x64,al
        call    a20wait2
        in      al,0x60
        push    eax
        call    a20wait
        mov     al,0xD1
        out     0x64,al
        call    a20wait
        pop     eax
        or      al,2
        out     0x60,al
        call    a20wait
        mov     al,0xAE
        out     0x64,al
        call    a20wait
        sti
        ret
a20wait:
        in      al,0x64
        test    al,2
        jnz     a20wait
        ret
a20wait2:
        in      al,0x64
        test    al,1
        jz      a20wait2
        ret

哎这其实很像Windows的代码啊!
Windows的代码有很多call 0x156啊?
看看这里的代码:

00000156  2BC9              sub cx,cx
00000158  E464              in al,0x64
0000015A  EB00              jmp short 0x15c
0000015C  2402              and al,0x2
0000015E  E0F8              loopne 0x158
00000160  2402              and al,0x2
00000162  C3                ret

sub cx, cx将cx减去cx??可能是置0吧
in al, 0x64和Wiki一样, 后面的就不一样了, 可是...jmp short 0x15c有什么用?我也不知道, and al,0x2是位与, 然后loopne 0x158, 只要cx != 0就跳转
好乱啊, 总之这段代码打开A20就行了, 好乱!
对了, 后面最后那个and是前面用的, 在000000C9有用, 000000C9根据判断会可选的跳过打开A20, 所以应该是判断是否BIOS自动打开吧.

随后我二脸懵逼:

000000E2  B800BB            mov ax,0xbb00
000000E5  CD1A              int 0x1a

http://www.ctyme.com/intr/int-1a.htm没有任何记载, 哎!

000000E7  6623C0            and eax,eax
000000EA  753B              jnz 0x127
000000EC  6681FB54435041    cmp ebx,0x41504354
000000F3  7532              jnz 0x127
000000F5  81F90201          cmp cx,0x102
000000F9  722C              jc 0x127

也就是eax != 0则跳到0x127, ebx == 0x41504354 则跳0x127, 最后一个跳转我百思不得其解, 因为我不知道cmp设置CF的条件

000000FB  666807BB0000      push dword 0xbb07
00000101  666800020000      push dword 0x200
00000107  666808000000      push dword 0x8
0000010D  6653              push ebx
0000010F  6653              push ebx
00000111  6655              push ebp
00000113  666800000000      push dword 0x0
00000119  6668007C0000      push dword 0x7c00
0000011F  6661              popad

上面的一堆push模拟了一个pushad操作, 然后popad, 其中, ebx和ebp没有更改, esp变成ebx
pushad的顺序:(E)AX, (E)CX, (E)DX, (E)BX, (E)SP, (E)BP, (E)SI, (E)DI
所以相当于下面代码:

mov eax, 0xbb07
mov ecx, 0x200
mov edx, 0x8
mov esp, ebx
mov esi, 0x0
mov edi, 0x7c00

然后还是设置:

00000121  680000            push word 0x0
00000124  07                pop es

设置es为0x0
然后:

00000125  CD1A              int 0x1a

上面设置了eax为0xbb07, 隐含mov ax, 0x07
mov ax, 0x07等于mov al, 0x07
真不明白, TIME - SET ALARM (AT,XT286,PS)是Int 1A/AH=06h, 可是从未调用过
这个int 0x1a定义在http://www.ctyme.com/intr/rb-2283.htm
取消时间报警?

00000127  5A                pop dx
00000128  32F6              xor dh,dh
0000012A  EA007C0000        jmp 0x0:0x7c00
0000012F  CD18              int 0x18

pop dx又是什么操作, 然后dh置0, 所以只取dl?
好吧是取引导设备号, 在上面push的

000000C3  FF7600            push word [bp+0x0]

一句jmp执行加载的引导扇区

0000012A  EA007C0000        jmp 0x0:0x7c00

顺便补充下上一篇的问题, int 0x18是中断启动, 一旦int 0x18, 不会继续引导!
一般设备int 0x18会显示"No bootable device"等


总结

Windows的硬盘引导扇区其实是一个Loader, 寻找活动分区, 加载该分区第一个扇区并执行, 所以真正的代码应该在引导分区的第一个扇区, 好吧我承认这次找错了

可是!你都看完两章了, 还不赞助一下, 感觉被你白嫖了...

爱发电赞助通道(推荐)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容