Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Зневадження Android-додатків за допомогою Steth...

GDG Ternopil
February 11, 2017

Зневадження Android-додатків за допомогою Stetho - Павло Данилюк

Зневадження Android-додатків за допомогою Stetho - Павло Данилюк

GDG Ternopil

February 11, 2017
Tweet

More Decks by GDG Ternopil

Other Decks in Programming

Transcript

  1. • ​«a sophisticated debug bridge for Android applications» • бібліотека,

    яка всередині вашого додатку піднімає HTTP-сервер для Chrome Remote Debugging Protocol + ще дещо • надає деякі корисні можливості керування станом додатку через Chrome Developer Tools • Facebook Open Source BSD 3-clause • актуальна версія: 1.4.2 перший коміт: 29.01.2015
  2. 1. compile 'com.facebook.stetho:stetho:1.4.2' 2. ​public class DebugApplication extends Application {

    @Override public void onCreate() { super.onCreate(); Stetho.initializeWithDefaults(this); } } 3. <application android:name=".DebugApplication"> 3.1. http://littlerobots.nl/blog/stetho-for-android-debug-builds-only/ 4. ​chrome://inspect
  3. 1. compile 'com.facebook.stetho:stetho-okhttp3:1.4.2' 2. ​​OkHttpClient client = new OkHttpClient.Builder() .addNetworkInterceptor(new

    StethoInterceptor()) .build(); OkHttp 3 1. compile 'com.facebook.stetho:stetho-okhttp:1.4.2' 2. ​​OkHttpClient client = new OkHttpClient(); client.networkInterceptors().add(new StethoInterceptor()); OkHttp 2.2+
  4. ​OkHttpClient client = ​//... MyApi myApi = new RestAdapter.Builder() .setClient(new

    OkClient(client)) ​ // rest of config .build() .create(MyApi.class); OkHttp + Retrofit 1. compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' 2. ​​Picasso picasso = new Picasso.Builder(context) .downloader(new OkHttp3Downloader(client)) .build(); picasso.load(url).into(view)​ /*...*/ ; OkHttp + Picasso
  5. OkHttp + Glide 1. ​compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar' compile ('com.github.bumptech.glide:okhttp3-integration:1.4.0') { exclude

    group: 'glide-parent' } 2. ​public class GlideConfig implements GlideModule { public void applyOptions(...) { /* ... */ } public void registerComponents(Context context, Glide glide) { ​OkHttpClient client = ​//... glide.register( GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client) ); } } 3. <meta-data android:name="my.app.package.GlideConfig" android:value="GlideModule" />
  6. 1. compile 'com.facebook.stetho:stetho-urlconnection:1.4.2' 2. ​// create a manager for each

    individual request ​StethoURLConnectionManager manager = new StethoURLConnectionManager("optional name"); 3. ​// setup connection as usual URL url = new URL("http://my.website.io/api/..."); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); //... 4. ​// wrap connect() with pre- and post- manager.preConnect(connection, null); connection.connect(); manager.postConnect(); 5. ​// filter received input stream through the manager inputStream = manager.interpretResponseStream( connection.getInputStream()) HttpURLConnection
  7. ​​Stetho.initializeWithDefaults(this); Stetho.initialize(Stetho.newInitializerBuilder(context) .enableWebKitInspector(new InspectorModulesProvider() { @Override public Iterable<ChromeDevtoolsDomain> get() {

    return new Stetho.DefaultInspectorModulesBuilder(context) .finish(); } }) .enableDumpapp(new DumperPluginsProvider() { @Override public Iterable<DumperPlugin> get() { return new Stetho.DefaultDumperPluginsBuilder(context) .finish(); } }) .build());
  8. Stetho.initialize(Stetho.newInitializerBuilder(context) .enableWebKitInspector(new InspectorModulesProvider() { @Override public Iterable<ChromeDevtoolsDomain> get() { return

    new Stetho.DefaultInspectorModulesBuilder(context) .provideDatabaseDriver(new CustomDriver(context)) .finish(); } }) .enableDumpapp(/*...*/) .build()); • ContentProviderDatabaseDriver Приклад: http://bit.ly/stethosample72 • Stetho-Realm https://github.com/uPhyca/stetho-realm
  9. public class CustomDriver extends Database.DatabaseDriver { public CustomDriver(Context context) {

    super(context); } public List<String> getDatabaseNames() { return Arrays.asList("1.db", "2.db"); } public List<String> getTableNames(String dbName) { return Arrays.asList("Table 1", "Table 2"); } public Database.ExecuteSQLResponse executeSQL( String dbName, String query, ExecuteResultHandler<...> handler) { // Exec the query, obtain the cursor Cursor cursor = //... // Feed the cursor to the handler return handler.handleSelect(cursor); } }
  10. final RuntimeReplFactory rf = new JsRuntimeReplFactoryBuilder(context) .build(); Stetho.initialize(Stetho.newInitializerBuilder(context) .enableWebKitInspector(new InspectorModulesProvider()

    { @Override public Iterable<ChromeDevtoolsDomain> get() { return new Stetho.DefaultInspectorModulesBuilder(context) .runtimeRepl(rf) .finish(); } }) .enableDumpapp(/*...*/) .build());
  11. final RuntimeReplFactory rf = new JsRuntimeReplFactoryBuilder(context) .build(); .importPackage("android.os") .importClass(Toast.class) .addVariable("greeting",

    "Hello GDG Ternopil!") .addFunction("showToast", new BaseFunction() { @Override public Object call(org.mozilla.javascript.Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { Handler handler = new Handler(Looper.getMainLooper()); handler.post(() -> { Toast.makeText(context, args[0].toString(), LENGTH_SHORT) .show(); }); return org.mozilla.javascript.Context.getUndefinedValue(); } })
  12. Недоліки • не найзручніше рішення • нема підказки коду •

    необхідність пам’ятати / тримати під руками заготовки коду або назви і параметри функцій • треба напружувати мозок при перемиканні між JS і Java • відсутня будь-яка оптимізація (хоча на етапі розробки байдуже) • підкачує всю Rhino у проект • +7300 методів — 13% від ліміту dex!
  13. 1. ​​public class MyCustomPlugin implements DumperPlugin { public String getName()

    { return "my1337plugin"; } public void dump(DumperContext dumperContext) throws DumpException { List<String> args = dumperContext.getArgsAsList(); PrintStream out = dumperContext.getStdout(); } } 2. ​/*...*/ .enableDumpapp(new DumperPluginsProvider() { @Override public Iterable<DumperPlugin> get() { return new Stetho.DefaultDumperPluginsBuilder(context) .provide(new myCustomPlugin()) .finish(); } }) 3. > py dumpapp my1337plugin arg0 arg1 arg2
  14. public class NotifyCursorsDumperPlugin implements DumperPlugin { private final Context mContext;

    public NotifyCursorsDumperPlugin(Context context) { mContext = context; } @Override public String getName() { return "notifycursors"; } @Override public void dump(DumperContext dumpContext) throws DumpException { ContentResolver cr = mContext.getContentResolver(); cr.notifyChange(MaterialCueContract.OverlayEntry.CONTENT_URI, null); cr.notifyChange(MaterialCueContract.SecondEntry.CONTENT_URI, null); cr.notifyChange(MaterialCueContract.ThirdEntry.CONTENT_URI, null); } }