├── README.md ├── TestAppsFlyer ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ └── runConfigurations.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── king │ │ │ └── testappsflyer │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── test │ │ │ │ └── flyer │ │ │ │ ├── InstallReceiver.java │ │ │ │ └── MainActivity.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── king │ │ └── testappsflyer │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle └── raptor_frida_android_trace_luoyanbei.js /README.md: -------------------------------------------------------------------------------- 1 | # android_frida_trace_myTrace 2 | android使用frida脚本追踪方法调用,方法之间的关系一目了然。 3 | 4 | ### 开始 5 | 在 [android逆向__超级好用,使用frida追踪方法](https://juejin.im/post/5d0ca6c8e51d45777b1a3db2) 一文中,介绍了如何使用firda脚本追踪android的方法调用,在实际的使用中,方法被调用,就会在终端打印方法的开始标记、方法的参数和方法的结束标记。但是看起来有些凌乱: 6 | ``` 7 | *** entered com.test.flyer.MainActivity$1.onClick 8 | arg[0]: android.support.v7.widget.AppCompatButton{4267c9c8 VFED..C. ...P.... 0,0-264,144 #7f070022 app:id/button} 9 | *** entered com.test.flyer.MainActivity.test 10 | arg[0]: Jack 11 | *** entered com.test.flyer.MainActivity.gainAge 12 | arg[0]: 16 13 | retval: 26 14 | *** exiting com.test.flyer.MainActivity.gainAge 15 | *** entered com.test.flyer.MainActivity.gainEnjoy 16 | arg[0]: 篮球 17 | retval: 我喜欢篮球 18 | *** exiting com.test.flyer.MainActivity.gainEnjoy 19 | retval: OK 20 | *** exiting com.test.flyer.MainActivity.test 21 | retval: undefined 22 | *** exiting com.test.flyer.MainActivity$1.onClick 23 | ``` 24 | 截图效果: 25 | 26 | ![](https://user-gold-cdn.xitu.io/2019/6/26/16b9392baa6668e0?w=1246&h=1090&f=jpeg&s=167283) 27 | 28 | ### 改进 29 | 在这样一堆打印中,我们不能清晰的分辨各个方法的嵌套关系,总之一句话,就是不够醒目。于是乎对 raptor_frida_android_trace.js 脚本做了改进,下面是部分内容: 30 | ``` 31 | var logContentArray = new Array(); 32 | 33 | var singlePrefix = "|----" 34 | 35 | 36 | // find and trace all methods declared in a Java Class 37 | function traceClass(targetClass) 38 | { 39 | var hook = Java.use(targetClass); 40 | var methods = hook.class.getDeclaredMethods(); 41 | hook.$dispose; 42 | 43 | var parsedMethods = []; 44 | methods.forEach(function(method) { 45 | parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]); 46 | }); 47 | 48 | var targets = uniqBy(parsedMethods, JSON.stringify); 49 | targets.forEach(function(targetMethod) { 50 | traceMethod(targetClass + "." + targetMethod); 51 | }); 52 | } 53 | 54 | // trace a specific Java Method 55 | function traceMethod(targetClassMethod) 56 | { 57 | var delim = targetClassMethod.lastIndexOf("."); 58 | if (delim === -1) return; 59 | 60 | // slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分 61 | var targetClass = targetClassMethod.slice(0, delim) 62 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 63 | 64 | var hook = Java.use(targetClass); 65 | var overloadCount = hook[targetMethod].overloads.length; 66 | 67 | console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]"); 68 | 69 | for (var i = 0; i < overloadCount; i++) { 70 | 71 | // hook方法 72 | hook[targetMethod].overloads[i].implementation = function() { 73 | 74 | var logContent_1 = "entered--"+targetClassMethod; 75 | 76 | var prefixStr = gainLogPrefix(logContentArray); 77 | 78 | logContentArray.push(prefixStr + logContent_1); 79 | 80 | console.warn(prefixStr + logContent_1); 81 | 82 | // print backtrace, 打印调用堆栈 83 | // Java.perform(function() { 84 | // var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()); 85 | // console.log(prefixStr +"Backtrace:" + bt); 86 | // }); 87 | 88 | // print args 89 | // if (arguments.length) console.log(); 90 | 91 | // 打印参数 92 | for (var j = 0; j < arguments.length; j++) 93 | { 94 | var tmpLogStr = prefixStr + "arg[" + j + "]: " + arguments[j]; 95 | console.log(tmpLogStr); 96 | logContentArray.push(tmpLogStr); 97 | } 98 | // print retval 99 | var retval = this[targetMethod].apply(this, arguments); // rare crash (Frida bug?) 100 | // 打印返回值 101 | // console.log("\n"+ targetClassMethod +"--retval: " + retval); 102 | var tmpReturnStr = prefixStr + "retval: " + retval; 103 | logContentArray.push(tmpReturnStr); 104 | console.log(tmpReturnStr); 105 | //结束标志 106 | var logContent_ex = "exiting--" + targetClassMethod; 107 | logContentArray.push(prefixStr + logContent_ex); 108 | console.warn(prefixStr + logContent_ex); 109 | return retval; 110 | } 111 | } 112 | } 113 | 114 | // 获取打印前缀 115 | function gainLogPrefix(theArray) 116 | { 117 | var lastIndex = theArray.length - 1; 118 | 119 | if (lastIndex<0) 120 | { 121 | return singlePrefix; 122 | } 123 | 124 | for (var i = lastIndex; i >= 0; i--) 125 | { 126 | var tmpLogContent = theArray[i]; 127 | var cIndex = tmpLogContent.indexOf("entered--"); 128 | 129 | if ( cIndex == -1) 130 | { 131 | var cIndex2 = tmpLogContent.indexOf("exiting--"); 132 | if ( cIndex2 == -1) 133 | { 134 | continue; 135 | } 136 | else 137 | { 138 | //与上个方法平级 139 | var resultStr = tmpLogContent.slice(0,cIndex2); 140 | return resultStr; 141 | } 142 | } 143 | else 144 | { 145 | //在上个方法的内部 146 | var resultStr = singlePrefix + tmpLogContent.slice(0,cIndex);//replace(/entered--/, ""); 147 | // console.log("("+tmpLogContent+")前缀是:("+resultStr+")"); 148 | return resultStr; 149 | 150 | } 151 | } 152 | return ""; 153 | } 154 | 155 | // usage examples 156 | setTimeout(function() { // avoid java.lang.ClassNotFoundException 157 | 158 | Java.perform(function() { 159 | 160 | trace("com.test.flyer.MainActivity"); 161 | // trace("com.test.flyer.MainActivity.gainAge"); 162 | 163 | // trace("com.target.utils.CryptoUtils.decrypt"); 164 | // trace("com.target.utils.CryptoUtils"); 165 | // trace("CryptoUtils"); 166 | // trace(/crypto/i); 167 | // trace("exports:*!open*"); 168 | 169 | }); 170 | }, 0); 171 | ``` 172 | js文件内,主要新增 gainLogPrefix方法和 gainLogPrefix_Module方法,用于生成"|----"标记。 173 | 174 | 使用新的js追踪方法,终端打印如下: 175 | ``` 176 | |----entered--com.test.flyer.MainActivity$1.onClick 177 | |----arg[0]: android.support.v7.widget.AppCompatButton{426a5f40 VFED..C. ...P.... 0,0-264,144 #7f070022 app:id/button} 178 | |----|----entered--com.test.flyer.MainActivity.test 179 | |----|----arg[0]: Jack 180 | |----|----|----entered--com.test.flyer.MainActivity.gainAge 181 | |----|----|----arg[0]: 16 182 | |----|----|----retval: 26 183 | |----|----|----exiting--com.test.flyer.MainActivity.gainAge 184 | |----|----|----entered--com.test.flyer.MainActivity.gainEnjoy 185 | |----|----|----arg[0]: 篮球 186 | |----|----|----retval: 我喜欢篮球 187 | |----|----|----exiting--com.test.flyer.MainActivity.gainEnjoy 188 | |----|----retval: OK 189 | |----|----exiting--com.test.flyer.MainActivity.test 190 | |----retval: undefined 191 | |----exiting--com.test.flyer.MainActivity$1.onClick 192 | ``` 193 | 截图效果: 194 | 195 | ![](https://user-gold-cdn.xitu.io/2019/6/26/16b938ca5fe26c74?w=1642&h=738&f=jpeg&s=240850) 196 | 197 | 看起来顺眼多了,哈哈哈,希望各位大佬能喜欢。 198 | 199 | ### 结束 200 | 本次测试项目和js脚本获取方式: 201 | 202 | 1、关注公众号"逆向APP",回复"frida追踪方法02" 203 | ![](https://user-gold-cdn.xitu.io/2019/6/26/16b9397aa15a907a?w=258&h=258&f=jpeg&s=27708) 204 | 205 | 2、[github地址](https://github.com/luoyanbei/android_frida_trace_myTrace),可以帮忙点个 Star 206 | -------------------------------------------------------------------------------- /TestAppsFlyer/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /TestAppsFlyer/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /TestAppsFlyer/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /TestAppsFlyer/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /TestAppsFlyer/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.example.king.testappsflyer" 7 | minSdkVersion 15 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:28.0.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | } 29 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/src/androidTest/java/com/example/king/testappsflyer/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.king.testappsflyer; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals( "com.example.king.testappsflyer", appContext.getPackageName() ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/src/main/java/com/test/flyer/InstallReceiver.java: -------------------------------------------------------------------------------- 1 | package com.test.flyer; 2 | 3 | import java.util.Timer; 4 | import java.util.TimerTask; 5 | 6 | import android.content.BroadcastReceiver; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.util.Log; 10 | 11 | import java.util.logging.Handler; 12 | import java.util.logging.LogRecord; 13 | 14 | /** 15 | * 广告显示进行安装监听进行上报 16 | */ 17 | 18 | public class InstallReceiver extends BroadcastReceiver { 19 | 20 | private static String TAG = "InstallReceiver"; 21 | private Context mContext; 22 | 23 | /** 24 | * 25 | * 26 | * @param context 27 | * @param intent 28 | */ 29 | @Override 30 | public void onReceive(Context context, Intent intent) { 31 | 32 | 33 | 34 | } 35 | 36 | private void send(String packageName, String referrer) { 37 | 38 | } 39 | 40 | 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/src/main/java/com/test/flyer/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.flyer; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.Toast; 10 | 11 | import com.example.king.testappsflyer.R; 12 | 13 | public class MainActivity extends AppCompatActivity { 14 | 15 | private Button testBtn; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate( savedInstanceState ); 20 | setContentView( R.layout.activity_main ); 21 | 22 | testBtn = (Button) findViewById(R.id.button); 23 | testBtn.setOnClickListener( new View.OnClickListener() { 24 | @Override 25 | public void onClick(View v) { 26 | Log.d( "test", "do--click--testBtn" ); 27 | String text = test("Jack"); 28 | Log.d( "test", "do--test--return= "+text ); 29 | // send("step.tracker.stepcounter.walking", "utm_source=google-play&utm_medium=WJ"); 30 | 31 | } 32 | } ); 33 | 34 | } 35 | 36 | private void send(String packageName, String referrer) { 37 | Intent intent = new Intent(); 38 | intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 39 | intent.putExtra("referrer", referrer); 40 | intent.setAction("com.android.vending.INSTALL_REFERRER"); 41 | intent.setPackage(packageName); 42 | this.sendBroadcast(intent); 43 | Log.d("InstallReceiver", "sendBroadcast: success"); 44 | } 45 | 46 | 47 | public String test(String name) { 48 | Log.d("test", "do--test"); 49 | int age = gainAge( 16 ); 50 | String enjoy = gainEnjoy("篮球"); 51 | Toast toast=Toast.makeText(MainActivity.this, "do--test--success--"+name+"--age="+age, Toast.LENGTH_LONG); 52 | //显示toast信息 53 | toast.show(); 54 | return "OK"; 55 | } 56 | 57 | 58 | private int gainAge(int age) { 59 | 60 | Log.d("test", "do--gainAge--age= "+age); 61 | 62 | return age+10; 63 | 64 | } 65 | 66 | private String gainEnjoy(String enjoy) { 67 | 68 | Log.d("test", "do--gainEnjoy--enjoy= "+enjoy); 69 | 70 | return "我喜欢"+enjoy; 71 | 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /TestAppsFlyer/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 |