-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhooker.go
175 lines (151 loc) · 3.83 KB
/
hooker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package emulator
import (
"fmt"
"bytes"
bin "encoding/binary"
zl "github.com/rs/zerolog"
uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
ks "github.com/keystone-engine/keystone/bindings/go/keystone"
)
type HookerCallback func(NativeMethodContext)error
type Hooker struct {
emu *Emulator
ks *ks.Keystone
logger zl.Logger
size uint64
currId uint64
hookMagic uint64
hookStart uint64
hookCurrent uint64
hooks map[uint64]HookerCallback
}
func NewHooker(emu *Emulator, logger zl.Logger, base, size uint64) *Hooker {
hk := &Hooker{
emu: emu,
logger: logger,
size: size,
currId: 0xFF00,
hooks: map[uint64]HookerCallback{},
}
ks, err := ks.New(ks.ARCH_ARM, ks.MODE_THUMB)
if err != nil {
hk.logger.Debug().Err(err).Msg("keystone init failed")
return nil
}
hk.logger.Debug().Msg("keystone ok")
hk.ks = ks
hk.hookMagic = base
hk.hookStart = base + 4
hk.hookCurrent = hk.hookStart
hk.emu.Mu.HookAdd(uc.HOOK_CODE, hk.hook, hk.hookStart, hk.hookStart + size)
return hk
}
func (hk *Hooker) getNextId() uint64 {
curId := hk.currId
hk.currId = hk.currId + 1
return curId
}
func (hk *Hooker) writeFunction(f HookerCallback) (uint64, error) {
hookId := hk.getNextId()
hookAddr := hk.hookCurrent
addrHex := fmt.Sprintf("0x%x", hookId)
asm := "PUSH {R4,LR}\n" +
"MOV R4, #" + addrHex + "\n" +
"IT AL\n" +
"POP {R4,PC}"
// fmt.Println(asm)
code, v, ok := hk.ks.Assemble(asm, 0)
if !ok {
hk.logger.Debug().
Str("hAddr", addrHex).
Msgf("keystone cannot write hook function %+v", v)
return 0, ErrAsmFailed
}
if v != 4 {
hk.logger.Debug().
Str("hAddr", addrHex).
Int("len", len(code)).
Bytes("code", code).
Err(ErrUnexpectedAsmLength).
Msgf("invalid asm length %+#v", v)
return 0, ErrUnexpectedAsmLength
}
hk.logger.Debug().
Str("hAddr", addrHex).
Msg("write_function")
hk.hookCurrent = hk.hookCurrent + uint64(len(code))
hk.hooks[hookId] = f
return hookAddr, nil
}
func maxFFunc(m map[uint64]HookerCallback) (r uint64) {
for k, _ := range m {
if k > r {
r = k
}
}
return r
}
func (hk *Hooker) WriteFunctionTable(table map[uint64]HookerCallback) (uint64, uint64) {
var (
index uint64
address uint64
err error
)
// First, we write every function and store its result address.
indexMax := maxFFunc(table)
hookMap := map[uint64]uint64{}
for k, vf := range table {
address, err = hk.writeFunction(vf)
if err != nil {
continue
}
hookMap[k] = address
}
// Then we write the function table.
tableBytes := []byte{}
tableAddr := hk.hookCurrent
for index = 0; index < indexMax; index++ {
if o, exist := hookMap[index]; exist {
address = o
}else{
address = 0
}
by := make([]byte, 4)
bin.LittleEndian.PutUint32(by,uint32(address + 1)) // explicit
tableBytes = append(tableBytes, by...)
}
hk.emu.Mu.MemWrite(tableAddr, tableBytes)
hk.hookCurrent = hk.hookCurrent + uint64(len(tableBytes))
// Then we write the a pointer to the table.
ptrAddr := hk.hookCurrent
by := make([]byte, 4)
bin.LittleEndian.PutUint32(by, uint32(tableAddr))
hk.emu.Mu.MemWrite(ptrAddr, by)
hk.hookCurrent = hk.hookCurrent + 4
return ptrAddr, tableAddr
}
func (hk *Hooker) hook(mu uc.Unicorn, addr uint64, size uint32) {
code, err := hk.emu.Mu.MemRead(addr, uint64(size))
if err != nil {
//hk.logger.Debug().Err(err).Msg("hook memread failed")
return
}
// "IT AL"
cmp := []byte{0xE8, 0xBF}
if size != 2 || bytes.Compare(code, cmp) != 0 {
//hk.logger.Debug().Err(ErrUnexpectedAsmLength).Msg("not 'IT AL' instruction")
return
}
// Find hook.
hookId, err := hk.emu.Mu.RegRead(uc.ARM_REG_R4)
if err != nil {
return
}
hookFunc := hk.hooks[hookId]
err = hookFunc(NativeMethodContext{hk.emu, mu})
if err != nil {
hk.logger.Info().Err(err).Msg("hook function callback error, stopping emulation")
mu.Stop()
return
}
}