@zsa233/frida-analykit-agent 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/package.json +41 -0
- package/src/api/android.ts +80 -0
- package/src/bridges.ts +18 -0
- package/src/cmodule/scan_adrp.c +81 -0
- package/src/cmodule/scan_adrp.ts +118 -0
- package/src/config.ts +56 -0
- package/src/consts.ts +31 -0
- package/src/elf/insn.ts +61 -0
- package/src/elf/module.ts +751 -0
- package/src/elf/struct.ts +271 -0
- package/src/elf/tools.ts +33 -0
- package/src/elf/verifier.ts +74 -0
- package/src/elf/xref.ts +360 -0
- package/src/func.ts +32 -0
- package/src/helper.ts +685 -0
- package/src/index.ts +10 -0
- package/src/jni/env.ts +1439 -0
- package/src/jni/struct.ts +217 -0
- package/src/lib/libc.ts +161 -0
- package/src/lib/libssl.ts +95 -0
- package/src/message.ts +26 -0
- package/src/net/ssl.ts +360 -0
- package/src/net/struct.ts +51 -0
- package/src/net/tools.ts +0 -0
- package/src/process.ts +137 -0
- package/src/rpc.ts +268 -0
- package/src/runtime-globals.d.ts +11 -0
- package/src/utils/array_pointer.ts +102 -0
- package/src/utils/queue.ts +102 -0
- package/src/utils/scan.ts +103 -0
- package/src/utils/std.ts +165 -0
- package/src/utils/text_endec.ts +35 -0
- package/src/utils/utils.ts +111 -0
package/src/elf/xref.ts
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { help, MemoryPage, NativePointerObject } from "../helper.js"
|
|
4
|
+
import { ScanAdrpCMod } from "../cmodule/scan_adrp.js"
|
|
5
|
+
import { setGlobalProperties } from "../config.js";
|
|
6
|
+
import { InstructionSequence } from "./insn.js";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function countLeadingSignBits(input: number | bigint, width: number): number {
|
|
11
|
+
if(width < 1 || width > 64) {
|
|
12
|
+
throw new RangeError('width must be between 1 and 64');
|
|
13
|
+
}
|
|
14
|
+
const mask = (1n << BigInt(width)) - 1n
|
|
15
|
+
let value = BigInt(input) & mask
|
|
16
|
+
|
|
17
|
+
const signBit = (value >> BigInt(width - 1)) & 1n
|
|
18
|
+
let count = 0
|
|
19
|
+
for(let i = width - 1; i >= 0; i--) {
|
|
20
|
+
const bit = (value >> BigInt(i)) & 1n
|
|
21
|
+
if(bit === signBit) {
|
|
22
|
+
count ++
|
|
23
|
+
}else{
|
|
24
|
+
break
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return count
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
function choiceMatchPattern(p: NativePointer, byteNum: number): string {
|
|
32
|
+
return p.toMatchPattern().split(/\s+/).slice(0, byteNum).join(' ')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
function stripLeadingZeros(hexDump: string): { count: number, stripped: string}{
|
|
37
|
+
const parts = hexDump.trim().split(/\s+/)
|
|
38
|
+
let count = 0
|
|
39
|
+
for(const part of parts) {
|
|
40
|
+
if(part.toLowerCase() === '00') {
|
|
41
|
+
count++
|
|
42
|
+
}else{
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const strippedParts = parts.slice(count)
|
|
47
|
+
return {
|
|
48
|
+
count,
|
|
49
|
+
stripped: strippedParts.join(' ')
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
export class AdrlXref {
|
|
57
|
+
/* adr/adrp Xi, #{imm1}
|
|
58
|
+
------------------------------------------------------------------
|
|
59
|
+
| 31 | 30 ─ 29 | 28 27 26 25 24 | 23 ──────────────── 5 | 4 ─ 0 |
|
|
60
|
+
| op | immlo | 1 0 0 0 0 | immhi (19b) | Rd |
|
|
61
|
+
------------------------------------------------------------------
|
|
62
|
+
adr : op=0
|
|
63
|
+
adrp: op=1
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/* add Xm, Xn, #{imm2}
|
|
67
|
+
----------------------------------------------------------------------
|
|
68
|
+
| 31 | 30 | 29 | 28 27 26 25 24 | 23 ─ 22 | 21 ─ 10 | 9 ─ 5 | 4 ─ 0 |
|
|
69
|
+
| sf | op | S | 1 0 0 0 1 | sh | imm12 | Rn | Rd |
|
|
70
|
+
----------------------------------------------------------------------
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
static readonly ADRP_FIXED28_24_BITSET_MASK = ptr(0b10000).shl(24)
|
|
74
|
+
|
|
75
|
+
target: NativePointer
|
|
76
|
+
|
|
77
|
+
constructor(target: NativePointer) {
|
|
78
|
+
this.target = target
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get targetPage(): NativePointer{
|
|
82
|
+
return this.target.and(ptr('0xfff').not())
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get targetPageOffset(): NativePointer {
|
|
86
|
+
return this.target.and('0xfff')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
scanAdrl(
|
|
91
|
+
scanRange: { base: NativePointer, size: number },
|
|
92
|
+
maxGapToAdd: number = 16,
|
|
93
|
+
): Adrl[] {
|
|
94
|
+
const { base, size } = scanRange
|
|
95
|
+
|
|
96
|
+
const targetPage = this.targetPage
|
|
97
|
+
// adrp
|
|
98
|
+
const op = ptr(0b1)
|
|
99
|
+
|
|
100
|
+
const adrpMatches: NativePointer[] = []
|
|
101
|
+
|
|
102
|
+
const pageMask = ptr(Process.pageSize - 1).not()
|
|
103
|
+
const adrpImmLenMask = ptr(0x1FFFFF)
|
|
104
|
+
|
|
105
|
+
type ScanTargetRange = {
|
|
106
|
+
base: NativePointer
|
|
107
|
+
size: number
|
|
108
|
+
high: boolean
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
help.memoryReadDo(base, size, (makeReadable, makeRecovery): void => {
|
|
112
|
+
makeReadable()
|
|
113
|
+
const ranges: ScanTargetRange[] = []
|
|
114
|
+
if (this.target <= base) {
|
|
115
|
+
ranges.push({ base, size, high: true })
|
|
116
|
+
} else if (this.target >= base.add(size)) {
|
|
117
|
+
ranges.push({ base, size, high: false })
|
|
118
|
+
} else {
|
|
119
|
+
ranges.push({ base, size: Number(this.target.sub(base)), high: false })
|
|
120
|
+
ranges.push({ base: this.target, size: size - Number(this.target.sub(base)), high: true })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (let range of ranges) {
|
|
124
|
+
const minPage = range.base.and(pageMask)
|
|
125
|
+
const maxPage = range.base.add(range.size).and(pageMask)
|
|
126
|
+
|
|
127
|
+
const maxPageDelta = targetPage.sub(minPage).shr(12).and(adrpImmLenMask)
|
|
128
|
+
const minPageDelta = targetPage.sub(maxPage).shr(12).and(adrpImmLenMask)
|
|
129
|
+
|
|
130
|
+
let immVal: NativePointer
|
|
131
|
+
let leadingCount: number
|
|
132
|
+
if(range.high) {
|
|
133
|
+
leadingCount = countLeadingSignBits(BigInt(minPageDelta.toString()), 21)
|
|
134
|
+
immVal = adrpImmLenMask
|
|
135
|
+
}else{
|
|
136
|
+
leadingCount = countLeadingSignBits(BigInt(maxPageDelta.toString()), 21)
|
|
137
|
+
immVal = NULL
|
|
138
|
+
}
|
|
139
|
+
const immMask: NativePointer = ptr((((1n << BigInt(leadingCount)) - 1n) << BigInt(21 - leadingCount)).toString())
|
|
140
|
+
|
|
141
|
+
const B = ptr(1)
|
|
142
|
+
const immloMask = immMask.and(0b11)
|
|
143
|
+
const immhiMask = immMask.shr(2)
|
|
144
|
+
const anyRdMask = NULL
|
|
145
|
+
const adrpMask = choiceMatchPattern(
|
|
146
|
+
NULL.or(B.shl(31))
|
|
147
|
+
.or(immloMask.shl(29))
|
|
148
|
+
.or(AdrlXref.ADRP_FIXED28_24_BITSET_MASK)
|
|
149
|
+
.or(immhiMask.shl(5))
|
|
150
|
+
.or(anyRdMask),
|
|
151
|
+
4,
|
|
152
|
+
)
|
|
153
|
+
const { stripped: adrpMaskStripped, count: alignOffset } = stripLeadingZeros(adrpMask)
|
|
154
|
+
|
|
155
|
+
const immlo = immVal.and(0b11)
|
|
156
|
+
const immhi = immVal.shr(2)
|
|
157
|
+
const anyRd = NULL
|
|
158
|
+
const adrpSign = choiceMatchPattern(
|
|
159
|
+
NULL.or(op.shl(31))
|
|
160
|
+
.or(immlo.shl(29))
|
|
161
|
+
.or(AdrlXref.ADRP_FIXED28_24_BITSET_MASK)
|
|
162
|
+
.or(immhi.shl(5))
|
|
163
|
+
.or(anyRd),
|
|
164
|
+
4,
|
|
165
|
+
)
|
|
166
|
+
const adrpSignStripped = adrpSign.split(/\s+/).slice(alignOffset).join(' ')
|
|
167
|
+
|
|
168
|
+
const scanPattern = `${adrpSignStripped} : ${adrpMaskStripped}`
|
|
169
|
+
|
|
170
|
+
const scanRes = ScanAdrpCMod.scan(
|
|
171
|
+
range, scanPattern, this.target, alignOffset,
|
|
172
|
+
)
|
|
173
|
+
if(scanRes.length) {
|
|
174
|
+
adrpMatches.push(...scanRes)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
makeRecovery()
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const adrpInstructions = adrpMatches.reduce<Adrl[]>((acc, v) => {
|
|
182
|
+
const adrl = this.verify(v, maxGapToAdd)
|
|
183
|
+
if (adrl) {
|
|
184
|
+
acc.push(adrl)
|
|
185
|
+
}
|
|
186
|
+
return acc
|
|
187
|
+
}, [])
|
|
188
|
+
return adrpInstructions
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// adrp + add
|
|
192
|
+
scanAdrlSlow(
|
|
193
|
+
scanRange: { base: NativePointer, size: number},
|
|
194
|
+
maxGapToAdd: number = 16,
|
|
195
|
+
): Adrl[] {
|
|
196
|
+
const { base, size } = scanRange
|
|
197
|
+
|
|
198
|
+
const targetPage = this.targetPage
|
|
199
|
+
// adrp
|
|
200
|
+
const op = ptr(0b1)
|
|
201
|
+
|
|
202
|
+
const adrpPageMask = choiceMatchPattern(ptr(0x9fffffe0), 4)
|
|
203
|
+
|
|
204
|
+
const adrpMatches: MemoryScanMatch[] = []
|
|
205
|
+
|
|
206
|
+
help.memoryReadPageDo(base, size, (page: MemoryPage): boolean => {
|
|
207
|
+
const { readable } = page
|
|
208
|
+
const range = { base: page.base, size: page.size}
|
|
209
|
+
if(!range) {
|
|
210
|
+
return false
|
|
211
|
+
}else if (!readable) {
|
|
212
|
+
console.error(`[AdrlXref] scanAdrp base[${range.base} => ${range.base.add(range.size)}] is unreadable.`)
|
|
213
|
+
return false
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const pcPage = range.base
|
|
217
|
+
const pageDelta = targetPage.sub(pcPage).shr(3*4).and(0x1FFFFF) // immhi:immlo = 21位
|
|
218
|
+
const immlo = pageDelta.and(0b11)
|
|
219
|
+
const immhi = pageDelta.shr(2)
|
|
220
|
+
const anyRd = NULL
|
|
221
|
+
const adrpSign = choiceMatchPattern(
|
|
222
|
+
NULL.or(op.shl(31))
|
|
223
|
+
.or(immlo.shl(29))
|
|
224
|
+
.or(AdrlXref.ADRP_FIXED28_24_BITSET_MASK)
|
|
225
|
+
.or(immhi.shl(5))
|
|
226
|
+
.or(anyRd),
|
|
227
|
+
4,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
const scanPattern = `${adrpSign} : ${adrpPageMask}`
|
|
231
|
+
const scanResults = Memory.scanSync(range.base, range.size, scanPattern).filter(v => {
|
|
232
|
+
return v.address.and(0x3).isNull()
|
|
233
|
+
})
|
|
234
|
+
adrpMatches.push(...scanResults)
|
|
235
|
+
return false
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
const adrpInstructions = adrpMatches.reduce<Adrl[]>((acc, v) => {
|
|
239
|
+
const adrl = this.verify(v.address, maxGapToAdd)
|
|
240
|
+
if(adrl) {
|
|
241
|
+
acc.push(adrl)
|
|
242
|
+
}
|
|
243
|
+
return acc
|
|
244
|
+
}, [])
|
|
245
|
+
|
|
246
|
+
return adrpInstructions
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
verify(p: NativePointer, maxGapToAdd: number = 16): Adrl | null {
|
|
251
|
+
try {
|
|
252
|
+
const maybeAdrl = Adrl.loadFromPointer(p)
|
|
253
|
+
const adrpInsn = maybeAdrl.adrpInsn
|
|
254
|
+
if (adrpInsn.mnemonic !== 'adrp') {
|
|
255
|
+
return null
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (maybeAdrl.getTarget(false, maxGapToAdd)?.equals(this.target)) {
|
|
259
|
+
return maybeAdrl
|
|
260
|
+
}
|
|
261
|
+
return null
|
|
262
|
+
} catch (error) {
|
|
263
|
+
|
|
264
|
+
}
|
|
265
|
+
return null
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
scanAdr(): Adrl[] {
|
|
270
|
+
// TODO:
|
|
271
|
+
return []
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
type JumpScanRes = {
|
|
278
|
+
src: InstructionSequence
|
|
279
|
+
insn: Arm64Instruction
|
|
280
|
+
next: NativePointer
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
export class Adrl extends InstructionSequence {
|
|
286
|
+
readonly adrpInsn: Arm64Instruction
|
|
287
|
+
private addInsn?: Arm64Instruction
|
|
288
|
+
|
|
289
|
+
constructor(adrp: Arm64Instruction, add?: Arm64Instruction) {
|
|
290
|
+
super(adrp)
|
|
291
|
+
this.adrpInsn = adrp
|
|
292
|
+
this.addInsn = add
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
get instruction(): Arm64Instruction {
|
|
296
|
+
return Instruction.parse(this.$handle) as Arm64Instruction
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
scanBL(base?: NativePointer, maxGap: number = 16): JumpScanRes | null {
|
|
300
|
+
if(!base) {
|
|
301
|
+
base = this.$handle
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let inc = 0
|
|
305
|
+
for (const insn of this) {
|
|
306
|
+
switch (insn.mnemonic) {
|
|
307
|
+
case 'bl':
|
|
308
|
+
return {
|
|
309
|
+
src: this,
|
|
310
|
+
insn: insn,
|
|
311
|
+
next: insn.next,
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
inc++
|
|
315
|
+
if(inc >= maxGap) return null
|
|
316
|
+
}
|
|
317
|
+
return null
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
getTarget(mustFoundAdd: boolean = false, maxGapToAdd: number = 16): NativePointer | null {
|
|
321
|
+
const [op1, op2] = this.adrpInsn.operands
|
|
322
|
+
const target = ptr(op2.value.toString())
|
|
323
|
+
if (!this.addInsn) {
|
|
324
|
+
let inc = 0
|
|
325
|
+
verifyLoop: for (const insn of this) {
|
|
326
|
+
switch (insn.mnemonic) {
|
|
327
|
+
case 'add':
|
|
328
|
+
if (insn.operands.length !== 3) {
|
|
329
|
+
break
|
|
330
|
+
}
|
|
331
|
+
const [addOp1, addOp2, addOp3] = insn.operands
|
|
332
|
+
if (addOp1.value !== op1.value) {
|
|
333
|
+
break
|
|
334
|
+
}
|
|
335
|
+
if (addOp2.value !== addOp1.value) {
|
|
336
|
+
console.error(`[Adrl] findAdd [${insn}] op2,来源于另一个寄存器,无法预估目标地址。`)
|
|
337
|
+
break
|
|
338
|
+
}
|
|
339
|
+
this.addInsn = insn
|
|
340
|
+
break verifyLoop
|
|
341
|
+
}
|
|
342
|
+
inc++
|
|
343
|
+
if (inc >= maxGapToAdd) return mustFoundAdd ? null : target
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
}
|
|
347
|
+
if(!this.addInsn) {
|
|
348
|
+
return target
|
|
349
|
+
}
|
|
350
|
+
return target.add(this.addInsn.operands[2].value.toString())
|
|
351
|
+
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
setGlobalProperties({
|
|
358
|
+
AdrlXref,
|
|
359
|
+
|
|
360
|
+
})
|
package/src/func.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
import { nativeFunctionOptions } from "./consts.js"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export class FuncHelper {
|
|
6
|
+
static list: any[] = []
|
|
7
|
+
|
|
8
|
+
static $nativeWrapper<RetType extends NativeFunctionReturnType>(
|
|
9
|
+
retType: RetType, argTypes: any[],
|
|
10
|
+
) {
|
|
11
|
+
return function (f: NativePointer, wrapFunc: AnyFunction) {
|
|
12
|
+
const impl = f.isNull() ? NULL : new NativeFunction(f, retType, argTypes, nativeFunctionOptions)
|
|
13
|
+
const wrapper = function () {
|
|
14
|
+
return wrapFunc(impl, ...Array.from(arguments))
|
|
15
|
+
}
|
|
16
|
+
const cb = new NativeCallback(wrapper, retType, argTypes)
|
|
17
|
+
FuncHelper.list.push(cb)
|
|
18
|
+
return cb
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// int __cxa_atexit(void (*f)(void *), void *objptr, void *dso);
|
|
23
|
+
static atexit = this.$nativeWrapper('void', ['pointer'])
|
|
24
|
+
|
|
25
|
+
// int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
|
|
26
|
+
static pthread_start_routine = this.$nativeWrapper('pointer', ['pointer'])
|
|
27
|
+
|
|
28
|
+
// void (*keylog_callback)(const SSL *ssl, const char *line)
|
|
29
|
+
static SSL_CTX_keylog_callback = this.$nativeWrapper('void', ['pointer', 'pointer'])
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
}
|