Last active
December 27, 2019 01:59
-
-
Save williballenthin/e7b1ec10efd2de19c46a to your computer and use it in GitHub Desktop.
Go program that demonstrates unexpected behavior in the Unicorn engine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/hex" | |
"fmt" | |
uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn" | |
"strings" | |
) | |
var asm = strings.Join([]string{ | |
"48c7c003000000", // mov rax, 3 mapped: 0x1000 | |
"0f05", // syscall mapped: 0x1007 | |
"48c7c700400000", // mov rdi, 0x4000 mapped: 0x1009 | |
"488907", // mov [rdi], rdx mapped: 0x1010 | |
"488b07", // mov rdx, [rdi] mapped: 0x1013 | |
"4883c201", // add rdx, 1 mapped: 0x1016 | |
}, "") | |
func run() error { | |
code, err := hex.DecodeString(asm) | |
if err != nil { | |
return err | |
} | |
mu, err := uc.NewUnicorn(uc.ARCH_X86, uc.MODE_64) | |
if err != nil { | |
return err | |
} | |
// map and write code to memory | |
if err := mu.MemMap(0x1000, 0x1000); err != nil { | |
return err | |
} | |
if err := mu.MemWrite(0x1000, code); err != nil { | |
return err | |
} | |
// map scratch space | |
if err := mu.MemMap(0x4000, 0x1000); err != nil { | |
return err | |
} | |
// set example register | |
if err := mu.RegWrite(uc.X86_REG_RDX, 1); err != nil { | |
return err | |
} | |
step := func(mu uc.Unicorn) error { | |
// always stop after one instruction | |
h, e := mu.HookAdd(uc.HOOK_CODE, func(mu uc.Unicorn, addr uint64, size uint32) { | |
fmt.Printf("hit code: 0x%x", addr) | |
fmt.Printf(" stop!\n") | |
e := mu.Stop() | |
if e != nil { | |
panic(e) | |
} | |
}) | |
if e != nil { | |
return e | |
} | |
defer func() { | |
e := mu.HookDel(h) | |
if e != nil { | |
panic(e) | |
} | |
fmt.Printf("deleted hook\n") | |
}() | |
ip, e := mu.RegRead(uc.X86_REG_RIP) | |
if e != nil { | |
return e | |
} | |
if err := mu.Start(ip, ip+uint64(len(code))); err != nil { | |
return err | |
} | |
return nil | |
} | |
showrip := func(mu uc.Unicorn) error { | |
ip, err := mu.RegRead(uc.X86_REG_RIP) | |
if err != nil { | |
return err | |
} | |
fmt.Printf("ip: 0x%x\n", ip) | |
return nil | |
} | |
err = mu.RegWrite(uc.X86_REG_RIP, 0x1000) | |
if err != nil { | |
return err | |
} | |
showrip(mu) | |
step(mu) | |
showrip(mu) | |
step(mu) | |
showrip(mu) | |
return nil | |
} | |
func main() { | |
if err := run(); err != nil { | |
fmt.Println(err) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
from __future__ import print_function | |
from unicorn import * | |
from unicorn.x86_const import * | |
CODE = "".join([ | |
"48c7c003000000", # mov rax, 3 mapped: 0x1000 | |
"0f05", # syscall mapped: 0x1007 | |
"48c7c700400000", # mov rdi, 0x4000 mapped: 0x1009 | |
"488907", # mov [rdi], rdx mapped: 0x1010 | |
"488b07", # mov rdx, [rdi] mapped: 0x1013 | |
"4883c201", # add rdx, 1 mapped: 0x1016 | |
]).decode("hex") | |
def step(mu): | |
def stop_hook(uc, address, *args, **kwargs): | |
try: | |
print("hit code: 0x%x stop!" % (address)) | |
uc.emu_stop() | |
except UcError as e: | |
print("ERROR: %s" % e) | |
try: | |
h = mu.hook_add(UC_HOOK_CODE, stop_hook) | |
ip = mu.reg_read(UC_X86_REG_RIP) | |
mu.emu_start(ip, ip+len(CODE)) | |
mu.hook_del(h) | |
except UcError as e: | |
print("ERROR: %s" % e) | |
def showip(mu): | |
ip = mu.reg_read(UC_X86_REG_RIP) | |
print("ip: 0x%x" % (ip)) | |
def main(): | |
try: | |
mu = Uc(UC_ARCH_X86, UC_MODE_64) | |
# base of CODE | |
mu.mem_map(0x1000, 0x1000) | |
mu.mem_write(0x1000, CODE) | |
# scratch, used by CODE | |
mu.mem_map(0x4000, 0x1000) | |
mu.reg_write(UC_X86_REG_RDX, 0x1) | |
mu.reg_write(UC_X86_REG_RIP, 0x1000) | |
showip(mu) | |
step(mu) | |
showip(mu) | |
step(mu) | |
showip(mu) | |
except UcError as e: | |
print("ERROR: %s" % e) | |
if __name__ == '__main__': | |
import sys | |
sys.exit(main()) |
Go expected output:
[willi@hostname step_test]$ go build && ./step_test
ip: 0x1000
hit code: 0x1000 stop!
deleted hook
ip: 0x1007
hit code: 0x1007 stop!
deleted hook
ip: 0x1009
Python observed output:
[willi@hostname uc]$ python2 sample_x86.py
ip: 0x1000
hit code: 0x1000 stop!
hit code: 0x1007 stop!
ip: 0x1009
hit code: 0x1009 stop!
hit code: 0x1010 stop!
ip: 0x1009
Python expected output:
[willi@hostname uc]$ python2 sample_x86.py
ip: 0x1000
hit code: 0x1000 stop!
ip: 0x1007
hit code: 0x1007 stop!
ip: 0x1009
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Go observed output: