├── README.md ├── TraceFunction.java ├── img.png ├── img_1.png ├── img_2.png ├── img_3.png ├── img_4.png └── img_5.png /README.md: -------------------------------------------------------------------------------- 1 | # 需要在unidbg的环境下使用 !!! 2 | 3 | ## 作用 4 | 5 | * 只关心函数调用,函数执行后参数变化情况 6 | * 可以快速定位参数,输出是文本格式,可以用任意文本编辑器打开 7 | * 可以脱出很多有用有价值的信息,帮助快速定位 8 | * 同时记录了所在的内存地址,方便使用traceWrite/Read 跟踪定位 9 | * 现版本的加了函数执行计数,在一个函数多次执行的时候,可以快速定位轮数 10 | * java不是很熟练,有不合理的地方,请自行修改源码 11 | 12 | 13 | ### 更新 14 | * 参数注释说明( 类名下 15 | * 函数调用过滤,防止函数调用次数过多,导致日志太多,trace效率极低 16 | 17 | ## 用法 18 | ### 在unidbg初始化时加入 19 | ``` text 20 | new TraceFunction(emulator, module, savePath); 21 | ``` 22 | ## 更新 23 | ### 加了过滤 24 | ![img_4.png](img_4.png) 25 | ### 完整日志 26 | ![img_5.png](img_5.png) 27 | 28 | ## 效果 29 | ![img_1.png](img_1.png) 30 | 31 | ![img_2.png](img_2.png) 32 | 33 | ![img_3.png](img_3.png) 34 | 35 | ### 最新的改动 36 | ![img.png](img.png) 37 | 38 | ## 日志说明 39 | #### 函数调用 40 | ```text 41 | ======== =========================== 函数分割线 42 | 执行函数的指令地址 sub:函数地址 [call count: 调用次数] 43 | 44 | -------- --------------------------- 函数入参 45 | 内存地址 46 | 47 | ``` 48 | #### 函数返回 49 | ```text 50 | ======== =========================== 函数分割线 51 | 执行函数的指令地址 call sub:返回的函数地址 52 | 53 | -------- --------------------------- 函数反参 (也就是入参) 54 | 内存地址 55 | -------- --------------------------- retValue (函数返回值) 56 | ``` -------------------------------------------------------------------------------- /TraceFunction.java: -------------------------------------------------------------------------------- 1 | package com.open; 2 | 3 | import com.github.unidbg.Emulator; 4 | import com.github.unidbg.Module; 5 | import com.github.unidbg.arm.context.RegisterContext; 6 | import com.github.unidbg.debugger.Debugger; 7 | import com.github.unidbg.debugger.FunctionCallListener; 8 | import com.github.unidbg.pointer.UnidbgPointer; 9 | 10 | import java.io.*; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.nio.file.StandardOpenOption; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | public class TraceFunction { 19 | /* 20 | outPutPath: 日志文件保存路径 21 | arg_len: 打印入参个数 22 | windowSize: 读取内存大小( 自己可以尝试加入偏移,可能会有惊喜 23 | showRets: 显示函数返回值 24 | callCounter: 函数计数器 25 | functionTimes: 函数调用次数阈值 26 | */ 27 | private final Emulator emulator; 28 | private final RegisterContext registerContext; 29 | private final Module module; 30 | private final String outPutPath; 31 | private final int arg_len; // 打印入参个数 32 | private final int windowSize; // 显示内存大小(自己可以尝试加入偏移,可能会有惊喜 33 | private final boolean showRets; // 显示函数返回值 34 | private final Map callCounter = new HashMap<>(); // 函数计数器 35 | private final int functionTimes; // 函数调用次数阈值 36 | 37 | 38 | public TraceFunction(Emulator emulator, Module module, String outPutPath) { 39 | this.emulator = emulator; 40 | this.module = module; 41 | this.outPutPath = outPutPath; 42 | this.windowSize = 80; 43 | this.registerContext = emulator.getContext(); 44 | this.arg_len = 4; 45 | this.showRets = true; 46 | this.functionTimes = 0; 47 | trace_function(); 48 | } 49 | 50 | public TraceFunction(Emulator emulator, Module module, String outPutPath, int functionTimes) { 51 | this.emulator = emulator; 52 | this.module = module; 53 | this.outPutPath = outPutPath; 54 | this.windowSize = 80; 55 | this.registerContext = emulator.getContext(); 56 | this.arg_len = 4; 57 | this.showRets = true; 58 | this.functionTimes = functionTimes; 59 | trace_function(); 60 | } 61 | 62 | public TraceFunction(Emulator emulator, Module module, String outPutPath, int arg_len, int functionTimes) { 63 | this.emulator = emulator; 64 | this.module = module; 65 | this.outPutPath = outPutPath; 66 | this.windowSize = 80; 67 | this.registerContext = emulator.getContext(); 68 | this.showRets = true; 69 | this.arg_len = arg_len; 70 | this.functionTimes = functionTimes; 71 | trace_function(); 72 | } 73 | 74 | public TraceFunction(Emulator emulator, Module module, String outPutPath, boolean showRets, int functionTimes) { 75 | this.emulator = emulator; 76 | this.module = module; 77 | this.outPutPath = outPutPath; 78 | this.windowSize = 80; 79 | this.registerContext = emulator.getContext(); 80 | this.showRets = showRets; 81 | this.arg_len = 4; 82 | this.functionTimes = functionTimes; 83 | trace_function(); 84 | } 85 | 86 | public TraceFunction(Emulator emulator, Module module, String outPutPath, int arg_len, boolean showRets, int functionTimes) { 87 | this.emulator = emulator; 88 | this.module = module; 89 | this.outPutPath = outPutPath; 90 | this.windowSize = 80; 91 | this.registerContext = emulator.getContext(); 92 | this.showRets = showRets; 93 | this.arg_len = arg_len; 94 | this.functionTimes = functionTimes; 95 | trace_function(); 96 | } 97 | 98 | public TraceFunction(Emulator emulator, Module module, String outPutPath, int arg_len, boolean showRets, int windowSize, int functionTimes) { 99 | this.emulator = emulator; 100 | this.module = module; 101 | this.outPutPath = outPutPath; 102 | this.windowSize = windowSize; 103 | this.registerContext = emulator.getContext(); 104 | this.showRets = showRets; 105 | this.arg_len = arg_len; 106 | this.functionTimes = functionTimes; 107 | trace_function(); 108 | } 109 | 110 | 111 | public void trace_function() { 112 | Debugger debugger = emulator.attach(); 113 | PrintStream traceStream = null; 114 | try { 115 | traceStream = new PrintStream(new FileOutputStream(outPutPath, false)); 116 | } catch (FileNotFoundException e) { 117 | e.printStackTrace(); 118 | } 119 | final PrintStream finalTraceStream = traceStream; 120 | assert finalTraceStream != null; 121 | debugger.traceFunctionCall(null, new FunctionCallListener() { 122 | @Override 123 | public void onCall(Emulator emulator, long callerAddress, long functionAddress) { 124 | try { 125 | int callCount = callCounter.getOrDefault(functionAddress, 0); 126 | if (functionTimes != 0 && callCount >= functionTimes) return; 127 | callCounter.put(functionAddress, ++callCount); 128 | 129 | StringBuilder pcString = new StringBuilder(" "); 130 | pcString.append(registerContext.getPCPointer().toString()); 131 | while (pcString.length() < 59) { 132 | pcString.append(" "); 133 | } 134 | pcString.append("sub: 0x" + Long.toHexString(functionAddress - module.base)); 135 | pcString.append(" [call count: ").append(callCount).append("]"); // 添加调用次数 136 | writeFuncToFile(pcString + "\n"); 137 | for (int i = 0; i < arg_len; i++) { 138 | UnidbgPointer args = registerContext.getPointerArg(i); 139 | byte[] readBytes; 140 | try { 141 | readBytes = emulator.getBackend().mem_read(args.peer, windowSize); 142 | } catch (Exception e) { 143 | readBytes = new byte[16]; 144 | } 145 | writeArgToFile(readBytes, i, args.peer); 146 | } 147 | } catch (Exception e) { 148 | } 149 | } 150 | 151 | @Override 152 | public void postCall(Emulator emulator, long callerAddress, long functionAddress, Number[] args) { 153 | if (showRets) { 154 | try { 155 | int callCount = callCounter.getOrDefault(functionAddress, 0); 156 | if (functionTimes != 0 && callCount >= functionTimes) return; 157 | 158 | StringBuilder pcString = new StringBuilder(" "); 159 | pcString.append(registerContext.getPCPointer().toString()); 160 | while (pcString.length() < 59) { 161 | pcString.append(" "); 162 | } 163 | pcString.append("call_by: 0x" + Long.toHexString(functionAddress - module.base)); 164 | writeFuncToFile(pcString + "\n"); 165 | if (args != null && args.length > 0) { 166 | for (int l = 0; l < args.length; l++) { 167 | byte[] readBytes; 168 | UnidbgPointer pointer = UnidbgPointer.pointer(emulator, args[l].longValue()); 169 | try { 170 | readBytes = pointer.getByteArray(0, windowSize); 171 | } catch (Exception e) { 172 | readBytes = new byte[16]; 173 | } 174 | writeRetArgToFile(readBytes, l, pointer.peer); 175 | } 176 | } 177 | byte[] retBytes; 178 | long lrPoint = registerContext.getLR(); 179 | try { 180 | retBytes = emulator.getBackend().mem_read(lrPoint, windowSize); 181 | } catch (Exception e) { 182 | retBytes = new byte[16]; 183 | } 184 | writeRetToFile(retBytes, lrPoint); 185 | } catch (Exception e) { 186 | 187 | } 188 | } 189 | } 190 | }); 191 | } 192 | 193 | private void writeArgToFile(byte[] data, int args, long baseAddress) { 194 | Path outputPath = Paths.get(this.outPutPath); 195 | try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(outputPath, StandardOpenOption.CREATE, StandardOpenOption.APPEND))) { 196 | String separator = String.format("\n-------- ----------------------------------------------- ---------------- arg %d\n", args); 197 | outputStream.write(separator.getBytes()); 198 | String formattedHex = formatHexOutput(data, baseAddress); 199 | outputStream.write(formattedHex.getBytes()); 200 | outputStream.flush(); 201 | } catch (IOException e) { 202 | e.printStackTrace(); 203 | } 204 | } 205 | 206 | private void writeRetToFile(byte[] data, long baseAddress) { 207 | Path outputPath = Paths.get(this.outPutPath); 208 | try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(outputPath, StandardOpenOption.CREATE, StandardOpenOption.APPEND))) { 209 | String separator = String.format("\n-------- ----------------------------------------------- ---------------- retValue \n"); 210 | outputStream.write(separator.getBytes()); 211 | String formattedHex = formatHexOutput(data, baseAddress); 212 | outputStream.write(formattedHex.getBytes()); 213 | outputStream.flush(); 214 | } catch (IOException e) { 215 | e.printStackTrace(); 216 | } 217 | } 218 | 219 | private void writeRetArgToFile(byte[] data, int args, long baseAddress) { 220 | Path outputPath = Paths.get(this.outPutPath); 221 | try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(outputPath, StandardOpenOption.CREATE, StandardOpenOption.APPEND))) { 222 | String separator = String.format("\n-------- ----------------------------------------------- ---------------- retArg %d\n", args); 223 | outputStream.write(separator.getBytes()); 224 | String formattedHex = formatHexOutput(data, baseAddress); 225 | outputStream.write(formattedHex.getBytes()); 226 | outputStream.flush(); 227 | } catch (IOException e) { 228 | e.printStackTrace(); 229 | } 230 | } 231 | 232 | private void writeFuncToFile(String data) { 233 | Path outputPath = Paths.get(this.outPutPath); 234 | try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(outputPath, StandardOpenOption.CREATE, StandardOpenOption.APPEND))) { 235 | String separator = "\n\n======== =============================================== =========== func\n"; 236 | outputStream.write(separator.getBytes()); 237 | outputStream.write(data.getBytes()); 238 | outputStream.flush(); 239 | } catch (IOException e) { 240 | e.printStackTrace(); 241 | } 242 | } 243 | 244 | private String formatHexOutput(byte[] bytes, long baseAddress) { 245 | StringBuilder sb = new StringBuilder(); 246 | int length = bytes.length; 247 | for (int i = 0; i < length; i += 16) { 248 | sb.append(String.format("%08X ", baseAddress + i)); 249 | for (int j = 0; j < 16; j++) { 250 | if (i + j < length) { 251 | sb.append(String.format("%02X ", bytes[i + j])); 252 | } else { 253 | sb.append(" "); 254 | } 255 | } 256 | sb.append(" "); 257 | for (int j = 0; j < 16; j++) { 258 | if (i + j < length) { 259 | byte b = bytes[i + j]; 260 | if (b >= 32 && b <= 126) { 261 | sb.append((char) b); 262 | } else { 263 | sb.append('.'); 264 | } 265 | } else { 266 | sb.append(" "); 267 | } 268 | } 269 | sb.append(System.lineSeparator()); 270 | } 271 | return sb.toString(); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CookingDuck/TraceFunction/cf96205f2a10b559b165aad78dd6714b502520f5/img.png -------------------------------------------------------------------------------- /img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CookingDuck/TraceFunction/cf96205f2a10b559b165aad78dd6714b502520f5/img_1.png -------------------------------------------------------------------------------- /img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CookingDuck/TraceFunction/cf96205f2a10b559b165aad78dd6714b502520f5/img_2.png -------------------------------------------------------------------------------- /img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CookingDuck/TraceFunction/cf96205f2a10b559b165aad78dd6714b502520f5/img_3.png -------------------------------------------------------------------------------- /img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CookingDuck/TraceFunction/cf96205f2a10b559b165aad78dd6714b502520f5/img_4.png -------------------------------------------------------------------------------- /img_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CookingDuck/TraceFunction/cf96205f2a10b559b165aad78dd6714b502520f5/img_5.png --------------------------------------------------------------------------------