You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

x86汇编单步中断处理器问题:无法识别打印mov byte ptr [bx]及BX寄存器

x86汇编单步中断处理器修复方案

问题描述

基于x86架构编写单步中断处理器时,无法识别并打印mov byte ptr [bx]指令及BX寄存器的值,期望输出格式为:

0000:0128  C60723  mov byte ptr [bx], 23h ; bx= 0001, [bx]=12

原代码如下:

.model small
.stack 100h
.data
    senasIP dw ?
    senasCS dw ?
    
    regAX dw ?
    regBX dw ?
    regCX dw ?
    regDX dw ?
    regSP dw ?
    regBP dw ?
    regSI dw ?
    regDI dw ?
    
    baitas1 db ?
    baitas2 db ?
    baitas3 db ?
    baitas4 db ?
    baitas5 db ?
    baitas6 db ?
    
    zingsn_pranesimas db "Zingsninis pertraukimas: $"
    mov_bx_kabl       db "MOV bx, $"
    bx_lygu db "bx=$"
    enteris db 13,10,"$"
.code
    mov ax, @data
    mov ds, ax
    
    mov ax, 0
    mov es, ax ;Extra segmentas prasides ten pat kur vektoriu lentele
                ;To reikia, kad galetume prieiti prie vektoriu lenteles baitu reiksmiu
    
    mov ax, es:[4]
    mov bx, es:[6]
    mov senasCS, bx
    mov senasIP, ax ;neisikeliam tiesiai, nes nera perdavimo is atminties i atminti (butina panaudoti registrus, isimtis eilutines komandos, bet jas panaudoti butu sudetingiau)

;===================PERIMAME PERTRAUKIMA==========================
    mov ax, cs
    mov bx, offset pertraukimas
    
    mov es:[4], bx
    mov es:[6], ax

;=================AKTYVUOJAME ZINGSNINI REZIMA===================
    pushf ;PUSH SF
    pop ax
    or ax, 100h ;0000 0001 0000 0000 (TF=1, kiti lieka kokie buvo)
    push ax
    popf  ;POP SF ;>Zingsninis rezimas ijungiamas po sios komandos ivykdymo - ivykdzius kiekviena sekancia komanda ivyks zingsninis pertraukimas

;==================BELEKOKIOS KOMANDOS====================
    mov byte ptr [bx],23h
    mov bx,001h
;==================ISJUNGIAME ZINGSNINI REZIMA======================
    pushf
    pop  ax
    and  ax, 0FEFFh ;1111 1110 1111 1111 (nuliukas priekyj F, nes skaiciai privalo prasideti skaitmeniu, ne raide) - TF=0, visi kiti liks nepakeisti
    push ax
    popf ;
    
    mov ax, senasIP
    mov bx, senasCS
    mov es:[4], ax
    mov es:[6], bx
    
uzdaryti_programa:
    mov ah, 4Ch
    int 21h
    
    
    
    
;==================================================================
;Pertraukimo apdorojimo procedura
;==================================================================
pertraukimas:   
    ; Save register values
    mov regAX, ax               
    mov regBX, bx
    mov regCX, cx
    mov regDX, dx
    mov regSP, sp
    mov regBP, bp
    mov regSI, si
    mov regDI, di
    
    ; Retrieve instruction bytes at CS:IP
    pop si ; Get IP value
    pop di ; Get CS value
    push di ; Restore CS
    push si ; Restore IP

    ; Copy bytes into local storage
    mov al, cs:[si]       ; First byte of instruction
    mov ah, cs:[si+1]     ; Second byte of instruction
    mov baitas1, al
    mov baitas2, ah

    cmp al, 0C6h          ; Check for opcode `C6`
    jne grizti_is_pertraukimo
    cmp ah, 07h           ; Check for ModRM byte `07` (BX addressing mode)
    jne grizti_is_pertraukimo
    
    ; Print the recognized instruction
    mov ah, 9
    mov dx, offset zingsn_pranesimas
    int 21h
    
    ; Print CS:IP
    mov ax, di
    call printAX
    mov ah, 2
    mov dl, ":"
    int 21h
    mov ax, si
    call printAX
    call printSpace
    
    ; Print machine code bytes
    mov ah, baitas1
    call printAL
    mov al, baitas2
    call printAL
    mov al, cs:[si+2]     ; Immediate byte
    call printAL
    call printSpace
    call printSpace
    
    ; Print assembly mnemonic
    mov ah, 9
    mov dx, offset mov_bx_kabl ; Text: `MOV bx,`
    int 21h
    
    mov al, cs:[si+2]     ; Immediate value
    call printAL
    
    mov ah, 2
    mov dl, "h"           ; Append `h` for hexadecimal notation
    int 21h
    
    call printSpace
    call printSpace
    
    ; Print the value of BX register before execution
    mov ah, 9
    mov dx, offset bx_lygu ; Text: `bx=`
    int 21h
    mov ax, regBX
    call printAX
    
    ; Print newline
    mov ah, 9
    mov dx, offset enteris
    int 21h
    
grizti_is_pertraukimo:
    mov ax, regAX
    mov bx, regBX
    mov cx, regCX
    mov dx, regDX
    mov sp, regSP
    mov bp, regBP
    mov si, regSI
    mov di, regDI
IRET ; Return from interrupt handler

;===================PAGALBINES pertraukime naudojamos proceduros================

;>>>Spausdinti AX reiksme
printAX:
    push ax
    mov al, ah
    call printAL
    pop ax
    call printAL
RET

;>>>>Spausdink tarpa
printSpace:
    push ax
    push dx
        mov ah, 2
        mov dl, " "
        int 21h
    pop dx
    pop ax
RET

;>>>Spausdinti AL reiksme
printAL:
    push ax
    push cx
        push ax
        mov cl, 4
        shr al, cl
        call printHexSkaitmuo
        pop ax
        call printHexSkaitmuo
    pop cx
    pop ax
RET

;>>>Spausdina hex skaitmeni pagal AL jaunesniji pusbaiti (4 jaunesnieji bitai - > AL=72, tai 0010)
printHexSkaitmuo:
    push ax
    push dx
    
    and al, 0Fh ;nunulinam vyresniji pusbaiti AND al, 00001111b
    cmp al, 9
    jbe PrintHexSkaitmuo_0_9
    jmp PrintHexSkaitmuo_A_F
    
    PrintHexSkaitmuo_A_F: 
    sub al, 10 ;10-15 ===> 0-5
    add al, 41h
    mov dl, al
    mov ah, 2; spausdiname simboli (A-F) is DL'o
    int 21h
    jmp PrintHexSkaitmuo_grizti
    
    
    PrintHexSkaitmuo_0_9: ;0-9
    mov dl, al
    add dl, 30h
    mov ah, 2 ;spausdiname simboli (0-9) is DL'o
    int 21h
    jmp printHexSkaitmuo_grizti
    
    printHexSkaitmuo_grizti:
    pop dx
    pop ax
RET

END

核心问题及修复步骤

1. 栈帧访问错误

单步中断触发时,CPU会依次压入FLAGS、CS、IP到栈中。原代码直接pop si/pop di获取IP/CS,会破坏栈的原始结构,导致IRET无法正确恢复执行环境。

修复方式:通过BP寄存器访问栈中的CS和IP,避免修改栈结构:

pertraukimas:   
    ; 先保存所有寄存器到栈中(而非数据段变量,更可靠)
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push bp
    push sp
    
    mov bp, sp
    ; 栈结构:SP, BP, DI, SI, DX, CX, BX, AX, FLAGS, CS, IP
    mov si, [bp+14]   ; 取出IP值
    mov di, [bp+12]   ; 取出CS值

2. 寄存器保存时机错误

原代码在中断处理开头直接将BX存入regBX,但此时BX已被之前的中断向量设置代码修改,无法获取指令执行前的BX值。

修复方式:在触发单步中断前,确保BX指向合法内存,且通过栈保存的寄存器值来获取指令执行前的BX:

; 在主程序中先设置BX指向数据段变量
.data
    some_var db 12h ; 初始值12h,对应期望输出的[bx]=12
.code
;==================BELEKOKIOS KOMANDOS====================
    mov bx, offset some_var ; 设置BX指向数据段变量
    mov byte ptr [bx],23h   ; 待跟踪的指令
    mov bx,001h

3. 字符串常量错误

原代码中mov_bx_kabl定义为"MOV bx, $",与期望输出的mov byte ptr [bx],不符,导致打印错误。

修复方式:修改字符串定义:

.data
    mov_byte_bx       db "mov byte ptr [bx], $"
    semicolon_str     db " ; $"
    bracket_bx        db "[bx]=$"

4. 缺失[bx]值打印逻辑

原代码未实现读取并打印BX指向内存值的功能,无法满足期望输出。

修复方式:添加打印[bx]值的代码:

; 打印分号和空格
    mov ah, 9
    mov dx, offset semicolon_str
    int 21h
    
    ; 打印BX值
    mov ah, 9
    mov dx, offset bx_lygu
    int 21h
    mov ax, [bp+8] ; 从栈中取出指令执行前的BX值
    call printAX
    
    ; 打印[bx]值
    mov ah, 2
    mov dl, ","
    int 21h
    call printSpace
    mov ah, 9
    mov dx, offset bracket_bx
    int 21h
    mov bx, [bp+8]
    mov al, [bx]
    call printAL
    mov ah, 2
    mov dl, "h"
    int 21h

5. 单步中断触发时机注意

TF标志位会让CPU在每条指令执行完毕后触发INT1,因此要打印指令执行前的寄存器值,需确保获取的是中断触发前的寄存器状态(通过栈保存的值)。

修改后的完整代码

.model small
.stack 100h
.data
    senasIP dw ?
    senasCS dw ?
    
    zingsn_pranesimas db "Zingsninis pertraukimas: $"
    mov_byte_bx       db "mov byte ptr [bx], $"
    semicolon_str     db " ; $"
    bx_lygu           db "bx=$"
    bracket_bx        db "[bx]=$"
    enteris           db 13,10,"$"
    some_var          db 12h ; 用于测试[bx]的初始值
.code
    mov ax, @data
    mov ds, ax
    
    ; 保存原INT1中断向量
    mov ax, 0
    mov es, ax
    mov ax, es:[4]
    mov senasIP, ax
    mov ax, es:[6]
    mov senasCS, ax

    ; 设置自定义INT1中断向量
    mov ax, cs
    mov bx, offset pertraukimas
    mov es:[4], bx
    mov es:[6], ax

    ; 开启单步模式
    pushf
    pop ax
    or ax, 100h
    push ax
    popf

    ; 测试指令
    mov bx, offset some_var
    mov byte ptr [bx],23h
    mov bx,001h

    ; 关闭单步模式
    pushf
    pop ax
    and ax, 0FEFFh
    push ax
    popf

    ; 恢复原INT1中断向量
    mov ax, senasIP
    mov bx, senasCS
    mov es:[4], ax
    mov es:[6], bx
    
uzdaryti_programa:
    mov ah, 4Ch
    int 21h

;==================================================================
; 单步中断处理程序
;==================================================================
pertraukimas:   
    ; 保存所有寄存器到栈中
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push bp
    push sp
    
    mov bp, sp
    ; 从栈中获取IP和CS(指令执行前的地址)
    mov si, [bp+14]
    mov di, [bp+12]

    ; 读取指令字节
    mov al, cs:[si]
    mov ah, cs:[si+1]

    ; 判断是否为mov byte ptr [bx], imm8指令(opcode C6, ModRM 07)
    cmp al, 0C6h
    jne grizti_is_pertraukimo
    cmp ah, 07h
    jne grizti_is_pertraukimo
    
    ; 打印中断提示
    mov ah, 9
    mov dx, offset zingsn_pranesimas
    int 21h
    
    ; 打印CS:IP
    mov ax, di
    call printAX
    mov ah, 2
    mov dl, ":"
    int 21h
    mov ax, si
    call printAX
    call printSpace
    
    ; 打印机器码
    mov ah, al
    call printAL
    mov al, ah
    call printAL
    mov al, cs:[si+2]
    call printAL
    call printSpace
    call printSpace
    
    ; 打印汇编助记符
    mov ah, 9
    mov dx, offset mov_byte_bx
    int 21h
    mov al, cs:[si+2]
    call printAL
    mov ah, 2
    mov dl, "h"
    int 21h
    
    ; 打印寄存器和内存值
    mov ah, 9
    mov dx, offset semicolon_str
    int 21h
    
    mov ah, 9
    mov dx, offset bx_lygu
    int 21h
    mov ax, [bp+8] ; 取出指令执行前的BX值
    call printAX
    
    mov ah, 2
    mov dl, ","
    int 21h
    call printSpace
    mov ah, 9
    mov dx, offset bracket_bx
    int 21h
    mov bx, [bp+8]
    mov al, [bx]
    call printAL
    mov ah, 2
    mov dl, "h"
    int 21h
    
    ; 打印换行
    mov ah, 9
    mov dx, offset enteris
    int 21h
    
grizti_is_pertraukimo:
    ; 恢复所有寄存器
    pop sp
    pop bp
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    IRET

;=================== 辅助打印函数 ====================

; 打印AX的16进制值
printAX:
    push ax
    mov al, ah
    call printAL
    pop ax
    call printAL
RET

; 打印空格
printSpace:
    push ax
    push dx
        mov ah, 2
        mov dl, " "
        int 21h
    pop dx
    pop ax
RET

; 打印AL的8进制值(两位16进制)
printAL:
    push ax
    push cx
        push ax
        mov cl, 4
        shr al, cl
        call printHexSkaitmuo
        pop ax
        call printHexSkaitmuo
    pop cx
    pop ax
RET

; 打印单个16进制字符
printHexSkaitmuo:
    push ax
    push dx
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:58:04