AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79552514
Accepted
pts
pts
Asked: 2025-04-03 17:23:44 +0800 CST2025-04-03 17:23:44 +0800 CST 2025-04-03 17:23:44 +0800 CST

如何在不修改FLAGS或regs的情况下检测我的x86代码是在16位模式还是32位模式下运行?

  • 772

我的预编译 x86 代码可能运行在 16 位(实模式或 16 位保护模式)或 32 位(i386 保护模式)下。如何在运行时从代码中检测它?

我能找到这个 NASM 源代码:

bits 16
cpu 386

pushf
test ax, strict word 0  ; In 32-bit mode this is `test eax, ...', +2 bytes.
jmp short found_16
; Fall through to found_32.

found_32:
bits 32
popf
int 32  ; Or whatever code.

found_16:
bits 16
popf
int 16  ; Or whatever code.

但是我不喜欢它,因为它使用了堆栈。有没有一种解决方案,既不修改任何通用寄存器、段寄存器或标志,也不使用堆栈,而且可以在 8086(仅 16 位模式)和 386(两种模式)上运行?

我已尝试lea esi, [dword esi+0]过 32 位模式,但在 16 位模式下则转换为非 nop。


请注意,我知道对于大多数程序来说,模式是在编译时决定的(作为架构和平台的一部分),并且它们不必能够在运行时检测模式。对于正常启动的程序,操作系统也会根据文件头选择正确的模式,因此几乎没有意外以错误模式运行完整程序文件的危险。但是,某些程序片段(例如漏洞利用 shellcode)可以从各种运行时检测中受益(包括架构和操作系统)。我还想到了一些其他不太明显的用例。

assembly
  • 3 3 个回答
  • 122 Views

3 个回答

  • Voted
  1. Jester
    2025-04-03T19:33:43+08:002025-04-03T19:33:43+08:00

    如果您对临时更改感到满意,esi则可以撤消类似的操作:

        bits 16
        lea si, [si + 0xa6]
        jmp short found16
        nop
    found32:
        bits 32
        lea esi, [esi + 0x6ff21500]
        int 32
        nop dword [eax + 1] ; padding for better disasm
    found16:
        bits 16
        lea si, [si - 0xa6]
        int 16
    
    

    在 16 位模式下解码为:

    00000000  8DB4A600          lea si,[si+0xa6]
    00000004  EB0D              jmp short 0x13
    00000006  90                nop
    00000007  8DB60015          lea si,[bp+0x1500]
    0000000B  F26F              repne outsw
    0000000D  CD20              int 0x20
    0000000F  0F1F4001          nop word [bx+si+0x1]
    00000013  8DB45AFF          lea si,[si-0xa6]
    00000017  CD10              int 0x10
    
    

    在 32 位中:

    00000000  8DB4A600EB0D90    lea esi,[esi-0x6ff21500]
    00000007  8DB60015F26F      lea esi,[esi+0x6ff21500]
    0000000D  CD20              int 0x20
    
    
    • 7
  2. Best Answer
    Nate Eldredge
    2025-04-03T23:24:38+08:002025-04-03T23:24:38+08:00

    我意识到我可以改进我以前的解决方案。

    JMP NEAR,操作码 0xE9在 16 位模式下采用两字节 16 位立即位移,在 32 位模式下采用四字节 32 位位移。此外,此位移相对于下一条指令的开始。因此,如果 32 位位移的高 16 位为零,则意味着 16 位模式下的跳转目标比 32 位模式下的跳转目标低两个字节。这刚好足够短距离跳转到真正的 16 位目标。

    NASM 示例:

            bits 16
            jmp near found_16
            dw 0x0
    found_16:
            bits 16
            jmp short main_16       ; must be exactly 2 bytes
    found_32:       
            bits 32
            ;; up to 127 total bytes of code can go here
            ;; jump elsewhere if you need more space
            int 32
            hlt
    main_16:
            bits 16
            ;; unlimited space here
            int 16
            hlt
    

    输出ndisasm -b16 foo.bin:

    00000000  E90200            jmp 0x5
    00000003  0000              add [bx+si],al  ; not executed
    00000005  EB03              jmp short 0xa
    00000007  CD20              int 0x20        ; not executed
    00000009  F4                hlt             ; not executed
    0000000A  CD10              int 0x10
    0000000C  F4                hlt
    

    输出ndisasm -b32 foo.bin:

    00000000  E902000000        jmp 0x7
    00000005  EB03              jmp short 0xa   ; not executed
    00000007  CD20              int 0x20
    00000009  F4                hlt
    0000000A  CD10              int 0x10        ; not executed
    0000000C  F4                hlt             ; not executed
    

    我之前的解决方案(仅供参考)是将其用作0x0001位移的高 16 位,这样在 32 位模式下,跳转目标就位于 64K+2 字节之外。这需要至少有 64K+ 的可用代码空间。

        bits 16
        jmp near do_16
    next_insn_16:   
        dw 0x1
    next_insn_32:   
    do_16:
        int 16
        ;; The space between next_insn_32 and do_32
        ;; should equal 0x10000 + (do_16 - next_insn_16)
        db (0x10000 + (do_16 - next_insn_16) - ($ - next_insn_32)) dup 0x90
    do_32:
        bits 32
        int 32
    

    输出ndisasm -b16 foo.bin:

    00000000  E90200            jmp 0x5
    00000003  0100              add [bx+si],ax
    00000005  CD10              int 0x10
    00000007  90                nop
    ; ...
    

    输出ndisasm -b32 foo.bin:

    00000000  E902000100        jmp 0x10007
    00000005  CD10              int 0x10
    00000007  90                nop
    ; ...
    00010006  90                nop
    00010007  CD20              int 0x20
    
    • 6
  3. Peter Cordes
    2025-04-03T21:41:48+08:002025-04-03T21:41:48+08:00

    想要这样做并希望保留包括 FLAGS 在内的原始架构状态是非常不寻常的。test我会使用您的指令长度差异检测方法。或者,mov reg, imm16/32如果您想保留 FLAGS,但可以破坏您选择的寄存器。

    如果您的 CPU 支持长 NOP(0F 1F modrm),则可以使用它代替test eax, imm32/imm16操作码,以避免影响 FLAGS。至少从 P6(Pentium Pro / Pentium II)开始就存在长 NOP 支持,但可能不存在于 P5 Pentium 或更早版本中;如果您关心与复古硬件的兼容性,则应仔细检查或避免这种情况并使用 Jester 的答案。

    bits 32
    nop dword [eax + 0x02EB0000]   ; 02 is the branch displacement for 16-bit mode
    found32:
     int 32
    found16:
     int 16
    

    以16位模式解码:

    $ ndisasm -b16 polyglot
    00000000  0F1F800000        nop word [bx+si+0x0]
    00000005  EB02              jmp short 0x9
    00000007  CD20              int 0x20
    00000009  CD10              int 0x10
    

    以32位模式解码:

    $ ndisasm -b32 polyglot
    00000000  0F1F800000EB02    nop dword [eax+0x2eb0000]
    00000007  CD20              int 0x20
    00000009  CD10              int 0x10
    

    与汇编程序计算出的分支位移相同的代码

    nop因此您可以在/jmp short和标签之间放置最多 127 个字节found16:。

    bits 32
    ; nop dword [eax + 0x02EB0000]
     db 0x0f, 0x1f, 0x80, 0, 0  ; nop word/dword opcode, ModRM, and 2 bytes of displacement
     db 0xEB  ; jmp short opcode
     db found16 - ($+1)   ; relative to the end of the insn, but $ is the start of the line
    found32:
     bits 32
     int 32
    found16:
     bits 16
     int 16
    

    半相关:https ://codegolf.stackexchange.com/questions/139243/determine-your-languages-version/139717#139717 -
    11 个字节的机器代码,设置 AL = 16,32或64(并破坏 FLAGS、CX 和 R8B)。

    • 3

相关问题

  • x86 - 通过 RETF 从 32 位切换到 64 位

  • 小 ESP32-C3(基于 RISC-V)的浮点汇编指令上无法识别的操作码

  • 如何使用GNU Assembler (GAS)从相应的.s文件创建手写的ELF文件

  • 我无法读取gameboy的LY寄存器

  • 获取Assembly中的参数值

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve