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.64k stars 1.35k forks source link

Incorect behaviour while using memory protection UC_PROT_ALL in UC_MEM_FETCH_PROT hook #1780

Open galnaktar opened 1 year ago

galnaktar commented 1 year ago

Description: Got 'access violation reading' error after setting UC_PROT_ALL permissions in UC_HOOK_MEM_FETCH_PROT hook routine. If UC_PROT_EXEC + UC_PROT_READ permissions used, everything works correct.

Environment: Win 8.1 x64 Python310 unicorn-2.0.1.post1-py2.py3-none-win_amd64.whl

source code:

from unicorn import *
from unicorn.arm_const import *

#.20000000 00F09FE5   LDR     PC, [20000008] ;=40000000
#.20000004 FEFFFFEA   b
#.20000008 00000040   #UD
#.2000000C 00000000   #UD
THUMB_CODE_0x20000000 = b"\x00\xF0\x9F\xE5\xFE\xFF\xFF\xEA\x00\x00\x00\x40\x00\x00\x00\x00"
ROM_START_0x20000000 = 0x20000000

#.40000000 00F09FE5   LDR     PC, [40000008] ;=20000000
#.40000004 FEFFFFEA   b
#.40000008 00000020   #UD
#.4000000C 00000000   #UD
THUMB_CODE_0x40000000 = b"\x00\xF0\x9F\xE5\xFE\xFF\xFF\xEA\x00\x00\x00\x20\x00\x00\x00\x00"
ROM_START_0x40000000 = 0x40000000

ROM_SIZE = 1 * 0x1000

def hook_code(uc, address, size, user_data):
    print(' pc: 0x%08X' % (uc.reg_read(UC_ARM_REG_PC),))

def hook_mem_invalid(uc, access, address, size, value, user_data):
    if access == UC_MEM_FETCH_PROT:
        print('## UC_MEM_FETCH_PROT address = %X access = %X' % (address, access))
        #uc.mem_protect(address, ROM_SIZE, UC_PROT_EXEC + UC_PROT_READ)
        uc.mem_protect(address, ROM_SIZE, UC_PROT_ALL)
        return True

uc = Uc(UC_ARCH_ARM, UC_MODE_LITTLE_ENDIAN)
uc.mem_map(ROM_START_0x20000000, ROM_SIZE, UC_PROT_EXEC + UC_PROT_READ)
uc.mem_write(ROM_START_0x20000000, THUMB_CODE_0x20000000)

uc.mem_map(ROM_START_0x40000000, ROM_SIZE, UC_PROT_EXEC + UC_PROT_READ)
uc.mem_write(ROM_START_0x40000000, THUMB_CODE_0x40000000)
uc.mem_protect(ROM_START_0x40000000, ROM_SIZE, UC_PROT_NONE)

uc.hook_add(UC_HOOK_CODE, hook_code)
uc.hook_add(UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid, begin=0x00000000, end=0xFFFFFFFF)
uc.emu_start(ROM_START_0x20000000, 0xFFFFFFFF)

Output: изображение

If code in 'hook_mem_invalid' is changed from ...UC_PROT_ALL...:

def hook_mem_invalid(uc, access, address, size, value, user_data):
    if access == UC_MEM_FETCH_PROT:
        print('## UC_MEM_FETCH_PROT address = %X access = %X' % (address, access))
        uc.mem_protect(address, ROM_SIZE, UC_PROT_ALL)
        return True

to ...UC_PROT_EXEC + UC_PROT_READ...:

def hook_mem_invalid(uc, access, address, size, value, user_data):
    if access == UC_MEM_FETCH_PROT:
        print('## UC_MEM_FETCH_PROT address = %X access = %X' % (address, access))
        uc.mem_protect(address, ROM_SIZE, UC_PROT_EXEC + UC_PROT_READ)
        return True

The output is: изображение

wtdcode commented 1 year ago

Could you illustrate what is unexpected...? I'm a bit confused here

galnaktar commented 1 year ago

It's just two regions of memory with jumps on each other - infinite loop. The first region allocated with UC_PROT_ALL access, While UC_PROT_NONE access is set for the second one.

UC_HOOK_MEM_FETCH_PROT hook installed to fix the access when the second region get the execution flow. If I use UC_PROT_EXEC + UC_PROT_READ flags if hook's callback function to fix the access for the second region everything works fine, and I've got infinite loop behaviour. изображение

But if I use UC_PROT_ALL flag used to fix the access for the second region, I've go an error. изображение

And I'm also confused, why setting UC_PROT_ALL flags produce an error, while UC_PROT_EXEC + UC_PROT_READ flags works fine.

The access violation error also looks strange, coz the second region base is 0x40000000, UC_PROT_ALL flag are set for 0x40000000 address, and execution flow from first region trasfered to 0x40000000. But I've got access violation error at 0x3FFFFFFF .....

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 15 days.

PhilippTakacs commented 1 year ago

I have tried to reproduce this with a equivalent C-test and get UC_MEM_FETCH_UNMAPPED at address 0x0. Also your provided binary doesn't disassemble to the code in the comment. See the capstone output:

$ cstool arm "\x00\xF0\x9F\xE5\xFE\xFF\xFF\xEA\x00\x00\x00\x20\x00\x00\x00\x00" 0x20000000
20000000  00 f0 9f e5  ldr  pc, [pc]
20000004  fe ff ff ea  b    #0x20000004
20000008  00 00 00 20  #UD
2000000c  00 00 00 00  #UD