| 31 | } |
| 32 | |
| 33 | export function testControlFlowGraph(input: PointerInput, maxBlocks = 24, maxInstructions = 6): void { |
| 34 | try { |
| 35 | if (typeof ControlFlowGraph === "undefined") { |
| 36 | log(`[cfg] ControlFlowGraph is not available in Frida ${Frida.version}`, "red"); |
| 37 | return; |
| 38 | } |
| 39 | |
| 40 | const address = toPointer(input); |
| 41 | const graph = new ControlFlowGraph(address); |
| 42 | const range = Process.findFunctionRange(address); |
| 43 | const containing = graph.findBlockContaining(address); |
| 44 | const blockLimit = normalizeLimit(maxBlocks, 24); |
| 45 | const instructionLimit = normalizeLimit(maxInstructions, 6); |
| 46 | const ids = new Map<BasicBlock, number>(); |
| 47 | |
| 48 | graph.blocks.forEach((block, index) => ids.set(block, index)); |
| 49 | |
| 50 | log("[cfg] ------------------------------------------------------------", "cyan"); |
| 51 | log(`[cfg] entrypoint=${graph.entrypoint} entryBlock=#${ids.get(graph.entryBlock) ?? "?"} blocks=${graph.blocks.length}`, "green"); |
| 52 | if (range !== null) { |
| 53 | log(`[cfg] function=${range.base}-${range.base.add(range.size)} size=${range.size}`, "green"); |
| 54 | } |
| 55 | if (containing !== null) { |
| 56 | log(`[cfg] containing=#${ids.get(containing) ?? "?"} ${containing.start}-${containing.end}`, "yellow"); |
| 57 | } |
| 58 | |
| 59 | for (const block of graph.blocks.slice(0, blockLimit)) { |
| 60 | logBlock(block, ids, instructionLimit); |
| 61 | } |
| 62 | |
| 63 | if (graph.blocks.length > blockLimit) { |
| 64 | log(`[cfg] ... ${graph.blocks.length - blockLimit} blocks omitted ...`, "yellow"); |
| 65 | } |
| 66 | } catch (error) { |
| 67 | log(`[cfg] failed: ${String(error)}`, "red"); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | function logBlock(block: BasicBlock, ids: Map<BasicBlock, number>, instructionLimit: number): void { |
| 72 | const id = ids.get(block) ?? -1; |