找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 69|回复: 2

【处理器】eip跨界溢出行为在不同CPU上的表现

[复制链接]
发表于 4 天前 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×

前言

属于是弹幕流以前好奇过的一个问题了:如果访问的内存跨越地址边界,CPU会怎么样。实验一下之后发现:这个答案取决于CPU自己怎么实现的。Intel和AMD的行为是不一样的。

实验理论

为了防止弹幕流能看懂,这里直接用纯汇编实现,因此选择了传统BIOS环境。
代码的基本逻辑如下:

  1. 进入保护模式
  2. 构造对等页表:即虚拟地址=物理地址。
  3. 将0x000-0xFFF和0xFFFFF000-0xFFFFFFFF范围映射到已知有效的一个物理页上。这样保证跨界的地址总是指向有效的物理内存。
  4. 写一个跨界的指令。这里以cpuid(字节码是0F A2)为例,把0F写在0xFFFFFFFF上,把A2写在0x0上。别忘了构造一个返回指令。
  5. 构造IDT接收异常。我推测是CPU会认为这个指令可能会引起cs段出界,那么应该是#GP异常。那么IDT只需要接住这个异常即可。
  6. 执行0xFFFFFFFF处的cpuid指令。
  7. 如果IDT接收到异常,则向串口和屏幕输出接到异常的指令。
  8. 如果顺利返回,则向串口和屏幕输出cpuid的执行结果。这个结果包含的是当前CPU的型号。

选择同时向屏幕和串口输出应该可以保证绝大部分机器都能看到输出了。

实验代码

bits 16
org 0x7C00

%macro write_serial 2
    mov dx,0x3f8+%1
    mov al,%2
    out dx,al
%endmacro

start:
    cli
    ; Reset DS segment
    xor ax,ax
    mov ds,ax
    ; Load new GDT
    lgdt [GDTR]
    ; Turn on Protection Mode
    mov eax,CR0
    or al,1
    mov cr0,eax
    ; Refresh code segment
    jmp dword 0x08: PEStart

GDTR:
.limit:
    dw GDT.End-GDT-1
    dd GDT

GDT:
    ; Null selector
    dq 0
    ; CS.Limit
    dw 0xFFFF
    ; CS.BaseLow
    dw 0
    ; CS.BaseMid
    db 0
    ; CS.Attrib
    dw 0xCF9A
    ; CS.BaseHigh
    db 0
    ; DS.Limit
    dw 0xFFFF
    ; DS.BaseLow
    dw 0
    ; DS.BaseMid
    db 0
    ; DS.Attrib
    dw 0xCF92
    ; DS.BaseHigh
    db 0
    ; CS.Limit
    dw 0xFFFF
    ; CS.BaseLow
    dw 0x800
    ; CS.BaseMid
    db 0
    ; CS.Attrib
    dw 0xCF9A
    ; CS.BaseHigh
    db 0
    ; DS.Limit
    dw 0xFFFF
    ; DS.BaseLow
    dw 0x800
    ; DS.BaseMid
    db 0
    ; DS.Attrib
    dw 0xCF92
    ; DS.BaseHigh
    db 0
.End:

bits 32
PEStart:
    ; Initialize segment registers
    mov ax,0x10
    mov es,ax
    mov ss,ax
    mov ds,ax
    mov gs,ax
    mov ax,0x20
    mov fs,ax
    ; Setup stack
    mov esp,0xFFFC
    ; Initialize Serial Port
    write_serial 1,0x00
    write_serial 3,0x80
    write_serial 0,0x03
    write_serial 1,0x00
    write_serial 3,0x03
    write_serial 2,0xC7
    write_serial 4,0x0B
    ; Setup PDEs
    mov edi,0x8000
    mov eax,0x87
    mov edx,0x200000
    call init_pxe
    ; Setup PTE - 0xFFC000000 to 0xFFFFFFFF
    mov dword [0x8FFC],0x9007
    mov eax,0xFFC00007
    mov edx,0x1000
    call init_pxe
    ; Setup PTE - 0x000 to 0xFFF
    mov dword [0x8000],0xA007
    mov eax,0x7
    call init_pxe
    ; Map 0xFFFFF000 to 0xB000
    mov dword [0x9FFC],0xB007
    ; Map 0x0 to 0x000
    mov dword [0xA000],0xB007
    ; Setup CR4
    mov eax,0x18
    mov cr4,eax
    ; Setup CR3
    mov eax,0x8000
    mov cr3,eax
    ; Enable Paging
    mov eax,cr0
    bts eax,31
    mov cr0,eax
    ; Setup IDT
    mov word [0xC068],gp_fault_handler
    mov word [0xC06A],0x08
    mov dword [0xC06C],0x8E00
    lidt [IDT]
    ; Create CPUID instruction at 0xFFFFFFFF
    mov byte [0xFFFFFFFF],0x0F
    mov byte [0x0],0xA2
    ; Create return instruction
    mov byte [0x1],0xC3
    mov esi,0x7E00
    mov edi,0xB8000
    ; Jump
    mov eax,0x80000002
    push 0xFFFFFFFF
    call [esp]
    mov [esi],eax
    mov [esi+0x04],ebx
    mov [esi+0x08],ecx
    mov [esi+0x0C],edx
    mov eax,0x80000003
    push 0xFFFFFFFF
    call [esp]
    mov [esi+0x10],eax
    mov [esi+0x14],ebx
    mov [esi+0x18],ecx
    mov [esi+0x1C],edx
    mov eax,0x80000004
    push 0xFFFFFFFF
    call [esp]
    mov [esi+0x20],eax
    mov [esi+0x24],ebx
    mov [esi+0x28],ecx
    mov [esi+0x2C],edx
    mov word [esi+0x32],0x0D0A
    mov ecx,0x32
    call dprint
    ; End
    hlt

; Setup PTE
; Input:
; edi: PTE Base
; eax: Page Base /w Permission
; edx: Page Size
; ecx will be cleared to zero
init_pxe:
    mov ecx,1024
.setup_pxe_loop:
    stosd
    add eax,edx
    loop .setup_pxe_loop
    ret

; Input:
; esi: source text
; edi: VGA Buffer
; ecx: text length
dprint:
    push edx
    mov ax,0x0700
    cld
.transmit_loop:
.check_loop:
    mov dx,0x3f8+5
    in al,dx
    test al,0x20
    jz .check_loop
    lodsb
    mov dx,0x3F8
    out dx,al
    stosw
    loop .transmit_loop
    pop edx
    ret

gp_fault_handler:
    mov esi,gp_text
    mov ecx,dword [gp_text.len]
    call dprint
    pop eax
    hlt

IDT:
    dw 0x7FF
    dd 0xC000

gp_text:
    db "#GP is intercepted!",13,10
.len:
    dd .len-gp_text

times 510-($-$$) db 0
dw 0xAA55

times 1440*1024-($-$$) db 0

编译出来发现,512个字节空间里只剩20个字节了,有点极限了。。。

编译与运行

NASM编译本demo:

nasm test.asm -o test.img

QEMU

qemu-system-x86_64 -drive format=raw,file=test.img -serial stdio

由于是模拟执行,不论什么CPU,你能同时在屏幕上和串口上看到QEMU Virtual CPU version 2.5+

你可以让QEMU通过硬件虚拟化技术运行demo,这样就能体现出CPU之间的差异了。在Linux上追加-accel kvm -cpu host,在Windows上追加-accel whpx,在Mac上追加-accel hvf
在Intel的CPU上,你应该能看到CPU的型号,而在AMD的CPU上,你应该会看到#GP is intercepted!

VMware

要用VMware运行这个demo,需要将这个demo的映像添加为虚拟机软盘,并设置使用BIOS作为固件,启动虚拟机。
在Intel的CPU上,你应该能看到CPU的型号,而在AMD的CPU上,你应该会看到#GP is intercepted!
如果想看串口输出,需要添加一个串口设备,令其通过命名管道进行通信。选择“This end is the server”,“The other end is an application”,然后勾选“Yield CPU on poll”。
用PuTTY通过命名管道与虚拟机串口连接即可。

Hyper-V

与VMware同理,注意这里必须使用一代的Hyper-V虚拟机。否则无法使用传统BIOS。

实机

要用实机运行这个demo,可以将这个映像写进一个U盘里,然后通过U盘运行。

结论

Intel和AMD对于跨界的指令有不同的规则。至于其他x86厂商(如国产x86的兆芯和海光)对此的规则则未知。

回复

使用道具 举报

发表于 4 天前 | 显示全部楼层
前排
回复

使用道具 举报

发表于 3 天前 | 显示全部楼层
这下子,弹幕流是真的看不懂了哈哈哈
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2025-2-23 01:42 , Processed in 0.033474 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表