分析Windows启动过程(一)

好的同学们, 我们现在来分析一下Windows的引导过程,
(其实是我在写OS发现ATAPI简直就是一个坑)
这里我不会用虚拟机, 只会分析代码

这次分析将在Windows7下完成, 首先说下工具:

  • 火绒(提取引导扇区)
  • NASM(重要的是NDISASM, 反汇编)
  • Notepad++(查看汇编源码)
  • WinHex(可选)

准备好了工具就可以开始了

计算机的启动过程

既然要分析引导过程, 那么让我们先来了解一下系统引导过程,
一般情况下, 计算机在启动时会先做硬件上的准备, 大多数必要硬件(显示器, CPU, 内存, 硬盘, 键盘等)在此时初始化, 然后运行BIOS, BIOS读取设置, 初始化IDT, 根据引导顺序依次查找可引导的设备, 比如CD, 软驱和硬盘, 通常判断是否可引导的方法是读入一个扇区(512字节), 检查最后两个直接是否为0x55aa, 绝大多数引导扇区都遵守这个规则, 然后将引导扇区内容复制到0000:7c00的位置并执行, 引导扇区的程序仅仅512字节, 还要减去0x55aa两个字节, 硬盘引导似乎还有一个64字节的DDR, 肯定不够做系统嘛, 所以引导扇区一般是加载另一个>512字节的程序到内存中并执行, 引导扇区的使命就完成了, 然后这个程序一般会做真正的初始化(32位初始化, A20地址线, 图形界面等)并加载别的程序, 这个程序可能是内核, 但大多情况不是, 因为16位可用内存很小, 仅仅0000:0000到ffff:ffff, 而BIOS也要使用许多内存

硬件层的初始化我们自然是管不着的, 所以就要从软件层入手, 软件层的入口点就是引导扇区了,于是开始分析引导扇区.

分析引导扇区 - 提取引导扇区

要分析一样东西, 可是连东西都没有怎么行呢?
要想取得引导扇区, Linux很简单, 可是Windows呢? 于是我想到了火绒
打开火绒的设置选择常规设置 -> 查杀设置就可以看到备份引导区了, 愣着干嘛, 点啊!

备份引导区

导出之后应该是一个512字节(1扇区)的bin文件, 用WinHex打开一下看看
WinHex

看最后两个字节, 0x55和0xAA对吧, 然后就可以分析了

分析引导扇区 - 反汇编

十六进制你看得懂? 准确的说, 你不会很烦吗?
汇编就使人不那么烦了呢.
NDISASM是个好东西, 它包含在了NASM里, 什么? 怎么安装? 百度啊!
打开你的CMD, 切换到导出的目录, 然后输入

ndisasm -b 16 w7bl.bin > w7bl.asm

其中, -b 16指16位, 因为基本上32位都不会在引导扇区初始化, w7bl.bin指你的引导扇区文件名, > w7bl.asm指将输出重定向到w7bl.asm这个文件


分析引导扇区 - 分析汇编代码

得到w7bl.asm这个文件之后用Notepad++打开它(或者你喜欢的编辑器)


贴段代码:

00000000  33C0              xor ax,ax
00000002  8ED0              mov ss,ax
00000004  BC007C            mov sp,0x7c00
00000007  8EC0              mov es,ax
00000009  8ED8              mov ds,ax
0000000B  BE007C            mov si,0x7c00
0000000E  BF0006            mov di,0x600
00000011  B90002            mov cx,0x200
00000014  FC                cld

这段代码出现在文件的最前面, 它用于初始化寄存器, 一句一句看

  1. xor ax, ax这种两个寄存器相同的xor在汇编里面很常见, 相当于 mov ax, 0x00, 也就是置寄存器ax为0. xor可以比mov少一个字节, 在引导扇区里, 有时每一个字节的空间都十分珍贵, 大多数编译器也会优化mov xx, 0xor xx, xx.这句代码置ax为0
  2. mov ss, ax也就是将ax的值复制到寄存器ss中, 在第一句代码里将ax置为了0, 所以ss也会为0, 这句代码将ax的值复制到ss(将ss置0)
  3. mov sp,0x7c00将sp寄存器(堆栈指针寄存器)设置为0x7c00, 0x7c00是引导扇区被加载到的地址, 这句代码设置堆栈的栈顶是0x7c00, 这句代码置sp为0x7c00

下面这几句和后面的程序有关

  1. mov es,ax和第二句一样, 就不详细讲了, 这是段寄存器, 因为引导程序被加载到0000:7c00, 所以当前代码的所有段都是基于0000的, 否则如果是1, 那么jmp 7c00将会同等于jmp 0001:7c00, 导致跳转错误, 这句代码置es为0
  2. mov ds,ax还是一样, 这句代码置ds为0
  3. mov si,0x7c00和第三句一样, 这句代码置si为0x7c00
  4. mov di,0x600将di置为0x600, 这句代码置di为0x600
  5. mov cx,0x200将cx寄存器(计数寄存器)置为0x200, 这句代码置cx为0x200
  6. cld还是和后面有关

接着一点点继续:

00000015  F3A4              rep movsb

这句代码的rep用于重复, 是一个前缀命令.这句命令会重复执行movsb直到cx为0, 每次执行自动将cx-1, 笔者在一份Intel Opcodes里看到这样一句话:

A4 MOVSB Move byte at address DS:(E)SI to address ES:(E)DI

意思是movsb会移动一个字节从DS:SI到ES:DI
在上面, cx被设置为了0x200, 也就是重复512次, 由于上面执行了cld
一句话概括一下: 从0000:7c00复制0x200字节到0000:0600
0x200十进制是512, 也就是复制引导程序(?笔者好奇为什么要这样, 不过似乎可以稍微减少一些用于表示地址的代码的体积)

然后就是一段奇特的代码:

00000017  50                push ax
00000018  681C06            push word 0x61c
0000001B  CB                retf

这是一种和Delphi编译出来的代码相似的代码
第一行push ax可以看作push 0
从上面将引导程序复制到0600可以知道, 0x0600也是引导程序, 由于16位没有GDT, 所以任何位置的数据都可以被执行
最先引起我的注意的是retf, 这里从上到下没有任何调用, 所以ret有关的代码都可以造成错误, 然后我想到了Delphi, 我记得它编译出来的代码很多这种风格的跳转, 所以这个代码意思应该就是跳转到0x61c, 否则别无他法
那么问题来了, 0x61c在哪?
再贴段代码:

0000001C  FB                sti

这时看左边, 0000001C表示这句代码(sti)在文件偏移0x001C的地方, 那么由BIOS加载后在0x7c1c, 引导扇区复制后就在0x0600 + 0x001c = 0x061c的地方了, 所以这句代码就是0x61c跳转到的位置.
sti用于打开中断功能(在大多数设备上其实是默认启用的, 推测很久以前要手动启用)

接着代码:

00000020  BDBE07            mov bp,0x7be
00000023  807E0000          cmp byte [bp+0x0],0x0
00000027  7C0B              jl 0x34
00000029  0F850E01          jnz near 0x13b
0000002D  83C510            add bp,byte +0x10

又是一段奇特的代码
第一行将bp设置为0x7be, 然后将bp位置的字节和0比较
接下来一句jl, 笔者在此之前也不知道什么意思, 然后发现一份文档上说:

Mnemonic Meaning Jump Condition
.....
JL Jump if Less (signed) SF != OF
.....

因为格式原因有点乱, jl是指有符号小于则跳转, 条件是标志位SF的值不等于OF的值
也就是说, 如果bp处的字节<0则跳转, 你可能会问:"哎这怎么可能?", 那你别跟人说看过这篇文章, 注意那个有符号, 也就是说, 当值>128的时候值被认为是-(x-128), 所以就是当值>128时跳转
接着是jnz,也就是说不等于0跳转, 这是无符号的, 也就是说没有复数, 但是!无符号的话, 上面那句>128跳转了, 这句jnz只能在<128时跳
随后是add bp,byte +0x10, 把bp的字节+0x10, 但是!>128跳转, <128跳转, 只有bp的字节=0才会继续执行

那就要先看逻辑了, 0x7be - 0x600 = 0x1be, 也就是文件偏移0x1be的地方
而这个地方代码如下:

000001BE  802021            and byte [bx+si],0x21

令人迷惑的是, 这里的值从来没有改变, 为什么上面要判断?
好吧不管了, 0x80就是0x1be的字节, 哎原来如此!0x80十进制是128!有符号的时候是0, 也就是说上面的jnz第一次是可以跳转的!
与此同时, 笔者意外发现了两个有用的东西:

00000163-0000017A: Invalid partition table
0000017B-000001B4: Error loading operating system

这两个位置实际上是两句字符串!
不管了继续看jnz, jnz的地址是0x13b, 但是注意到有个near描述, 也就是基于当前位置跳转, So?
文件偏移0x13b的代码如下:

0000013B  A0B507            mov al,[0x7b5]
0000013E  32E4              xor ah,ah
00000140  050007            add ax,0x700
00000143  8BF0              mov si,ax
00000145  AC                lodsb

也就是说, 设置al为内存0x7b5的值, 0x7b5 - 0x600是0x1b5,
0x1b5的值是什么?是000001B5 637B add [bp+di+0x7b],ah
这个地址刚刚好在Error loading operating system这个字符串的后面
0x1b5的字节是0x63, 十进制99
然后用xor清零ah
换句话说, 将ax设置为0x0063, 然后把ax + 0x700, 此时ax = 0x763
然后将ax复制到si
随后而来的是lodsb,lodsb从DS:SI复制一个字节到AL, SI因为一开始的cld所以自动+1, 哎问题又来了, 其实就是把0000:0763的一个字节读入AL嘛, 可是...可是...0x763 - 0x600是0x163...
还记得0x163是什么吗?是Invalid partition table的起始点诶!这里是字节0x49, 十进制73
接下来的代码:

00000146  3C00              cmp al,0x0
00000148  7409              jz 0x153

比较AL和0, AL=0则跳转0x153
好了贴下0x153的代码

00000153  F4                hlt
00000154  EBFD              jmp short 0x153

很好理解了吧, 死循环, 最典型的那种, 没想到Windows还会hlt省电, 节约空间的有些直接jmp死循环, 那如果不跳转呢?

0000014A  BB0700            mov bx,0x7
0000014D  B40E              mov ah,0xe
0000014F  CD10              int 0x10
00000151  EBF2              jmp short 0x145

呃, 不跳转其实也是循环, 输出Invalid partition table这句话然后死循环, 奇葩, 往回看.

重要!其实一开始就走错路了!笔者发现上面的jnz near 0x13b其实是检查DPT!!而且其实jnz不会跳转进来!

, 呜, 白忙了, 长的代*码....

哎好吧, 查完资料来解释一下吧

00000020  BDBE07            mov bp,0x7be
00000023  807E0000          cmp byte [bp+0x0],0x0
00000027  7C0B              jl 0x34
00000029  0F850E01          jnz near 0x13b

这里是检查DDT是否可以引导
后面的代码也很重要:

00000030  E2F1              loop 0x23
00000032  CD18              int 0x18

当然还有前面一句代码

0000001D  B90400            mov cx,0x4

他们连续起来就是在DDT查找第一个可引导的扇区, mov cx,0x4表示最多四个(DDT上限)
然后一句int 0x18调用磁盘启动钩子(启动CASSETTE BASIC?)也就是系统没有可用的启动磁盘
如果找到了可以引导的分区则jl 0x34会跳转, 0x34刚好在0x18后面
找不到可引导的分区则尝试使用4号DDT记录进行引导
后面的看下一篇吧
分析Windows启动过程(二)

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

推荐阅读更多精彩内容

  • 这个程序的核心目的是:试验大地址的读写,在保护模式下面寻址空间可达4GB,实模式下只能寻址1MB。(why:为什么...
    王侦阅读 816评论 0 0
  • 计算机通过执行指令序列来使机器得以工作,所以对于每一系列的计算机都有指定的一组指令集供计算机使用,这组指令...
    未来科技工作室阅读 7,993评论 1 10
  • 前面应该有一章,“一:操作系统的概述”,懒得写,但是很重要,最好去看下视频,如果有人看的话,以后有空再补 首先我们...
    Wcdaren阅读 1,734评论 0 1
  • # 常见汇编代码 # 1. 编写程序:比较AX,BX,CX中带符号数的大小,将最大的数放在AX中 code...
    喝豆腐脑加糖阅读 2,609评论 0 0
  • 核心目的:实现由实模式到保护模式的转换 核心步骤: 1)程序定义了GDT数据结构 2)16位代码进行了一些...
    王侦阅读 1,123评论 0 0