几年前写的小玩意儿: 黑苹果引导工具tboot
若干年前,tpu听说苹果的MacOSX也推出PC版本了,十分兴奋,赶紧下载ISO想试试看。我记得下载的版本是10.4.6吧。然后准备安装。由于硬盘前面都满了,于是找了个扩展分区安装。安装后傻眼了......不启动啊。然后才开始上网看各种文档,这才知道,MacOSX只能安装在主分区。
tpu不甘心啊,找来各种资料研究。darwin本身是开放源代码的,关于引导的文件,都可以找得到。比如chain0, boot0 boot1 boot2等。有了这些原始的资料帮助,再加上tpu很久之前就研究过MBR这些东西,于是就拼凑出了tboot这个东西(tpu's bootloader)。
tboot的原理很简单,在boot0的代码基础上,完善了扩展分区的搜索代码。这样不论系统装在哪个分区上,都可以找得到。找到之后,再加载该分区前面的boot1部分。boot1再解析分区结构,继续加载boot2。boot2完成整个OSX的加载。
boot2运行后,如果你的硬盘有多个分区,会显示一个分区列表,让你选择从哪一个分区启动。默认的选项是第一个分区。但一般OSX所在的分区不是第一个,这样每次启动你必须再手动选择一次,很是麻烦。于是tpu对boot2打了个补丁,让它把OSX所在的分区作为默认分区。这样就方便许多了。
虽然tpu为引导OSX费了这么大的劲,但OSX只在我硬盘上存在几周就消失了。用起来实在是不习惯啊。不过副产品tboot一直有很多网友在用,它确实解决了一些实际的问题,tpu觉得所作的努力还是值得的。听说现在引导工具已经是百花齐放了,比tboot各种先进。不知道tboot是否还能继续引导最新的OSX...
; tboot.s;; Load HFS volume on any partition(main or ext); writen by tpu, base on boot0 source from Darwin;; Set to 1 to enable obscure debug messages.DEBUG EQU 0; Various constants.kBoot0Segment EQU 0x0000kBoot0Stack EQU 0xFFF0 ; boot0 stack pointerkBoot0LoadAddr EQU 0x7C00 ; boot0 load addresskBoot0RelocAddr EQU 0xE000 ; boot0 relocated addresskBoot2Sectors EQU 126 ; sectors to load for boot2kBoot2Address EQU 0x0000 ; boot2 load addresskBoot2Segment EQU 0x2000 ; boot2 load segmentkMBRBuffer EQU 0x1000 ; MBR buffer addresskExtBuffer EQU 0x1200 ; MBR buffer addresskPartTableOffset EQU 0x1bekMBRPartTable EQU kMBRBuffer + kPartTableOffsetkExtPartTable EQU kExtBuffer + kPartTableOffsetkSectorBytes EQU 512 ; sector size in byteskBootSignature EQU 0xAA55 ; boot sector signaturekPartCount EQU 4 ; number of paritions per tablekPartTypeBoot EQU 0xab ; boot2 partition typekPartTypeUFS EQU 0xa8 ; UFS partition typekPartTypeHFS EQU 0xaf ; HFS partition typekPartTypeExtDOS EQU 0x05 ; DOS extended partition typekPartTypeExtWin EQU 0x0f ; Windows extended partition typekPartTypeExtLinux EQU 0x85 ; Linux extended partition typekPartActive EQU 0x80kDriveNumber EQU 0x80; Format of fdisk partition entry.; The symbol 'part_size' is automatically defined as an `EQU'; giving the size of the structure. struc part.bootid: resb 1 ; bootable or not .head: resb 1 ; starting head, sector, cylinder.sect: resb 1 ;.cyl: resb 1 ;.type: resb 1 ; partition type.endhead resb 1 ; ending head, sector, cylinder.endsect: resb 1 ;.endcyl: resb 1 ;.lba: resd 1 ; starting lba.sectors resd 1 ; size in sectors endstruc; Macros.%macro DebugCharMacro 1mov al, %1call _putc%endmacro%if DEBUG%define putc(x)DebugCharMacro x%else%define putc(x)%endif%macro puts 1mov si, %1call put_string%endmacro;--------------------------------------; Start of text segment.SEGMENT .textORG 0xe000 ; must match kBoot0RelocAddr;--------------------------------------; Boot code is loaded at 0:7C00h.start:; Set up the stack to grow down from kBoot0Segment:kBoot0Stack.; Interrupts should be off while the stack is being manipulated.cli ; interrupts offxor ax, ax ; zero axmov ss, ax ; ss <- 0mov sp, kBoot0Stack ; sp <- top of stacksti ; reenable interruptsmov es, ax ; es <- 0mov ds, ax ; ds <- 0; Relocate boot0 code.mov si, kBoot0LoadAddr ; si <- sourcemov di, kBoot0RelocAddr ; di <- destinationcld ; auto-increment SI and/or DI registersmov cx, kSectorBytes/2 ; copy 256 wordsrepnz movsw ; repeat string move (word) operation; Code relocated, jump to start_reloc in relocated location.jmp 0:start_reloc;--------------------------------------; Start execution from the relocated location.;start_reloc:.loop_next_drive:; Clear drive flags.xor eax, eaxmov [first_part], eax ; clear EBIOS LBA offsetmov [this_part], eax%if DEBUGputc('D')mov al, dlcall putbputc(' ')%endif; Read MBR sector to memory.mov al, 1 ; load one sectormov bx, kMBRBuffer ; MBR load addressxor ecx, ecxcall loadjc .next_drivemov di, kMBRBuffermov si, kMBRBuffer+kPartTableOffsetcmp WORD [si + part_size * kPartCount], kBootSignaturejne .next_drive ; Signature error; Look for the HFS partition in the MBR partition tablecall find_boot ; will not return on success.next_drive:inc dl ; next drive numbertest dl, 0x4 ; went through all 4 drives?jz .loop_next_drive ; not yet, loop againputs part_error_str.hang:hltjmp SHORT .hang;--------------------------------------; Find the HFS partition and load the booter from the partition.;--------------------------------------find_boot:mov cx, 4 ; number of partition entries per table.loop_part:push cxpush si%if DEBUGmov al, [si + part.type]call putbputc(' ')mov eax, [si + part.lba]call putdputc(10)putc(13)%endifmov eax, [si + part.lba]mov bl, [si + part.type] ; print partition typecmp bl, kPartTypeExtLinux ; Extended Linuxje.ext_checkcmp bl, kPartTypeExtDOS ; Extended DOSje.ext_checkcmp bl, kPartTypeExtWin ; Extended Windows(95)jne.nonext.ext_check:cmp di, kMBRBufferjnz .ext_not_mbrmov [first_part], eaxxor eax, eax.ext_not_mbr:mov [this_part], eax; Read extended partition tablemov ecx, eaxmov al, 1 ; load one sectormov bx, kExtBuffer ; extended load addresscall loadjc .next_partmov si, kExtBuffer+kPartTableOffsetcmp WORD [si + part_size * kPartCount], kBootSignaturejne .next_part ; Signature errorpush dimov di, kExtBuffercall find_bootpopdicmp di, kMBRBufferjnz .next_partxor eax, eaxmov [this_part], eaxmov [first_part], eax.next_part:pop sipop cxadd si, part_size ; advance SI to next partition entryloop .loop_part ; loop through all partition entries.exit_findret.nonext:cmp bl, 0je .next_partcmp bl, kPartTypeHFSje .found_partcmp bl, kPartTypeUFSje .found_partcmp bl, kPartTypeBootjne .next_part.found_part:add eax, [this_part]mov ecx, eaxadd eax, [first_part]mov [si+part.lba], eax; Found boot partition, read boot sector to memory.mov bx, kBoot0LoadAddrmov al, 1call loadjc .next_part ; load error, keep looking?putc("F")cmp WORD [bx + 510], kBootSignaturejnz .next_part; patch boot1hmov eax, [0x7cbb]cmp eax, 0x20000200jnz .no_patchmov eax, 0x00007d40mov [0x7cbb], eaxpush simov si, patch_code_startmov di, 0x7d40mov cx, (patch_code_end-patch_code_start)cldrepnzmovsbpop si.no_patch:; Jump to partition booter. The drive number is already in register DL.; SI is pointing to the modified partition entry.jmp kBoot0Segment:kBoot0LoadAddr;--------------------------------------; load - Read sectors from a partition using LBA addressing.;--------------------------------------load:pushad ; save all registersmov bp, sp ; save current SP;push DWORD 0 ; offset 12, upper 32-bit LBApush ds ; For sake of saving memory,push ds ; push DS register, which is 0.add ecx, [first_part] ; offset 8, lower 32-bit LBApush ecxpush es ; offset 6, memory segmentpush bx ; offset 4, memory offsetxor ah, ah ; offset 3, must be 0push ax ; offset 2, number of sectorspush WORD 16 ; offset 0-1, packet size%if DEBUGputc('L')mov eax, ecxcall putdputc(10)putc(13)%endifmov si, spmov ah, 0x42int 0x13mov sp, bp ; restore SPpopadret;--------------------------------------; Write a string to the console.;; Arguments:; DS:SI pointer to a NULL terminated string.;; Clobber list:; AX, BX, SI;put_stringmov bx, 1 ; BH=0, BL=1 (blue)cld ; increment SI after each lodsb call.looplodsb ; load a byte from DS:SI into ALcmp al, 0 ; Is it a NULL?je .exit ; yes, all donemov ah, 0xE ; INT10 Func 0xEint 0x10 ; display byte in tty modejmp short .loop.exitret%if DEBUG; Show a DWORD value.putdror eax, 16call putwror eax, 16; Show a WORD value.putwror ax, 8call putbror ax, 8; Show a BYTE value.putbror al, 4call put_nibbleror al, 4; Show 4 bit value.put_nibblepush eaxand al, 0x0fadd al, 0x30cmp al, 0x39jna .pasciiadd al, 0x07.pasciicall _putcpop eaxret; Show a ASCII character to the console._putcpushamov bx, 1mov ah, 0x0eint 0x10poparet%endif ;DEBUGpatch_code_start:; patch boot2: SelectBootVolumemov di, 0x39acmov eax, [es:di]cmp eax, 0x01a8da45jne .no_patchmov al, 2mov [es:di+3], al.no_patch:; Jump to boot2. The drive number is already in register DL.jmp kBoot2Segment:kBoot2Address + kSectorBytespatch_code_end:; NULL terminated strings.part_error_str: db 'No HFS partition found', 10, 13, 0;--------------------------------------; Pad the rest of the 512 byte sized booter with zeroes. The last; two bytes is the mandatory boot sector signature.pad_boottimes 510-($-$$) db 0dw kBootSignatureABSOLUTE 0xE400; In memory variables.first_part resd 1 ; starting LBA of the intial extended partition.this_part resd 1 ; starting LBA of the current extended partition. part_base resd 1 END