├── .gitignore ├── README.md ├── iglogger.java └── iglogger.smali /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Eclipse project files 19 | .classpath 20 | .project 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | IGLogger 2 | ======== 3 | 4 | Class to help with adding logging function in smali output from 3rd party Android apps. 5 | 6 | Usage 7 | ===== 8 | 9 | Compile this to an APK, use APKTool to decompile, place the "iglogger.smali" in the 10 | root of the application you want logging from (after you APKTool'ed it). Then to 11 | log out, simply add a line of Smali where you want to log out. The easiest case 12 | is simple to add: 13 | 14 | invoke-static {}, Liglogger;->d()I 15 | 16 | Alternatively, you can log variables, but you do need to ensure you get the types 17 | correct. Example, if v1 is already a string, use: 18 | 19 | invoke-static {v1}, Liglogger;->d(Ljava/lang/String;)I 20 | 21 | If you want a log "TAG" message other than the one below, each logging method also 22 | as a method which will take a string as the first parameter (this matches the standard 23 | android.util.Log calls), however, to ensure you don't overwrite a register already in use, 24 | it is recommended you increase the "locals" count by 1 at the start of the method. 25 | 26 | .locals 10 # previously was 9 27 | 28 | const-string v9, "!!!IGLOGGER - v1 array length : !!!" 29 | invoke-static {v9, v1}, Liglogger;->d(Ljava/lang/String;I)I 30 | 31 | This ensures you are not overwriting application data which may have already been in "v9". 32 | In the previous example, "v1" was an of type "int". 33 | 34 | "Trace" methods are designed to work with the APKSmash script (injects logging 35 | automatically based on Android API calls). Some of these can be useful for logging items 36 | by hand as well. For example, APKSmash will inject the "trace_intent_sendactivity" method 37 | and pass the Intent to it before the Activity is started. The code might look like this. 38 | 39 | invoke-static {v1}, Liglogger;->trace_intent_sendactivity(Landroid/content/Intent;)I 40 | invoke-virtual {v0, v1}, Landroid/app/Activity;->startActivity(Landroid/content/Intent;)V 41 | 42 | The Intent trace loggers will attempt to parse the Intent in two ways. The first is a simple 43 | call to toString (you might see this for the Intent in the logs already). The second way 44 | is to export the proper "am" (Activity Manager) command to allow resending the Intent 45 | from an adb shell (note: this is still a little buggy but should get you started. Watch 46 | for error messages in the am command) 47 | 48 | 49 | Tips for Errors 50 | =============== 51 | 52 | If you get a validation error such as: 53 | 54 | W/dalvikvm(12928): VFY: register1 v1 type 17, wanted ref 55 | 56 | This means you got the type of "v1" wrong and it is of type "17". The look up for these 57 | types is listed at the URL below (also marking these in the comments for the types supported 58 | in this logging class) 59 | 60 | http://source.android.com/tech/dalvik/dex-format.html 61 | 62 | ("Value Formats" table) 63 | 64 | If logging looks strange or missing from Logcat in DDMS/Monitor, try "adb logcat". 65 | 66 | Version History 67 | =============== 68 | 69 | v2.55 70 | 71 | + Added printing of the line number to all trace functions. The new APKSmash script should insert line numbers, making this output more useful. 72 | 73 | v2.50 74 | 75 | + Added a lot more "trace" classes (for use mainly with APKSmash). 76 | + Handles more types of objects and parsing (Intents, SQL calls) 77 | -------------------------------------------------------------------------------- /iglogger.java: -------------------------------------------------------------------------------- 1 | import java.net.HttpURLConnection; 2 | import java.net.URL; 3 | import java.net.URLConnection; 4 | import java.util.Collection; 5 | import java.util.HashSet; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Map.Entry; 10 | import java.util.Set; 11 | 12 | import android.content.ContentValues; 13 | import android.content.Intent; 14 | import android.database.sqlite.SQLiteQueryBuilder; 15 | import android.os.Bundle; 16 | import android.text.TextUtils; 17 | 18 | /* 19 | * Wrapper for debugging methods. 20 | * I'm suggesting making this all "wtf" level, but feel free to change this :-) 21 | * 22 | * =========== Usage ==================== 23 | * 24 | * Compile this to an APK, use APKTool to decompile, place the "iglogger.smali" in the 25 | * root of the application you want logging from (after you APKTool'ed it). Then to 26 | * log out, simply add a line of Smali where you want to log out. The easiest case 27 | * is simple to add: 28 | * 29 | * invoke-static {}, Liglogger;->d()I 30 | * 31 | * Alternatively, you can log variables, but you do need to ensure you get the types 32 | * correct. Example, if v1 is already a string, use: 33 | * 34 | * invoke-static {v1}, Liglogger;->d(Ljava/lang/String;)I 35 | * 36 | * If you want a log "TAG" message other than the one below, each logging method also 37 | * as a method which will take a string as the first parameter (this matches the standard 38 | * android.util.Log calls), however, to ensure you don't overwrite a register already in use, 39 | * it is recommended you increase the "locals" count by 1 at the start of the method. 40 | * 41 | * .locals 10 # previously was 9 42 | * 43 | * const-string v9, "!!!IGLOGGER - v1 array length : !!!" 44 | * invoke-static {v9, v1}, Liglogger;->d(Ljava/lang/String;I)I 45 | * 46 | * This ensures you are not overwriting application data which may have already been in "v9". 47 | * In the previous example, "v1" was an of type "int". 48 | * 49 | * 50 | * =========== Tips for Errors ==================== 51 | * 52 | * If you get a validation error such as: 53 | * W/dalvikvm(12928): VFY: register1 v1 type 17, wanted ref 54 | * This means you got the type of "v1" wrong and it is of type "17". The look up for these 55 | * types is listed at the URL below (also marking these in the comments for the types supported 56 | * in this logging class) 57 | * 58 | * http://source.android.com/tech/dalvik/dex-format.html 59 | * ("Value Formats" table) 60 | * 61 | */ 62 | 63 | public class iglogger { 64 | // Change this as needed. This is the default log message string 65 | // In logcat, set your filter for "tag:!!!" to view the messages from this class 66 | private static String LOG_TAG = "!!! IGLogger"; 67 | private static String TRACE_TAG = "!!! IGTraceLogger"; 68 | private static String VERSION = "IGLogger 2.55 - 04/24/2013"; 69 | 70 | /* Default Case 71 | * - Use this call to just print you where here in a method 72 | * 73 | * *** SMALI CODE TO ADD *** 74 | * invoke-static {}, Liglogger;->d()I 75 | * 76 | */ 77 | static public int d() { 78 | Throwable t = new Throwable(); 79 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 80 | 81 | // Unfortuantely we cant get the details for an overloaded method 82 | // this would be helpful for obfuscated classes 83 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 84 | 85 | // Line number so far as "-1" but this might work on some apps. 86 | // We'll leave it for now. 87 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 88 | return android.util.Log.wtf(logtag, logtag); 89 | } 90 | 91 | 92 | /* Special Case: stacktrace 93 | * - Use this print a stack trace to the log when you hit this code, but no 94 | * error is thrown. Useful in obfuscated classes where its hard to tell what 95 | * called into your current method. 96 | * 97 | * *** SMALI CODE TO ADD *** 98 | * invoke-static {}, Liglogger;->stacktrace()I 99 | * 100 | */ 101 | static public int stacktrace() { 102 | Throwable t = new Throwable(); 103 | for(int x = 1; x < t.getStackTrace().length; x++){ 104 | String logtag = LOG_TAG + ": STACKTRACE: " + x; 105 | android.util.Log.wtf(logtag, t.getStackTrace()[x].getClassName()); 106 | } 107 | return 1; 108 | } 109 | 110 | /* String Case 111 | * Smali Type Value: 0x17 112 | * 113 | * *** SMALI CODE TO ADD *** 114 | * invoke-static {v0}, Liglogger;->d(Ljava/lang/String;)I 115 | * 116 | */ 117 | static public int d(String m) { 118 | Throwable t = new Throwable(); 119 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 120 | return android.util.Log.wtf(logtag, notEmpty(m)); 121 | } 122 | 123 | static public int d(String t, String m) { 124 | //writeLog("D", t, m); 125 | return android.util.Log.wtf(t, notEmpty(m)); 126 | } 127 | 128 | 129 | 130 | /* Boolean Case 131 | * Smali Type Value: 0x1f 132 | * 133 | * *** SMALI CODE TO ADD *** 134 | * invoke-static {v0}, Liglogger;->d(Ljava/lang/Boolean;)I 135 | * 136 | */ 137 | static public int d(Boolean m) { 138 | Throwable t = new Throwable(); 139 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 140 | return android.util.Log.wtf(logtag, notEmpty(m.toString())); 141 | } 142 | 143 | static public int d(String t, Boolean m) { 144 | return android.util.Log.wtf(t, notEmpty(m.toString())); 145 | } 146 | 147 | /* Char Case 148 | * Smali Type Value: 0x17 149 | * 150 | * *** SMALI CODE TO ADD *** 151 | * invoke-static {v0}, Liglogger;->d(C)I 152 | * 153 | */ 154 | static public int d(char m) { 155 | Throwable t = new Throwable(); 156 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 157 | return android.util.Log.wtf(logtag, notEmpty(Character.toString(m))); 158 | } 159 | 160 | static public int d(String t, char m) { 161 | return android.util.Log.wtf(t, notEmpty(Character.toString(m))); 162 | } 163 | 164 | /* Int Case 165 | * Smali Type Value: 0x04 166 | * 167 | * *** SMALI CODE TO ADD *** 168 | * invoke-static {v0}, Liglogger;->d(I)I 169 | * 170 | */ 171 | static public int d(int m) { 172 | Throwable t = new Throwable(); 173 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 174 | String s = notEmpty(Integer.toString(m)); 175 | return android.util.Log.wtf(logtag, s); 176 | } 177 | 178 | static public int d(String t, int m) { 179 | String s = notEmpty(Integer.toString(m)); 180 | return android.util.Log.wtf(t, s); 181 | } 182 | 183 | /* Long Case 184 | * Smali Type Value: 0x06 185 | * 186 | * *** SMALI CODE TO ADD *** 187 | * invoke-static {v0}, Liglogger;->d(J)I 188 | * 189 | */ 190 | static public int d(long m) { 191 | Throwable t = new Throwable(); 192 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 193 | String s = notEmpty(Long.toString(m)); 194 | return android.util.Log.wtf(logtag, s); 195 | } 196 | 197 | static public int d(String t, long m) { 198 | String s = notEmpty(Long.toString(m)); 199 | return android.util.Log.wtf(t, s); 200 | } 201 | 202 | /* Float Case 203 | * Smali Type Value: 0x10 204 | * 205 | * *** SMALI CODE TO ADD *** 206 | * invoke-static {v0}, Liglogger;->d(F)I 207 | * 208 | */ 209 | static public int d(float m) { 210 | Throwable t = new Throwable(); 211 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 212 | return android.util.Log.wtf(logtag, Float.toString(m)); 213 | } 214 | 215 | static public int d(String t, float m) { 216 | return android.util.Log.wtf(t, Float.toString(m)); 217 | } 218 | 219 | 220 | /* Double Case 221 | * Smali Type Value: 0x11 222 | * 223 | * *** SMALI CODE TO ADD *** 224 | * invoke-static {v0}, Liglogger;->d(D)I 225 | * 226 | */ 227 | static public int d(double m) { 228 | Throwable t = new Throwable(); 229 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 230 | return android.util.Log.wtf(logtag, Double.toString(m)); 231 | } 232 | 233 | static public int d(String t, double m) { 234 | return android.util.Log.wtf(t, Double.toString(m)); 235 | } 236 | 237 | 238 | /* Short Case 239 | * Smali Type Value: 0x02 240 | * 241 | * *** SMALI CODE TO ADD *** 242 | * 243 | * 244 | */ 245 | static public int d(short m) { 246 | Throwable t = new Throwable(); 247 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 248 | return android.util.Log.wtf(logtag, Short.toString(m)); 249 | } 250 | 251 | static public int d(String t, short m) { 252 | return android.util.Log.wtf(t, Short.toString(m)); 253 | } 254 | 255 | /* Byte Case 256 | * Smali Type Value: 0x00 257 | * 258 | * *** SMALI CODE TO ADD *** 259 | * 260 | * 261 | */ 262 | static public int d(byte m) { 263 | Throwable t = new Throwable(); 264 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 265 | return android.util.Log.wtf(logtag, Byte.toString(m)); 266 | } 267 | 268 | static public int d(String t, byte m) { 269 | return android.util.Log.wtf(t, Byte.toString(m)); 270 | } 271 | 272 | /* String Array Case 273 | * Smali Type Value: 0x1c 274 | * 275 | * *** SMALI CODE TO ADD *** 276 | * invoke-static {v0}, Liglogger;->d([Ljava/lang/String;)I 277 | * 278 | */ 279 | static public int d(String []m) { 280 | Throwable t = new Throwable(); 281 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 282 | 283 | for(int x=0; x < m.length; x++){ 284 | String currentlogtag = logtag + " - array " + x; 285 | android.util.Log.wtf(currentlogtag, notEmpty(m[x])); 286 | } 287 | return 1; 288 | } 289 | 290 | static public int d(String t, String []m) { 291 | for(int x=0; x < m.length; x++){ 292 | String logtag = t + " - array " + x; 293 | android.util.Log.wtf(logtag, notEmpty(m[x])); 294 | } 295 | return 1; 296 | } 297 | 298 | 299 | /* Collection of Strings Case 300 | * Smali Type Value: 0x1d 301 | * 302 | * Hint, look at the ".signature" of the method in your decompiled application to 303 | * see if your collection is a type . You may need to add a new method here 304 | * if it's not 305 | * 306 | * *** SMALI CODE TO ADD *** 307 | * invoke-static {v0}, Liglogger;->d(Ljava/util/Collection;)I 308 | * 309 | */ 310 | static public int d(Collection m) { 311 | Throwable t = new Throwable(); 312 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 313 | 314 | int x = 0; 315 | for (Iterator iterator = m.iterator(); iterator.hasNext();) { 316 | String currentlogtag = logtag + " - collection " + x++; 317 | android.util.Log.wtf(currentlogtag, notEmpty(iterator.next())); 318 | } 319 | return 1; 320 | } 321 | 322 | static public int d(String t, Collection m) { 323 | int x = 0; 324 | for (Iterator iterator = m.iterator(); iterator.hasNext();) { 325 | String logtag = t + " - collection " + x++; 326 | android.util.Log.wtf(logtag, notEmpty(iterator.next())); 327 | } 328 | return 1; 329 | } 330 | 331 | 332 | /* Object Case 333 | * 334 | * *** SMALI CODE TO ADD *** 335 | * invoke-static {v0}, Liglogger;->d(Ljava/lang/Object;)I 336 | * 337 | */ 338 | static public int d(Object m) { 339 | Throwable t = new Throwable(); 340 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 341 | 342 | try{ 343 | android.util.Log.wtf(logtag, m.toString()); 344 | } catch (Exception e) { 345 | android.util.Log.wtf(logtag, "Error, could not convert to string"); 346 | } 347 | return 1; 348 | } 349 | 350 | static public int d(String t, Object m) { 351 | try{ 352 | android.util.Log.wtf(t, m.toString()); 353 | } catch (Exception e) { 354 | android.util.Log.wtf(t, "Error, could not convert to string"); 355 | } 356 | return 1; 357 | } 358 | 359 | 360 | /* Byte Array Case 361 | * - will print the byte array as a hex string 362 | * 363 | * *** SMALI CODE TO ADD *** 364 | * invoke-static {v0}, Liglogger;->d([B)I 365 | * 366 | */ 367 | static public int d(byte[] m) { 368 | Throwable t = new Throwable(); 369 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName() + " - byte[] in Hex: "; 370 | return android.util.Log.wtf(logtag, notEmpty(ListToHex(m))); 371 | } 372 | 373 | static public int d(String t, byte[] m) { 374 | return android.util.Log.wtf(t, notEmpty(ListToHex(m))); 375 | } 376 | 377 | 378 | /* URL Case 379 | * 380 | * *** SMALI CODE TO ADD *** 381 | * invoke-static {v0}, Liglogger;->d(Ljava/net/URL;)I 382 | * 383 | */ 384 | static public int d(URL m) { 385 | Throwable t = new Throwable(); 386 | String logtag = LOG_TAG + ": " + t.getStackTrace()[1].getClassName(); 387 | 388 | try{ 389 | android.util.Log.wtf(logtag, m.toString()); 390 | } catch (Exception e) { 391 | android.util.Log.wtf(logtag, "Error, could not convert URL to string"); 392 | } 393 | return 1; 394 | } 395 | 396 | static public int d(String t, URL m) { 397 | try{ 398 | android.util.Log.wtf(t, m.toString()); 399 | } catch (Exception e) { 400 | android.util.Log.wtf(t, "Error, could not convert URL to string"); 401 | } 402 | return 1; 403 | } 404 | 405 | /* URLConnection Case 406 | * 407 | * *** SMALI CODE TO ADD *** 408 | * invoke-static {v0}, Liglogger;->d(Ljava/net/HttpURLConnection;)I 409 | * 410 | */ 411 | static public int d(HttpURLConnection m) { 412 | Throwable t = new Throwable(); 413 | String logtag = LOG_TAG + ": URLConnection :" + t.getStackTrace()[1].getClassName(); 414 | return d(logtag,(URLConnection) m); 415 | } 416 | 417 | static public int d(String logtag, HttpURLConnection m) { 418 | return d(notEmpty(logtag),(URLConnection) m); 419 | } 420 | 421 | static public int d(URLConnection m) { 422 | Throwable t = new Throwable(); 423 | String logtag = LOG_TAG + ": URLConnection :" + t.getStackTrace()[1].getClassName(); 424 | return d(logtag,m); 425 | } 426 | 427 | static public int d(String logtag, URLConnection m) { 428 | 429 | try{ 430 | android.util.Log.wtf(logtag, m.toString()); 431 | 432 | //Print Headers 433 | Map> headers = m.getRequestProperties(); 434 | for (String key : headers.keySet()) { 435 | // The header may appear more than once, print all the things 436 | List values = headers.get(key); 437 | Iterator itr = values.iterator(); 438 | while(itr.hasNext()) { 439 | android.util.Log.wtf(logtag + " Header: " + key, notEmpty(itr.next())); 440 | } 441 | } 442 | 443 | } catch (Exception e) { 444 | android.util.Log.wtf(logtag, "Error, could not convert URLConnection to string"); 445 | } 446 | return 1; 447 | } 448 | 449 | /* =============================== TRACE ======================================== 450 | * 451 | * These calls can handle more complex objects and are designed to work with the APKSmash script. 452 | * Things might get a be bloated down here since these are designed to be placed in the APK 453 | * in an automated way (ie, logging all Intents which are received or sent to an app). Some of 454 | * these might be useful for standard logging as well though. 455 | * 456 | * 457 | */ 458 | 459 | 460 | 461 | /* Trace Method 462 | * - Use this call to just print you where here in a method 463 | * - Main use is for putting this at the start of each method to help trace an obfuscated app 464 | * 465 | * *** SMALI CODE TO ADD *** 466 | * invoke-static {}, Liglogger;->trace_method()I 467 | * 468 | */ 469 | static public int trace_method() { 470 | Throwable t = new Throwable(); 471 | String logtag = TRACE_TAG + " in Method: " + t.getStackTrace()[1].getClassName(); 472 | 473 | // Unfortuantely we cant get the details for an overloaded method 474 | // this would be helpful for obfuscated classes 475 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 476 | 477 | // Line number so far as "-1" but this might work on some apps. 478 | // We'll leave it for now. 479 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 480 | return android.util.Log.i(logtag, logtag); 481 | } 482 | static public int trace_boolmethod(boolean m) { 483 | Throwable t = new Throwable(); 484 | String p = ""; 485 | String logtag = TRACE_TAG + " Boolean Method Return: " + t.getStackTrace()[1].getClassName(); 486 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 487 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 488 | 489 | if(m){ 490 | p = " returning: TRUE"; 491 | } else { 492 | p = " returning: FALSE"; 493 | } 494 | return android.util.Log.i(logtag, notEmpty(p)); 495 | } 496 | 497 | 498 | /* More Trace Stuff 499 | * 500 | * Since we can't pass a string to the logger without more changes to smali code, 501 | * we'll make a good log string here and call the correct trace method. 502 | * 503 | */ 504 | static public int trace_dbcolumn(String m) { 505 | Throwable t = new Throwable(); 506 | String logtag = TRACE_TAG + " DB Get Column : " + t.getStackTrace()[1].getClassName(); 507 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 508 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 509 | return android.util.Log.wtf(logtag, notEmpty(m)); 510 | } 511 | static public int trace_dbgetstring(String m) { 512 | Throwable t = new Throwable(); 513 | String logtag = TRACE_TAG + " DB Get String Value : " + t.getStackTrace()[1].getClassName(); 514 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 515 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 516 | return android.util.Log.wtf(logtag, notEmpty(m)); 517 | } 518 | static public int trace_sharedpref(String m) { 519 | Throwable t = new Throwable(); 520 | String logtag = TRACE_TAG + " Shared Pref Access : " + t.getStackTrace()[1].getClassName(); 521 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 522 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 523 | return android.util.Log.wtf(logtag, notEmpty(m)); 524 | } 525 | static public int trace_json(String m) { 526 | Throwable t = new Throwable(); 527 | String logtag = TRACE_TAG + " creating JSON: " + t.getStackTrace()[1].getClassName(); 528 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 529 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 530 | return android.util.Log.wtf(logtag, notEmpty(m)); 531 | } 532 | static public int trace_httpstring(String m) { 533 | Throwable t = new Throwable(); 534 | String logtag = TRACE_TAG + " HTTP String: " + t.getStackTrace()[1].getClassName(); 535 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 536 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 537 | return android.util.Log.wtf(logtag, notEmpty(m)); 538 | } 539 | static public int trace_intent(String m) { 540 | Throwable t = new Throwable(); 541 | String logtag = TRACE_TAG + " creating INTENT: " + t.getStackTrace()[1].getClassName(); 542 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 543 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 544 | return android.util.Log.wtf(logtag, notEmpty(m)); 545 | } 546 | static public int trace_intent(Intent i) { 547 | return trace_intent(i, "UNKNOWN", "UNKNOWN"); 548 | } 549 | static public int trace_intent_sendactivity(Intent i) { 550 | return trace_intent(i, "start", "SENDING"); 551 | } 552 | static public int trace_intent_sendservice(Intent i) { 553 | return trace_intent(i, "startservice", "SENDING"); 554 | } 555 | static public int trace_intent_sendbroadcast(Intent i) { 556 | return trace_intent(i, "broadcast", "SENDING"); 557 | } 558 | static public int trace_intent_receiveactivity(Intent i) { 559 | return trace_intent(i, "start", "RECEIVED"); 560 | } 561 | static public int trace_intent_receiveservice(Intent i) { 562 | return trace_intent(i, "startservice", "RECEIVED"); 563 | } 564 | static public int trace_intent_receivebroadcast(Intent i) { 565 | return trace_intent(i, "broadcast", "RECEIVED"); 566 | } 567 | static public int trace_intent(Intent i, String commandtype, String sendorreceive) { 568 | Throwable t = new Throwable(); 569 | String logtag = TRACE_TAG + " " + sendorreceive + " INTENT from: " + t.getStackTrace()[2].getClassName(); 570 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 571 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 572 | 573 | String m = "am "+ commandtype; 574 | try{ 575 | // This should put data in a form for 'adb shell am' 576 | 577 | if(i.getAction() != null) 578 | m += " -a '" + i.getAction() + "'"; 579 | if(i.getDataString() != null) 580 | m += " -d '" + i.getDataString() + "'"; 581 | if(i.getType() != null) 582 | m += " -t '" + i.getType() + "'"; 583 | 584 | // See if there are extras 585 | Bundle b = i.getExtras(); 586 | if((b != null) && (b.size() > 0)){ 587 | Set s = b.keySet(); 588 | for (String keyname : s) { 589 | if (b.get(keyname) instanceof String){ 590 | m += " --es '" + keyname + "' '" + b.get(keyname).toString() + "'"; } 591 | else if (b.get(keyname) instanceof Boolean){ 592 | m += " --ez '" + keyname + "' " + b.get(keyname).toString(); } 593 | else if (b.get(keyname) instanceof Integer){ 594 | m += " --ei '" + keyname + "' " + notEmpty(Integer.toString(b.getInt(keyname))); } 595 | else if (b.get(keyname) instanceof Float){ 596 | m += " --ef '" + keyname + "' " + notEmpty(Float.toString(b.getFloat(keyname))); } 597 | else if (b.get(keyname) instanceof Double){ 598 | m += " --ed '" + keyname + "' " + notEmpty(Double.toString(b.getDouble(keyname))); } 599 | else if (b.get(keyname) instanceof Long){ 600 | m += " --el '" + keyname + "' " + notEmpty(Long.toString(b.getLong(keyname))); } 601 | else if (b.get(keyname) instanceof Object){ 602 | // Fall back in case you can send some custom object 603 | // Seen this as serialized objects, hashmaps, etc... might add some reflection and see if we can parse more 604 | m += " UNKNOWN_TYPE(key: " + keyname + " value: "+ b.get(keyname).toString() + ")"; 605 | } 606 | else { 607 | m += " [[[ ERROR UNKNOWN TYPE for '" + keyname + "' class is: " + b.get(keyname).getClass() + "]]]"; 608 | } 609 | } 610 | } 611 | 612 | if(i.getFlags() > 0) 613 | m += " -f 0x" + Integer.toHexString(i.getFlags()); 614 | if(i.getPackage() != null) 615 | m += " '" + i.getPackage() + "'"; 616 | if(i.getComponent() != null) 617 | m += " " + i.getComponent().flattenToString(); 618 | 619 | 620 | } catch(Exception e){ 621 | m = "FAILED TO PARSE INTENT"; 622 | } 623 | 624 | // for debugging, we might have missed something above... consider turn this off later 625 | android.util.Log.wtf(TRACE_TAG + " INTENT toString ", notEmpty(i.toString())); 626 | 627 | return android.util.Log.wtf(logtag, notEmpty(m)); 628 | } 629 | static public int trace_getextras(Bundle b) { 630 | String m = ""; 631 | if((b == null) || (b.size() < 1)){ 632 | return 0; //I don't have a reason to log empty bundles 633 | } 634 | 635 | //Warning: Getting all these as Strings, but that might not be the correct type 636 | Set s = b.keySet(); 637 | for (String keyname : s) { 638 | m += "name: " + keyname; 639 | try { 640 | if (b.get(keyname) instanceof String){ 641 | m += " value: " + b.get(keyname).toString(); } 642 | else if (b.get(keyname) instanceof Integer){ 643 | m += " value (int): " + notEmpty(Integer.toString(b.getInt(keyname))); } 644 | else if (b.get(keyname) instanceof Float){ 645 | m += " value (float): " + notEmpty(Float.toString(b.getFloat(keyname))); } 646 | else if (b.get(keyname) instanceof Double){ 647 | m += " value (double): " + notEmpty(Double.toString(b.getDouble(keyname))); } 648 | else if (b.get(keyname) instanceof Long){ 649 | m += " value (double): " + notEmpty(Long.toString(b.getLong(keyname))); } 650 | else if (b.get(keyname) instanceof Object){ 651 | m += " value (obj): " + b.get(keyname).toString(); } 652 | else { 653 | m += " value - ERROR UNKNOWN TYPE: " + b.get(keyname).getClass(); 654 | } 655 | 656 | } catch (Exception e){ 657 | m += " ERROR GETTING VALUE PROBABLY A TYPE FAILURE (what type is this?)"; 658 | } 659 | } 660 | 661 | Throwable t = new Throwable(); 662 | String logtag = TRACE_TAG + " INTENT extras: " + t.getStackTrace()[1].getClassName(); 663 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 664 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 665 | return android.util.Log.wtf(logtag, notEmpty(m)); 666 | } 667 | 668 | /* Trace Stuff for SQL 669 | * 670 | * Just want a clean SQL string in the logs. Lots of ways this can come in though 671 | * (distinct y/n, limit y/n) 672 | * 673 | */ 674 | static public int trace_sqlstring(String m) { 675 | Throwable t = new Throwable(); 676 | String logtag = TRACE_TAG + " SQL String: " + t.getStackTrace()[1].getClassName(); 677 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 678 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 679 | return android.util.Log.wtf(logtag, notEmpty(m)); 680 | } 681 | static public int trace_sqlstring(String m, String[] a) { 682 | Throwable t = new Throwable(); 683 | String logtag = TRACE_TAG + " SQL String w/ args: " + t.getStackTrace()[1].getClassName(); 684 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 685 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 686 | return android.util.Log.wtf(logtag, notEmpty(m + a.toString() )); 687 | } 688 | static public int trace_sqlquery(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) { 689 | String sql = SQLiteQueryBuilder.buildQueryString(false, table, columns, selection, groupBy, having, orderBy, limit); 690 | Throwable t = new Throwable(); 691 | String logtag = TRACE_TAG + " SQL Query: " + t.getStackTrace()[1].getClassName(); 692 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 693 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 694 | return android.util.Log.wtf(logtag, notEmpty(sql)); 695 | } 696 | static public int trace_sqlquery(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) { 697 | String sql = SQLiteQueryBuilder.buildQueryString(distinct, table, columns, selection, groupBy, having, orderBy, limit); 698 | Throwable t = new Throwable(); 699 | String logtag = TRACE_TAG + " SQL Query: " + t.getStackTrace()[1].getClassName(); 700 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 701 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 702 | return android.util.Log.wtf(logtag, notEmpty(sql)); 703 | } 704 | static public int trace_sqlquery(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) { 705 | String sql = SQLiteQueryBuilder.buildQueryString(false, table, columns, selection, groupBy, having, orderBy, null); 706 | Throwable t = new Throwable(); 707 | String logtag = TRACE_TAG + " SQL Query: " + t.getStackTrace()[1].getClassName(); 708 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 709 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 710 | return android.util.Log.wtf(logtag, notEmpty(sql)); 711 | } 712 | static public int trace_sqlquery(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) { 713 | String sql = SQLiteQueryBuilder.buildQueryString(distinct, table, columns, selection, groupBy, having, orderBy, null); 714 | Throwable t = new Throwable(); 715 | String logtag = TRACE_TAG + " SQL Query: " + t.getStackTrace()[1].getClassName(); 716 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 717 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 718 | return android.util.Log.wtf(logtag, notEmpty(sql)); 719 | } 720 | static public int trace_sqlupdate(String table, ContentValues values, String whereClause, String[] whereArgs) { 721 | 722 | StringBuilder sqlsub = new StringBuilder(120); 723 | StringBuilder sql = new StringBuilder(120); 724 | sql.append("UPDATE "); 725 | sql.append(table); 726 | sql.append(" SET "); 727 | 728 | // move all bind args to one array 729 | int setValuesSize = values.size(); 730 | int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length); 731 | Object[] bindArgs = new Object[bindArgsSize]; 732 | int i = 0; 733 | 734 | //Added this code to be API 1 compatible: keySet() requires API 11 735 | Set ar = new HashSet(); 736 | Set> s=values.valueSet(); 737 | int x = 0; 738 | for (Entry entry : s) { 739 | ar.add(entry.getKey()); 740 | sqlsub.append((x > 0) ? ", " : ""); 741 | sqlsub.append(entry.getValue()); 742 | x++; 743 | } 744 | 745 | for (String colName : ar ) { 746 | sql.append((i > 0) ? "," : ""); 747 | sql.append(colName); 748 | bindArgs[i++] = values.get(colName); 749 | sql.append("=?"); 750 | } 751 | if (whereArgs != null) { 752 | for (i = setValuesSize; i < bindArgsSize; i++) { 753 | bindArgs[i] = whereArgs[i - setValuesSize]; 754 | } 755 | } 756 | if (!TextUtils.isEmpty(whereClause)) { 757 | sql.append(" WHERE "); 758 | sql.append(whereClause); 759 | } 760 | 761 | String m = sql.toString() + "; [" + sqlsub + "]"; 762 | 763 | Throwable t = new Throwable(); 764 | String logtag = TRACE_TAG + " SQL String: " + t.getStackTrace()[1].getClassName(); 765 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 766 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 767 | return android.util.Log.wtf(logtag, notEmpty(m)); 768 | } 769 | 770 | static public int trace_basicnamevaluepair(String m, String n) { 771 | Throwable t = new Throwable(); 772 | String logtag = TRACE_TAG + " name value: " + t.getStackTrace()[1].getClassName(); 773 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 774 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 775 | return android.util.Log.wtf(logtag, notEmpty(m) + ": " + notEmpty(n)); 776 | } 777 | 778 | /* Trace String Compare 779 | * Logs out a string being compared. Log right before or after this line 780 | * invoke-virtual {v3, v4}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z 781 | * 782 | * There is a known issue with this and Android 2.3 (and lower?) where this seems to go unlogged 783 | * Issue maybe with the throwable object and some classes. Logging can still work, but you wont 784 | * be able to automatically get the class/method name of the caller. 785 | * 786 | * *** SMALI CODE TO ADD *** 787 | * invoke-static {v1, v2}, Liglogger;->trace_stringcompare(Ljava/lang/String;Ljava/lang/String;)I 788 | * 789 | */ 790 | static public int trace_stringcompare(String m, String n) { 791 | Throwable t = new Throwable(); 792 | String logtag = TRACE_TAG + " string compare: " + t.getStackTrace()[1].getClassName(); 793 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 794 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 795 | return android.util.Log.wtf(logtag, notEmpty(m) + " == " + notEmpty(n)); 796 | } 797 | static public int trace_stringcompare(String m, Object n) { 798 | Throwable t = new Throwable(); 799 | String logtag = TRACE_TAG + " string compare: " + t.getStackTrace()[1].getClassName(); 800 | logtag = logtag + "->" + t.getStackTrace()[1].getMethodName(); 801 | logtag = logtag + " Line " + t.getStackTrace()[1].getLineNumber(); 802 | if(n == null ){ 803 | return android.util.Log.wtf(logtag, notEmpty(m) + " == "); 804 | } 805 | return android.util.Log.wtf(logtag, notEmpty(m) + " == " + notEmpty(n.toString())); 806 | } 807 | 808 | 809 | 810 | 811 | 812 | //Utility Classes 813 | public static String ListToHex(byte[] data){ 814 | String string = ""; 815 | for (int i=0;i= 0) ? b : 256 + b)); 818 | if(s.length() == 1) s.insert(0,'0'); 819 | string = string + s; 820 | } 821 | return string; 822 | } 823 | 824 | public static byte[] HexToList(String s){ 825 | int len = s.length(); 826 | byte[] data = new byte[len / 2]; 827 | for (int i = 0; i < len; i += 2) { 828 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 829 | + Character.digit(s.charAt(i+1), 16)); 830 | } 831 | return data; 832 | } 833 | 834 | public static String notEmpty(String s){ 835 | if(s == null || s.length() < 1){ 836 | return ""; 837 | } 838 | return s; 839 | } 840 | 841 | 842 | 843 | } 844 | --------------------------------------------------------------------------------