unicorn-engine / unicorn

Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
http://www.unicorn-engine.org
GNU General Public License v2.0
7.33k stars 1.31k forks source link

[Possible Bug] Fetch rip-based address failure with UC_ERR_FETCH_UNMAPPED #1906

Open TommyJerryMairo opened 7 months ago

TommyJerryMairo commented 7 months ago

Describe the bug

When executing rip-based indirect call instruction, the emulated CPU attempts to fetch at an unrelated address, which is unexpected and raises a UC_ERR_FETCH_UNMAPPED error.

To Reproduce

  1. Download and save the proof-of-concept script unicorn-indircall.py to a local system
  2. Execute the script with python

Expected behaviour

The script exits succeed with the result printed

Actual output

$ python unicorn-indircall.py
>>> Tracing instruction at 0x0000000001000000, instruction size = 0x0000000000000006
>>> RIP is 0x0000000001000000
!!! Unicorn Engine Exception: Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)
!!! RIP is 0x7d894853e5894855

$

Environment

Proof-of-Concept: unicorn-indircall.py

#!/usr/bin/env python

from unicorn import *
from unicorn.x86_const import *
import os

#   0:   ff 15 3e 00 00 00       call   QWORD PTR [rip+0x3e]        # 0x44
#   6:   90                      nop
#   ......
#  43:   90                      nop
#  44:   55                      push   rbp
#  45:   48 89 e5                mov    rbp, rsp
#  48:   53                      push   rbx
#  49:   48 89 7d f0             mov    QWORD PTR [rbp-0x10], rdi
#  4d:   48 8b 5d f0             mov    rbx, QWORD PTR [rbp-0x10]
#  51:   eb 04                   jmp    0x57
#  53:   48 83 c3 01             add    rbx, 0x1
#  57:   0f b6 03                movzx  eax, BYTE PTR [rbx]
#  5a:   84 c0                   test   al, al
#  5c:   75 f5                   jne    0x53
#  5e:   48 89 d8                mov    rax, rbx
#  61:   48 2b 45 f0             sub    rax, QWORD PTR [rbp-0x10]
#  65:   48 8b 5d f8             mov    rbx, QWORD PTR [rbp-0x8]
#  69:   c9                      leave
#  6a:   c3                      ret
X86_CODE64 = b'\xff\x15\x3e\x00\x00\x00'+b'\x90'*62+b'\x55\x48\x89\xe5\x53\x48\x89\x7d\xf0\x48\x8b\x5d\xf0\xeb\x04\x48\x83\xc3\x01\x0f\xb6\x03\x84\xc0\x75\xf5\x48\x89\xd8\x48\x2b\x45\xf0\x48\x8b\x5d\xf8\xc9\xc3'

ADDRESS = 0x1000000
DATA_ADDR = ADDRESS+1024*1024
STACK_ADDR = ADDRESS+2*1024*1024-16

def hook_code64(uc, address, size, user_data):
    print(f">>> Tracing instruction at {address:#0{18}x}, instruction size = {size:#0{18}x}")
    rip = uc.reg_read(UC_X86_REG_RIP)
    print(f">>> RIP is {rip:#0{18}x}");

def main():
    data = os.urandom(4096)
    mu = Uc(UC_ARCH_X86, UC_MODE_64)
    mu.mem_map(ADDRESS, 144*1024*1024)
    mu.mem_write(ADDRESS, X86_CODE64)
    mu.mem_write(DATA_ADDR, data)
    mu.reg_write(UC_X86_REG_RSP, STACK_ADDR)
    mu.reg_write(UC_X86_REG_RDI, DATA_ADDR)
    mu.hook_add(UC_HOOK_CODE, hook_code64)
    try:
        mu.emu_start(ADDRESS,ADDRESS+8)
        print(f"Result={mu.reg_read(UC_X86_REG_RAX):#0{18}x}")
    except UcError as err:
        print(f"!!! Unicorn Engine Exception: {err}")
        print(f"!!! RIP is {mu.reg_read(UC_X86_REG_RIP):#0{18}x}")

if __name__ == '__main__':
    main()