;
; The VTF signature
;
; VTF-0 means that the VTF (Volume Top File) code does not require
; any fixups.
;
vtfSignature:
DB 'V', 'T', 'F', 0
ALIGN 16
resetVector:
;
; Reset Vector
;
; This is where the processor will begin execution
;
nop
nop
jmp EarlyBspInitReal16
ALIGN 16
fourGigabytes:
本文来自 The Old New Thing专栏,作者是Raymond Chen,他是资深的微软工程师参与多个版本的Windows开发,在他的专栏中讲述了很多关于 Windows研发的趣事。本文链接在 https://devblogs.microsoft.com/oldnewthing/20030828-00/?p=42753 。原标题是 “Hardware backwards compatibility” 。
如今,我终于可以在这里为 Windows 95 辩解一番(当然,我仍然不能披露前面提到的主流生产商的名字)。
比如,我最喜欢的一个硬件产品,有着一个非常糟糕的问题:如果你将显卡插入距离电源最远的扩展槽,运行之后系统就会崩溃。很明显这是由于硬件制造商压缩成本造成的。众所周知,硬件厂商追求的 Cost Down 总会导致稀奇古怪的问题。更加不幸的是,Windows95 就是运行在这样一批稀奇古怪的硬件上的操作系统。试想如下场景就能理解为什么我们竭尽全力来适配这些硬件呢:
//
// Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
// is enabled.
//
BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
SecCoreData->BootFirmwareVolumeBase = BootFv;
SecCoreData->BootFirmwareVolumeSize = (UINTN) BootFv->FvLength;
//LABZ_Debug_Start
_outpd(0x80,0xAB);
_outpd(0x80,0xAB);
_outpd(0x80,0xAB);
_outpd(0x80,0xAB);
_outpd(0x80,0xAB);
_outpd(0x80,0xAB);
//LABZ_Debug_End
DEBUG ((DEBUG_INFO,
"Check [BootFirmwareVolumeBase]-0x%X [PeiCoreEntryPoint]=0x%X BootFirmwareVolumeBase=0x%X \n",
(UINT64)BootFv,
(UINT64)(PeiCoreEntryPoint),
(UINT64)(*PeiCoreEntryPoint)
));
编译之后们可以看到对应的机器码如下:
; 1022 : _outpd(0x80,0xAB);
00050 66 ba 80 00 mov dx, 128 ; 00000080H
00054 b8 ab 00 00 00 mov eax, 171 ; 000000abH
00059 ef out dx, eax
; 1023 : _outpd(0x80,0xAB);
0005a 66 ba 80 00 mov dx, 128 ; 00000080H
0005e b8 ab 00 00 00 mov eax, 171 ; 000000abH
00063 ef out dx, eax
; 1024 : _outpd(0x80,0xAB);
00064 66 ba 80 00 mov dx, 128 ; 00000080H
00068 b8 ab 00 00 00 mov eax, 171 ; 000000abH
0006d ef out dx, eax
; 1025 : _outpd(0x80,0xAB);
0006e 66 ba 80 00 mov dx, 128 ; 00000080H
00072 b8 ab 00 00 00 mov eax, 171 ; 000000abH
00077 ef out dx, eax
; 1026 : _outpd(0x80,0xAB);
00078 66 ba 80 00 mov dx, 128 ; 00000080H
0007c b8 ab 00 00 00 mov eax, 171 ; 000000abH
00081 ef out dx, eax
; 1027 : _outpd(0x80,0xAB);
00082 66 ba 80 00 mov dx, 128 ; 00000080H
00086 b8 ab 00 00 00 mov eax, 171 ; 000000abH
0008b ef out dx, eax
就是说,我们在 Secmain.obj 中找到 “66 ba 80 00 b8 ab 00 00 00 ef 66 ba 80 00 b8 ab 00 00 00 ef 66 ba 80 00 b8 ab 00 00 00 ef66 ba 80 00 b8 ab 00 00 00 ef 66 ba 80 00 b8 ab 00 00 00 ef 66 ba 80 00 b8 ab 00 00 00 ef” 这一串,替换成需要的代码即可。
Step2. 使用 C# 编写一个程序,接收3个参数:原文件,要搜索的十六进制数值和要替换为的十六进制数值, 写好后的程序名称为 SAR.exe (Search and Replace)
Step3. 要替换为的内容如下:
00000000 488D05F9FFFFFF lea rax,[$]
00000007 66BA0204 mov dx,0x402
; 7-0 Bits
0000000B EE out dx,al
; 15-8 Bits
0000000C 48C1E808 shr rax,8
00000010 EE out dx,al
; 23-16 Bits
00000011 48C1E808 shr rax,8
00000015 EE out dx,al
; 31-24 Bits
00000016 48C1E808 shr rax,8
0000001A EE out dx,al
; 39-32 Bits
0000001B 48C1E808 shr rax,8
0000001F EE out dx,al
; 47-40 Bits
00000020 48C1E808 shr rax,8
00000024 EE out dx,al
; 55-48 Bits
00000025 48C1E808 shr rax,8
00000029 EE out dx,al
; 63-56 Bits
0000002A 48C1E808 shr rax,8
0000002E EE out dx,al
0000002F C3 ret
最重要的动作是: push(RIP)和 RIP=tempRIP。特别注意,这里的 RIP 不是当前 call 所在的 RIP(不是 00000000`fffce7a7),而是这条指令的下一条(手册中有描述Near Call. When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) on the stack (for use later as a return-instruction pointer). The processor then branches to the address in the current code segment specified by the target operand. The target operand specifies either an absolute offset in the code segment (an offset from the base of the code segment) or a relative offset (a signed displacement relative to the current value of the instruction pointer in the EIP register; this value points to the instruction following the CALL instruction). The CS register is not changed on near calls.)就是说执行e8 c8dd ffff操作的时候,RIP=00000000 fffce7ac, 于是 Push RIP到堆栈中,然后修改 RIP。 其中的 0xFFFF DDC8(-0x2238)是相对位置,于是跳转到的位置是 00000000 FFFC E7AC+(-0x2238)= 00000000 FFFC C574。也就是右侧 call 00000000`fffcc574的由来。