@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.
@@ -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
+ }