Skip to content

Instantly share code, notes, and snippets.

@williballenthin
Last active December 27, 2019 01:59
Show Gist options
  • Save williballenthin/e7b1ec10efd2de19c46a to your computer and use it in GitHub Desktop.
Save williballenthin/e7b1ec10efd2de19c46a to your computer and use it in GitHub Desktop.
Go program that demonstrates unexpected behavior in the Unicorn engine
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)
}
}
#!/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())
@williballenthin
Copy link
Author

Go observed output:

[willi@hostname step_test]$ go build && ./step_test 
ip: 0x1000
hit code: 0x1000  stop!       <---------
hit code: 0x1007  stop!       <---------    why is the callback hit twice?
deleted hook
ip: 0x1009   <-------------------
hit code: 0x1009  stop!         |
hit code: 0x1010  stop!         |
deleted hook                    |
ip: 0x1009   <-------------------   why is IP not changed?

@williballenthin
Copy link
Author

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

@williballenthin
Copy link
Author

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

@williballenthin
Copy link
Author

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