bochs调试 WONDOWS 10
基本配置
1.启动双击bochsdbg.exe
2.启动配置
常用调试命令
断点操作
b [address] 设置断点,其中 [address] 可以是物理地址(如 pb 0x7c00)、虚拟地址(如 vb 0:0x7c00)或线性地址(如 lb 0x7c00)。
物理地址
pb 0x7c00
虚拟地址
vb 0:0x7c00
线性地址
lb 0x7c00
info break 查询已经设置的所有断点。
程序执行控制
c 或 cont 或 continue 继续执行程序,直到遇到下一个断点。
继续执行,直到遇到下一个断点
c
s 或 step 或 si 或 stepi 单步执行指令。如果带参数 [count],则执行指定数量的指令,如 step 3 将执行接下来的三条指令。
执行1个命令
s
执行3个命令
step 3
寄存器查询
- 段寄存器查询
sreg
- 普通寄存器查询
reg
- 寄存器查询
dump_cpu
info [registers] 或 i r 显示所有寄存器的当前值
查询cup 寄存器的值
info cup
- 内存操作
x /fmt [address] [size] 查看内存区域的内容。/fmt 可以是 /x(十六进制)、/o(八进制)、/d(十进制)或 /a(ASCII)。
mwrite [address] [data] 向内存地址写入数据。
- 其他命令
q 或 quit 或 exit 停止调试并退出 Bochs。
help 或 h 显示帮助信息,可以跟具体命令获取详细帮助,如 help x。
- 反汇编
disassemble [start] [end] 或 dis 反汇编指定范围内的机器码。
- 断点管理
delete 或 del 或 d [breakpoint number] 删除指定的断点。
寄存器
段寄存器
cs:ip 代码段
ds 数据段
mov ax, [bx] ;等价于:mov ax, [ds:bx]
ss :sp 堆栈段 bp
附加段:ES、FS、GS
数据访问:AX,BX.CX,SI,DI
写入磁盘命令
dd if=mbr.bin of=.vhd bs=512 count=1 seek=2
参数解析
- if=mbr.bin: 指定输入文件为mbr.bin,通常这是一个包含主引导记录(MBR)的二进制文件。
- of=.vhd: 指定输出文件为.vhd,这是Virtual Hard Disk(虚拟硬盘)的文件格式,常用于虚拟机环境。
- bs=512: 设置每次读取或写入的数据块大小为512字节,这通常是磁盘扇区的标准大小。
- count=1: 只读取并写入一个数据块。
- seek=2: 在输出文件中跳过前两个512字节的块,从第三个512字节块开始写入数据。
中断
int 10
- AH = 00h:设置显示模式,如文本模式或图形模式。
- AH = 01h:光标位置,设置光标在屏幕上的位置。
- AH = 02h:写入字符,将一个字符写入当前光标位置。
- AH = 03h:读取字符,从当前光标位置读取一个字符。
- AH = 04h:写入字符串,将一个字符串写入当前光标位置。
- AH = 05h:读取字符串,从当前光标位置读取一个字符串。
- AH = 06h:清屏,清除屏幕显示。
- AH = 07h:设置光标形状。
- AH = 08h:设置光标可见性。
- AH = 09h:设置字符属性。
- AH = 0Ah:设置光标起始位置。
- AH = 0Bh:设置光标结束位置。
- AH = 0Ch:设置字符颜色。
- AH = 0Dh:设置页面颜色。
- AH = 0Eh:在当前光标位置输出一个字符。
- AH = 0Fh:读取光标位置。
mov ax, 0600h ;AH=06h,AL=00H 调用06h功能,清屏
mov bx, 0700h
mov cx,0
mov dx,184fh ;(80,25)
int 0x80
- 设置 EAX 寄存器: 在发起系统调用之前,EAX 寄存器必须被设置为一个代表所需系统调用编号的值。每个系统调用都有一个唯一的编号,例如,write 系统调用的编号通常是 4。
- 设置其他寄存器: 其他寄存器(如 EBX, ECX, EDX 等)用于传递给系统调用的参数。具体哪些参数应该放在哪个寄存器中,取决于具体的系统调用。
- 执行 int 0x80: 执行 int 0x80 指令后,处理器会跳转到内核中预先设定的中断处理程序。这个处理程序会读取 EAX 寄存器中的系统调用编号,然后根据编号执行相应的系统调用服务。
- 返回结果: 系统调用完成后,结果通常会被存储在 EAX 寄存器中,错误码则可能存储在其他寄存器中,如 EFLAGS。
section .data
message db 'Hello, World!',0section .text
global _start_start:
; 设置系统调用编号(write = 4)
mov eax, 4; 设置第一个参数(文件描述符,标准输出 = 1)
mov ebx, 1; 设置第二个参数(要写的字符串的地址)
mov ecx, message; 设置第三个参数(要写入的字节数)
mov edx, 13; 发起系统调用
int 0x80; 结束程序
mov eax, 1 ; 系统调用编号(exit = 1)
xor ebx, ebx ; exit code = 0
int 0x80
lba读写
lba支持的磁盘有:ide、SATA、SCSI、SSD、USB
lba的寄存器有:data寄存器,device寄存器,command寄存器
LBA28方式使用28位来描述一个扇区地址,最大支持128GB的硬磁盘容量。
data寄存器 | 0x1F0 | 已经读取或写入的数据,大小为两个字节(16位数据) 每次读取1个word,反复循环,直到读完所有数据 |
features寄存器 | 0x1F1 | 读取时的错误信息 写入时的额外参数 |
sector count寄存器 | 0x1F2 | 指定读取或写入的扇区数 |
LBA low寄存器 | 0x1F3 | lba地址的低8位 |
LBA mid寄存器 | 0x1F4 | lba地址的中8位 |
LBA high寄存器 | 0x1F5 | lba地址的高8位 |
device寄存器 | 0x1F6 | lba地址的前4位(占用device寄存器的低4位) 主盘值为0(占用device寄存器的第5位) 第6位值为1 LBA模式为1,CHS模式为0(占用device寄存器的第7位) 第8位值为1 |
command寄存器 | 0x1F7 | 读取,写入的命令,返回磁盘状态 1 读取扇区:0x20 写入扇区:0x30 磁盘识别:0xEC |
mbr与ide磁盘的读写
;能够将第二个扇区里面的内容加载进入内存
;mbr.asm loader.asm kernal.asm
;0 1 3
;将loader放入0x900
[bits 16]
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2 ;表示已LBA方式,我们的loader存在第2块扇区
SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
;利用0x06的功能,调用10号中断,
; AH = 0x06
; AL = 0 表示全部都要清楚
; BH = 上卷行的属性
;(CL,CH) 左上角 x,y
;(DL,DH) 右下角mov ax, 0600h
mov bx, 0700h
mov cx,0
mov dx,184fh ;(80,25)
int 10h
;输出当前我们在MBR
mov byte [gs:0x00], '1'
mov byte [gs:0x01], 0xA4
mov byte [gs:0x02], ' '
mov byte [gs:0x03], 0xA4
mov byte [gs:0x04], ' '
mov byte [gs:0x05], 0xA4
mov byte [gs:0x06], 'B'
mov byte [gs:0x07], 0xA4
mov byte [gs:0x08], 'R'
mov byte [gs:0x09], 0xA4
mov eax,LOADER_START_SECTOR ;LBA 读入的扇区
mov bx,LOADER_BASE_ADDR ;写入的地址
mov cx,1 ;等待读入的扇区数
call rd_disk
jmp LOADER_BASE_ADDR ;调到实际的物理内存rd_disk:
;eax LBA的扇区号
;bx 数据写入的内存地址
;cx 读入的扇区数
mov esi,eax ;备份eax
mov di,cx ;备份cx
;读一个扇区
mov dx, 0x1f2
mov al, cl
out dx, al
;将LBA的地址存入0x1f3,0x1f6
;7-0位写入0x1f3
mov eax,esi
mov dx, 0x1f3
out dx,al
;15-8位写给1f4
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;23-16位写给1f5
shr eax,cl
mov dx,0x1f5
out dx,al
;divice寄存器设置
shr eax,cl
and al,0x0f
or al,0xe0 ;设置7-4位为1110,此时才是lBA模式
mov dx,0x1f6
out dx,al
;设置commnd寄存器 向0x1f7写入读命令
mov dx,0x1f7
mov al,0x20
out dx,al
;检测硬盘状态
.not_ready:
nop
in al,dx
and al,0x88; 4位为1,表示可以传输,7位为1表示硬盘忙
cmp al,0x08
jnz .not_ready
;data寄存器设置 读数据
mov ax,di
mov dx, 256
mul dx
mov cx,ax
mov dx,0x1f0
.go_on:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on
ret
times 510 - ($-$$) db 0
dw 0xaa55
loader
;能够将第9个扇区里面的内容加载进入内存
;mbr.asm loader.asm kernal.asm
;0 1 3
;将kernal放入0x1500KERNAL_BASE_ADDR equ 0x1500
KERNAL_START_SECTOR equ 0x9
LOADER_BASE_ADDR equ 0x900
section loader vstart=LOADER_BASE_ADDR
mov ax,0xb800;指定文本模式显示缓存区
mov es,axmov byte [es:0x00],'o'
mov byte [es:0x01],0x07
mov byte [es:0x02],'k'
mov byte [es:0x03],0x06mov eax,KERNAL_START_SECTOR ;LBA 读入的扇区
mov bx,KERNAL_BASE_ADDR ;写入的地址
mov cx,1 ;等待读入的扇区数
call rd_disk
jmp KERNAL_BASE_ADDR ;调到实际的物理内存rd_disk:
;eax LBA的扇区号
;bx 数据写入的内存地址
;cx 读入的扇区数
mov esi,eax ;备份eax
mov di,cx ;备份cx
;读一个扇区
mov dx, 0x1f2
mov al, cl
out dx, al
;将LBA的地址存入0x1f3,0x1f6
;7-0位写入0x1f3
mov eax,esi
mov dx, 0x1f3
out dx,al
;15-8位写给1f4
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al
;23-16位写给1f5
shr eax,cl
mov dx,0x1f5
out dx,al
;divice寄存器设置
shr eax,cl
and al,0x0f
or al,0xe0 ;设置7-4位为1110,此时才是lBA模式
mov dx,0x1f6
out dx,al
;设置commnd寄存器 向0x1f7写入读命令
mov dx,0x1f7
mov al,0x20
out dx,al
;检测硬盘状态
.not_ready:
nop
in al,dx
and al,0x88; 4位为1,表示可以传输,7位为1表示硬盘忙
cmp al,0x08
jnz .not_ready
;data寄存器设置 读数据
mov ax,di
mov dx, 256
mul dx
mov cx,ax
mov dx,0x1f0
.go_on:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on
ret
kernal
[bits 16]
KERNAL_BASE_ADDR equ 0x1500
section kernal vstart=KERNAL_BASE_ADDRmov ax,0xb800 ;指向文本模式的显示缓冲区
mov es,axmov byte [es:0x00],'z'
mov byte [es:0x01],0xA4
mov byte [es:0x02],'h'
mov byte [es:0x03],0xA4
jmp $
times 512 - ($-$$) db 0
c与nasm联合编程
小试牛刀
1.gcc 编译命令
-o指定目标文件
-m32 32系统架构
-c 生成可链接文件:.o文件
gcc -o test.c.o -m32 -c test.c
test.c
void myprint(char *msg,int len);
int chooseNUM(int a,int b)
{
if(a>b)
{
myprint("a is big\n",9);
}
else
{
myprint("b is big\n",9);
}
}
2.nasm 编译命令
-f 生成elf类型的文件
-o 生成目标文件
nasm -f elf -o nasm.c.asm.o test.c.asm
test.c.asm
extern chooseNUM
[section .data]
num1 dd 22
num2 dd 33
[section .text]
global _start
global myprint
_start:
push num1
push num2
call chooseNUM
add esp,4
mov ebx,0
mov eax,1;sys_exit
int 0x80
myprint:
mov edx,[esp+8]
mov ecx,[esp+4]
mov ebx,1
mov eax,4
int 0x80;sys_write
ret
3.ld 链接
-s 精简
-m 指定系统架构
-o 生成目标问价
-e 指定入口函数:default:_start
ld -s -m elf_i386 -o cAndNasm test.c.o test.c.asm.o
4.执行
联合编程成功
开始开发
1.改造test.c
void myprint();
int DISPLAY()
{
myprint();
while(1)
{
}
}
2.改造test.c.asm
extern DISPLAY
[bits 16]
[section .text]
global _start
global myprint
_start:
call DISPLAY
myprint:
mov ax,0xb800
mov es,ax
mov byte [es:0x00],'A'
mov byte [es:0x01],0x07
ret
3.生成elf的.o文件
#-f efl 生成elf格式的问价
nasm -f elf -o testc.asm.o test.c.asm
#-m32 指定架构系统 -c 生成.o文件
gcc -m32 -c -o testc.o test.c
4.链接文件
#-s 精简 -m 指定系统架构 -Ttext 指定程序入口
ld -s -m elf_i386 -Ttext 0x1500 -o kernal testc.asm.o testc.o
5.objcopy 生成纯二进制文件
-O(大写) 生成文件类型 :binary
objcopy -O binary kernal kernal.bin
6.完成 dd 写入硬盘
安全模式
GDT
GDTR
nasm代码
;----------------------------------------------------------------------------
DA_DR EQU 90h ; 存在的只读数据段类型值
DA_DRW EQU 92h ; 存在的可读写数据段属性值
DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值
DA_C EQU 98h ; 存在的只执行代码段属性值
DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值
DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值
DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值
;----------------------------------------------------------------------------
DA_32 EQU 4000h ; 32 位段DA_DPL0 EQU 00h ; DPL = 0
DA_DPL1 EQU 20h ; DPL = 1
DA_DPL2 EQU 40h ; DPL = 2
DA_DPL3 EQU 60h ; DPL = 3
;----------------------------------------------------------------------------
%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限 1 (2 字节)
dw %1 & 0FFFFh ; 段基址 1 (2 字节)
db (%1 >> 16) & 0FFh ; 段基址 2 (1 字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性 1 + 段界限 2 + 属性 2 (2 字节)
db (%1 >> 24) & 0FFh ; 段基址 3 (1 字节)
%endmacro ;org 07c00h
jmp LABEL_BEGIN[SECTION .gdt]
; GDT
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ; 非一致代码段, 32
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
; GDT 结束GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限
dd 0 ; GDT基地址; GDT 选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt][SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h; 回写初始化 32 位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE32
mov word [LABEL_DESC_CODE32 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE32 + 4], al
mov byte [LABEL_DESC_CODE32 + 7], ah; 为加载 GDTR 作准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址; 加载 GDTR
lgdt [GdtPtr]; 关中断
cli; 打开地址线A20
in al, 92h
or al, 00000010b
out 92h, al; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax; 真正进入保护模式
jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处
; END of [SECTION .s16]
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax ; 视频段选择子(目的)mov edi, (80 * 3 + 0) * 2 ; 屏幕第 3 行, 第 0 列。
mov ah, 0Ch ; 0000: 黑底 1100: 红字
mov al, 'D'
mov [gs:edi], ax; 到此停止
jmp $SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
times 361 db 0
dw 0xaa55