SlideShare a Scribd company logo
|
Dependency Library injection
How to extend any app without a single line of code.
|
Przemek Jakubczyk
Android Technical Lead
Applause
|
Motivation
• Applause ships its SDK which monitors app state
• Often clients don’t want to mess with development process
• Product/Project owner can do it himself
• Or to configure SDK different way
|
The hack
|
Build chain (source)
Dalvik Executable
(DEX)
|
Build chain (resources)
Resources
binary container
|
Disassemble apk
Dex compiled classes
Binary resources
Smali files aka source code
PNG assets
Other xml based resources
|
Assemble back apk
Dex compiled classes
Binary resources
Smali files aka source code
PNG assets
Other xml based resources
|
Code example (Java)
src/main/res/values/strings.xml
<resources>
...
<string name="warning">Warning</string>
</resources>
src/main/java/com/example/MainActivity.java
import com.example.R;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
Toast.makeText(this, R.string.warning, Toast.LENGTH_SHORT).show();
}
|
Code example (smali)
.method protected onCreate(Landroid/os/Bundle;)V
...
# string constant number
const v0, 0x7f060001
# second Toast parameter
const/4 v1, 0x0
invoke-static {p0, v0, v1}, Landroid/widget/Toast;-
>makeText(Landroid/content/Context;II)Landroid/widget/Toast;
...
.end method
|
<resources>
<string name="warning">Warning</string>
<string name="no_worries">No worries</string>
</resources>
Add new resource
res/values/strings.xml
<resources>
...
<public type="string" name="warning" id="0x7f060001" />
<public type="string" name="no_worries" id="0x7f060002" />
</resources>
res/values/public.xml
|
Modify Smali code
.method protected onCreate(Landroid/os/Bundle;)V
...
# string constant number
const v0, 0x7f060002
# second Toast parameter
const/4 v1, 0x0
invoke-static {p0, v0, v1}, Landroid/widget/Toast;-
>makeText(Landroid/content/Context;II)Landroid/widget/Toast;
...
.end method
|
Let’s do it
We have:
Binary Library Snippet
|
Steps
1.Decompile and unpack
2.Resources
3.Android Manifest
4.Merge codebase
5.Find place to run the snippet
6.Paste the snippet
7.Sign the binary
8.Limitations
|
apktool d pola.apk
Decompile and unpack
unzip -d library lib.aar
|
Steps
1.Decompile and unpack
2.Resources
3.Android Manifest
4.Merge codebase
5.Find place to run the snippet
6.Paste the snippet
7.Sign the binary
8.Limitations
|
cp -r library/res pola-debug/res
Resources
|
Resources - extend references table
pola-debug/res/values/public.xml
<resources>
<public type="layout" name="zxing_capture" id="0x7f04002e" />
<public type="layout" name="first_library_layout" id="0x7f03002f" />
...
<public type="string" name="yes" id="0x7f080045" />
<public type="string" name="first_library_string" id="0x7f080046" />
...
<public type="id" name="action_twitter" id="0x7f0e0099" />
<public type="id" name="first_library_id" id="0x7f0e009a" />
...
</resources>
|
Resources - extend R.java
pola/smali/pl/pola_app/R$string.smali
.class public final Lpl/pola_app/R$string;
.super Ljava/lang/Object;
.source "R.java"
# static fields
...
.field public static final zxing_capture:I = 0x7f080045
.field public static final first_library_string:I = 0x7f03002f
|
While compiling a normal application all fields in R.java are
public static final
Resources - digression
CreateReportActivity.smali
const v2, 0x7f080027
R$string.java
public static int dialog_delete_photo = 0x7f080027;
Java compiler treats them as constants and inserts a value instead of
reference
|
R$string.java
public static int library_titile=0x7f0500d3;
Resources - digression
sget v1, Lcom/example/R$string;->library_titile:I
In library R.java all fields are public static not final! This means that
compiler won’t treat them as constant and will leave references.
R$string.smali
.field public static library_titile:I = 0x7f050021
|
• No need to modify library code referencing resources.
• While compile a normal application all fields in R.java are public static final
so java compiler treats them as constans and inserts a value instead of
reference.
R.string.zxing_capture -> 0x7f080045
• In library R.java all fields are public static not final! This means that
compiler won’t treat them as constant and will leave references.
• R.string.first_library_string -> R.string.first_library_string
Resources
|
Steps
1.Decompile and unpack
2.Resources
3.Android Manifest
4.Merge codebase
5.Find place to run the snippet
6.Paste the snippet
7.Sign the binary
8.Limitations
|
Android Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pl.pola_app"
platformBuildVersionCode="23" platformBuildVersionName="6.0-2704002">
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"/>
<activity android:name="pl.pola_app.ui.activity.MainActivity"/>
...
<activity android:name="com.mylibrary.HelloActivity"/>
</application>
</manifest>
|
Steps
1.Decompile and unpack
2.Resources
3.Android Manifest
4.Merge codebase
5.Find place to run the snippet
6.Paste the snippet
7.Sign the binary
8.Limitations
|
• Aar container has classes
• Use dx --dex to convert classes to .dex
• And use smali.jar to convert to .smali
• Merge two smali dirs
Merge codebase
|
Steps
1.Decompile and unpack
2.Resources
3.Android Manifest
4.Merge codebase
5.Find place to run the snippet
6.Paste the snippet
7.Sign the binary
8.Limitations
|
• Having all compiled code in one place is a half-success.
• We could use apktool to build back the binary but our code isn’t referenced
an application’s code.
• Our business requirement was to start Applause as early as it’s possible.
• android.app.Application.onCreate()
Find place to run the snippet
|
Find place to run the snippet
app-debug/AndroidManifest.xml
<manifest package="pl.pola_app" >
...
<application
android:name="pl.pola_app.PolaApplication"
... />
...
</application>
</manifest>
app-debug/smali/pl/pola_app/PolaApplication.smali
|
Steps
1.Decompile and unpack
2.Resources
3.Android Manifest
4.Merge codebase
5.Find place to run the snippet
6.Paste the snippet
7.Sign the binary
8.Limitations
|
• Change onCreate method
• Paste our code before applications’ code
• Let’s check Pola’s code
Paste the snippet - method I
31
|
@Override public void onCreate() {
super.onCreate();
component = PolaComponent.Initializer.init(this);
if(BuildConfig.USE_CRASHLYTICS) {
Fabric.with(this, new Crashlytics());
}
ButterKnife.setDebug(BuildConfig.DEBUG);
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
} else {
Timber.plant(new CrashReportingTree());
}
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(Utils.TIMEOUT_SECONDS, TimeUnit.SECONDS);
client.setReadTimeout(Utils.TIMEOUT_SECONDS, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.baseUrl(this.getResources().getString(R.string.pola_api_url))
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
32
|
Transforms to ...
|
.method public onCreate()V
.locals 6
.prologue
const-wide/16 v4, 0x14
.line 26
invoke-super {p0}, Landroid/app/Application;->onCreate()V
.line 28
invoke-static {p0}, Lpl/pola_app/internal/di/PolaComponent$Initializer;-
>init(Lpl/pola_app/PolaApplication;)Lpl/pola_app/internal/di/PolaComponent;
move-result-object v1
iput-object v1, p0, Lpl/pola_app/PolaApplication;->component:Lpl/pola_app/internal/di/PolaComponent;
.line 32
sget-boolean v1, Lpl/pola_app/BuildConfig;->DEBUG:Z
invoke-static {v1}, Lbutterknife/ButterKnife;->setDebug(Z)V
Smali #1
34
|
.line 34
sget-boolean v1, Lpl/pola_app/BuildConfig;->DEBUG:Z
if-eqz v1, :cond_0
.line 35
new-instance v1, Ltimber/log/Timber$DebugTree;
invoke-direct {v1}, Ltimber/log/Timber$DebugTree;-><init>()V
invoke-static {v1}, Ltimber/log/Timber;->plant(Ltimber/log/Timber$Tree;)V
.line 40
:goto_0
new-instance v0, Lcom/squareup/okhttp/OkHttpClient;
invoke-direct {v0}, Lcom/squareup/okhttp/OkHttpClient;-><init>()V
.line 41
.local v0, "client":Lcom/squareup/okhttp/OkHttpClient;
sget-object v1, Ljava/util/concurrent/TimeUnit;->SECONDS:Ljava/util/concurrent/TimeUnit;
invoke-virtual {v0, v4, v5, v1}, Lcom/squareup/okhttp/OkHttpClient;-
>setConnectTimeout(JLjava/util/concurrent/TimeUnit;)V
Smali #2
35
|
.line 42
sget-object v1, Ljava/util/concurrent/TimeUnit;->SECONDS:Ljava/util/concurrent/TimeUnit;
invoke-virtual {v0, v4, v5, v1}, Lcom/squareup/okhttp/OkHttpClient;-
>setReadTimeout(JLjava/util/concurrent/TimeUnit;)V
.line 44
new-instance v1, Lretrofit/Retrofit$Builder;
invoke-direct {v1}, Lretrofit/Retrofit$Builder;-><init>()V
.line 45
invoke-virtual {p0}, Lpl/pola_app/PolaApplication;->getResources()Landroid/content/res/Resources;
move-result-object v2
const v3, 0x7f08002f
invoke-virtual {v2, v3}, Landroid/content/res/Resources;->getString(I)Ljava/lang/String;
move-result-object v2
invoke-virtual {v1, v2}, Lretrofit/Retrofit$Builder;-
>baseUrl(Ljava/lang/String;)Lretrofit/Retrofit$Builder;
move-result-object v1
Smali #3
36
|
.line 46
invoke-static {}, Lretrofit/GsonConverterFactory;->create()Lretrofit/GsonConverterFactory;
move-result-object v2
invoke-virtual {v1, v2}, Lretrofit/Retrofit$Builder;-
>addConverterFactory(Lretrofit/Converter$Factory;)Lretrofit/Retrofit$Builder;
move-result-object v1
.line 47
invoke-virtual {v1, v0}, Lretrofit/Retrofit$Builder;-
>client(Lcom/squareup/okhttp/OkHttpClient;)Lretrofit/Retrofit$Builder;
move-result-object v1
.line 48
invoke-virtual {v1}, Lretrofit/Retrofit$Builder;->build()Lretrofit/Retrofit;
move-result-object v1
sput-object v1, Lpl/pola_app/PolaApplication;->retrofit:Lretrofit/Retrofit;
Smali #4
37
|
.line 49
return-void
.line 37
.end local v0 # "client":Lcom/squareup/okhttp/OkHttpClient;
:cond_0
new-instance v1, Lpl/pola_app/PolaApplication$CrashReportingTree;
const/4 v2, 0x0
invoke-direct {v1, v2}, Lpl/pola_app/PolaApplication$CrashReportingTree;-
><init>(Lpl/pola_app/PolaApplication$1;)V
invoke-static {v1}, Ltimber/log/Timber;->plant(Ltimber/log/Timber$Tree;)V
goto :goto_0
.end method
Smali #5
38
|
• Paste Applause.startNewSession(context, “app_key”) smali
equivalent
• Would be easy ...
• But need to be very careful on smali registries - variable, fields, params
Paste the snippet - method I
|
• Replace application:name in
Android Manifest to
LibraryApplication
Paste the snippet - method II
import android.app.Application
class LibraryApplication extends
Application {}
import pl.pola_app.PolaApplication
class LibraryApplication extends
PolaApplication {}
• Change base class of our
LibraryApplication to one
found in original Android
Manifest
• Ensure all super calls
<application
android:name=".PolApplication”
... >
<application
android:name=".LibraryApplication”
... >
|
• Replace super class in header
to LibraryApplication
Paste the snippet - method II (PolaApplication.smali)
.method public onCreate()V
.line 26
invoke-super {p0},
Landroid/app/Application;
->onCreate()V
.method public onCreate()V
.line 26
invoke-super {p0},
Lcom/example/LibraryApplication;
->onCreate()V
• Change all super calls
.class public
Lpl/pola_app/PolaApplication;
.super Landroid/app/Application;
.source "PolaApplication.java"
.class public
Lpl/pola_app/PolaApplication;
.super Lcom/example/LibraryApplication;
.source "PolaApplication.java"
|
Steps
1.Decompile and unpack
2.Resources
3.Android Manifest
4.Merge codebase
5.Find place to run the snippet
6.Paste the snippet
7.Sign the binary
8.Limitations
|
• The seal is broken
Sign the binary
md5(“pola.apk”) != md5(“new_pola.apk”)
• I am using a dummy certificate in order to run the binary on device
• However some of the main application functionalities won’t work
• For example Google Service are checking if certificate’s fingerprint equals
the one set in developer console.
|
Steps
1.Decompile and unpack
2.Resources
3.Android Manifest
4.Merge codebase
5.Find place to run the snippet
6.Paste the snippet
7.Sign the binary
8.Limitations
|
• 64k method limit. Adding your library might hit the ceiling. A semi-solution
is the check if app supports multi-dex and utilize more smali dirs
• Custom attributes are compiled a bit different. We’ve agreed not to use it to
have simpler instrumentation script.
• Dexguard uses not genuine aapt compiler where apktool uses the one
from Google which is more strict with file naming.
Limitations
|
Run and enjoy!
|
Questions?
|
THANK YOU
Thanks
and catch me on evening beer session

More Related Content

PDF
Practices and tools for building better API (JFall 2013)
PDF
Design functional solutions in Java, a practical example
PPTX
Eclipse 40 and Eclipse e4
KEY
Working Effectively With Legacy Code
PPTX
Scala Italy 2015 - Hands On ScalaJS
PDF
Advanced iOS Debbuging (Reloaded)
PPTX
Practices and Tools for Building Better APIs
PDF
Top ten of PHP 7.4
Practices and tools for building better API (JFall 2013)
Design functional solutions in Java, a practical example
Eclipse 40 and Eclipse e4
Working Effectively With Legacy Code
Scala Italy 2015 - Hands On ScalaJS
Advanced iOS Debbuging (Reloaded)
Practices and Tools for Building Better APIs
Top ten of PHP 7.4

What's hot (20)

PDF
Sling Models Using Sightly and JSP by Deepak Khetawat
PDF
Java Libraries You Can't Afford To Miss
PPTX
AEM and Sling
PDF
slingmodels
PDF
Titanium - Making the most of your single thread
PDF
High quality ap is with api platform
PDF
Best Practices in apps development with Titanium Appcelerator
PPTX
Parallel and Async Programming With C#
PDF
What you need to know about Lambdas - Jamie Allen
PPTX
Understanding ASP.NET Under The Cover - Miguel A. Castro
PDF
Reverse engineering and instrumentation of android apps
PPTX
Mastering the Sling Rewriter
ODP
Java Code Generation for Productivity
PPTX
PHP: GraphQL consistency through code generation
PDF
JAX-RS and CDI Bike the (Reactive) Bridge
PDF
Angular Intermediate
PPTX
Building Large Scale PHP Web Applications with Laravel 4
PPTX
Alteryx SDK
PPTX
Sling models by Justin Edelson
PDF
How Symfony Changed My Life
Sling Models Using Sightly and JSP by Deepak Khetawat
Java Libraries You Can't Afford To Miss
AEM and Sling
slingmodels
Titanium - Making the most of your single thread
High quality ap is with api platform
Best Practices in apps development with Titanium Appcelerator
Parallel and Async Programming With C#
What you need to know about Lambdas - Jamie Allen
Understanding ASP.NET Under The Cover - Miguel A. Castro
Reverse engineering and instrumentation of android apps
Mastering the Sling Rewriter
Java Code Generation for Productivity
PHP: GraphQL consistency through code generation
JAX-RS and CDI Bike the (Reactive) Bridge
Angular Intermediate
Building Large Scale PHP Web Applications with Laravel 4
Alteryx SDK
Sling models by Justin Edelson
How Symfony Changed My Life
Ad

Similar to Android Auto instrumentation (20)

PDF
Plugin-based software design with Ruby and RubyGems
PDF
Practices and tools for building better APIs
PDF
Intro to Rack
PPTX
How to build Sdk? Best practices
PDF
React Native +Redux + ES6 (Updated)
PDF
End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...
PDF
Baruco 2014 - Rubymotion Workshop
PDF
Cocoapods and Most common used library in Swift
PPTX
API workshop: Introduction to APIs (TC Camp)
KEY
Play Support in Cloud Foundry
PPTX
PPTX
Using advanced C# features in Sharepoint development
PDF
Full Stack React Workshop [CSSC x GDSC]
PPTX
React django
PDF
Divide and Conquer – Microservices with Node.js
PDF
Nike pop up habitat
PPTX
Java introduction
PPTX
Lecture 1 Introduction to React Native.pptx
PDF
BEST PRACTICES PER LA SCRITTURA DI APPLICAZIONI TITANIUM APPCELERATOR - Aless...
PPTX
Reactive application using meteor
Plugin-based software design with Ruby and RubyGems
Practices and tools for building better APIs
Intro to Rack
How to build Sdk? Best practices
React Native +Redux + ES6 (Updated)
End to end testing Single Page Apps & APIs with Cucumber.js and Puppeteer (Em...
Baruco 2014 - Rubymotion Workshop
Cocoapods and Most common used library in Swift
API workshop: Introduction to APIs (TC Camp)
Play Support in Cloud Foundry
Using advanced C# features in Sharepoint development
Full Stack React Workshop [CSSC x GDSC]
React django
Divide and Conquer – Microservices with Node.js
Nike pop up habitat
Java introduction
Lecture 1 Introduction to React Native.pptx
BEST PRACTICES PER LA SCRITTURA DI APPLICAZIONI TITANIUM APPCELERATOR - Aless...
Reactive application using meteor
Ad

More from Przemek Jakubczyk (10)

PPTX
Droidcon Berlin Barcamp 2016
PPTX
It's always your fault. Poznań ADG 2016
PPTX
RoboSpock Poznań ADG 2016
PPTX
It's always your fault
PPTX
PPTX
How to recognise that the user has just uninstalled your android app
PPTX
How to recognise that the user has just uninstalled your android app droidc...
PPTX
Uninstall opera
PPTX
Android accounts & sync
PPTX
Robospock droidcon '14
Droidcon Berlin Barcamp 2016
It's always your fault. Poznań ADG 2016
RoboSpock Poznań ADG 2016
It's always your fault
How to recognise that the user has just uninstalled your android app
How to recognise that the user has just uninstalled your android app droidc...
Uninstall opera
Android accounts & sync
Robospock droidcon '14

Recently uploaded (6)

DOC
证书学历UoA毕业证,澳大利亚中汇学院毕业证国外大学毕业证
PDF
Lesson 13- HEREDITY _ pedSAWEREGFVCXZDSASEWFigree.pdf
PPTX
Introduction to Packet Tracer Course Overview - Aug 21 (1).pptx
PPTX
ASMS Telecommunication company Profile
DOC
Camb毕业证学历认证,格罗斯泰斯特主教大学毕业证仿冒文凭毕业证
PDF
6-UseCfgfhgfhgfhgfhgfhfhhaseActivity.pdf
证书学历UoA毕业证,澳大利亚中汇学院毕业证国外大学毕业证
Lesson 13- HEREDITY _ pedSAWEREGFVCXZDSASEWFigree.pdf
Introduction to Packet Tracer Course Overview - Aug 21 (1).pptx
ASMS Telecommunication company Profile
Camb毕业证学历认证,格罗斯泰斯特主教大学毕业证仿冒文凭毕业证
6-UseCfgfhgfhgfhgfhgfhfhhaseActivity.pdf

Android Auto instrumentation

  • 1. | Dependency Library injection How to extend any app without a single line of code.
  • 3. | Motivation • Applause ships its SDK which monitors app state • Often clients don’t want to mess with development process • Product/Project owner can do it himself • Or to configure SDK different way
  • 5. | Build chain (source) Dalvik Executable (DEX)
  • 7. | Disassemble apk Dex compiled classes Binary resources Smali files aka source code PNG assets Other xml based resources
  • 8. | Assemble back apk Dex compiled classes Binary resources Smali files aka source code PNG assets Other xml based resources
  • 9. | Code example (Java) src/main/res/values/strings.xml <resources> ... <string name="warning">Warning</string> </resources> src/main/java/com/example/MainActivity.java import com.example.R; @Override protected void onCreate(Bundle savedInstanceState) { ... Toast.makeText(this, R.string.warning, Toast.LENGTH_SHORT).show(); }
  • 10. | Code example (smali) .method protected onCreate(Landroid/os/Bundle;)V ... # string constant number const v0, 0x7f060001 # second Toast parameter const/4 v1, 0x0 invoke-static {p0, v0, v1}, Landroid/widget/Toast;- >makeText(Landroid/content/Context;II)Landroid/widget/Toast; ... .end method
  • 11. | <resources> <string name="warning">Warning</string> <string name="no_worries">No worries</string> </resources> Add new resource res/values/strings.xml <resources> ... <public type="string" name="warning" id="0x7f060001" /> <public type="string" name="no_worries" id="0x7f060002" /> </resources> res/values/public.xml
  • 12. | Modify Smali code .method protected onCreate(Landroid/os/Bundle;)V ... # string constant number const v0, 0x7f060002 # second Toast parameter const/4 v1, 0x0 invoke-static {p0, v0, v1}, Landroid/widget/Toast;- >makeText(Landroid/content/Context;II)Landroid/widget/Toast; ... .end method
  • 13. | Let’s do it We have: Binary Library Snippet
  • 14. | Steps 1.Decompile and unpack 2.Resources 3.Android Manifest 4.Merge codebase 5.Find place to run the snippet 6.Paste the snippet 7.Sign the binary 8.Limitations
  • 15. | apktool d pola.apk Decompile and unpack unzip -d library lib.aar
  • 16. | Steps 1.Decompile and unpack 2.Resources 3.Android Manifest 4.Merge codebase 5.Find place to run the snippet 6.Paste the snippet 7.Sign the binary 8.Limitations
  • 17. | cp -r library/res pola-debug/res Resources
  • 18. | Resources - extend references table pola-debug/res/values/public.xml <resources> <public type="layout" name="zxing_capture" id="0x7f04002e" /> <public type="layout" name="first_library_layout" id="0x7f03002f" /> ... <public type="string" name="yes" id="0x7f080045" /> <public type="string" name="first_library_string" id="0x7f080046" /> ... <public type="id" name="action_twitter" id="0x7f0e0099" /> <public type="id" name="first_library_id" id="0x7f0e009a" /> ... </resources>
  • 19. | Resources - extend R.java pola/smali/pl/pola_app/R$string.smali .class public final Lpl/pola_app/R$string; .super Ljava/lang/Object; .source "R.java" # static fields ... .field public static final zxing_capture:I = 0x7f080045 .field public static final first_library_string:I = 0x7f03002f
  • 20. | While compiling a normal application all fields in R.java are public static final Resources - digression CreateReportActivity.smali const v2, 0x7f080027 R$string.java public static int dialog_delete_photo = 0x7f080027; Java compiler treats them as constants and inserts a value instead of reference
  • 21. | R$string.java public static int library_titile=0x7f0500d3; Resources - digression sget v1, Lcom/example/R$string;->library_titile:I In library R.java all fields are public static not final! This means that compiler won’t treat them as constant and will leave references. R$string.smali .field public static library_titile:I = 0x7f050021
  • 22. | • No need to modify library code referencing resources. • While compile a normal application all fields in R.java are public static final so java compiler treats them as constans and inserts a value instead of reference. R.string.zxing_capture -> 0x7f080045 • In library R.java all fields are public static not final! This means that compiler won’t treat them as constant and will leave references. • R.string.first_library_string -> R.string.first_library_string Resources
  • 23. | Steps 1.Decompile and unpack 2.Resources 3.Android Manifest 4.Merge codebase 5.Find place to run the snippet 6.Paste the snippet 7.Sign the binary 8.Limitations
  • 24. | Android Manifest <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pl.pola_app" platformBuildVersionCode="23" platformBuildVersionName="6.0-2704002"> ... <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name"/> <activity android:name="pl.pola_app.ui.activity.MainActivity"/> ... <activity android:name="com.mylibrary.HelloActivity"/> </application> </manifest>
  • 25. | Steps 1.Decompile and unpack 2.Resources 3.Android Manifest 4.Merge codebase 5.Find place to run the snippet 6.Paste the snippet 7.Sign the binary 8.Limitations
  • 26. | • Aar container has classes • Use dx --dex to convert classes to .dex • And use smali.jar to convert to .smali • Merge two smali dirs Merge codebase
  • 27. | Steps 1.Decompile and unpack 2.Resources 3.Android Manifest 4.Merge codebase 5.Find place to run the snippet 6.Paste the snippet 7.Sign the binary 8.Limitations
  • 28. | • Having all compiled code in one place is a half-success. • We could use apktool to build back the binary but our code isn’t referenced an application’s code. • Our business requirement was to start Applause as early as it’s possible. • android.app.Application.onCreate() Find place to run the snippet
  • 29. | Find place to run the snippet app-debug/AndroidManifest.xml <manifest package="pl.pola_app" > ... <application android:name="pl.pola_app.PolaApplication" ... /> ... </application> </manifest> app-debug/smali/pl/pola_app/PolaApplication.smali
  • 30. | Steps 1.Decompile and unpack 2.Resources 3.Android Manifest 4.Merge codebase 5.Find place to run the snippet 6.Paste the snippet 7.Sign the binary 8.Limitations
  • 31. | • Change onCreate method • Paste our code before applications’ code • Let’s check Pola’s code Paste the snippet - method I 31
  • 32. | @Override public void onCreate() { super.onCreate(); component = PolaComponent.Initializer.init(this); if(BuildConfig.USE_CRASHLYTICS) { Fabric.with(this, new Crashlytics()); } ButterKnife.setDebug(BuildConfig.DEBUG); if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } else { Timber.plant(new CrashReportingTree()); } OkHttpClient client = new OkHttpClient(); client.setConnectTimeout(Utils.TIMEOUT_SECONDS, TimeUnit.SECONDS); client.setReadTimeout(Utils.TIMEOUT_SECONDS, TimeUnit.SECONDS); retrofit = new Retrofit.Builder() .baseUrl(this.getResources().getString(R.string.pola_api_url)) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build(); } 32
  • 34. | .method public onCreate()V .locals 6 .prologue const-wide/16 v4, 0x14 .line 26 invoke-super {p0}, Landroid/app/Application;->onCreate()V .line 28 invoke-static {p0}, Lpl/pola_app/internal/di/PolaComponent$Initializer;- >init(Lpl/pola_app/PolaApplication;)Lpl/pola_app/internal/di/PolaComponent; move-result-object v1 iput-object v1, p0, Lpl/pola_app/PolaApplication;->component:Lpl/pola_app/internal/di/PolaComponent; .line 32 sget-boolean v1, Lpl/pola_app/BuildConfig;->DEBUG:Z invoke-static {v1}, Lbutterknife/ButterKnife;->setDebug(Z)V Smali #1 34
  • 35. | .line 34 sget-boolean v1, Lpl/pola_app/BuildConfig;->DEBUG:Z if-eqz v1, :cond_0 .line 35 new-instance v1, Ltimber/log/Timber$DebugTree; invoke-direct {v1}, Ltimber/log/Timber$DebugTree;-><init>()V invoke-static {v1}, Ltimber/log/Timber;->plant(Ltimber/log/Timber$Tree;)V .line 40 :goto_0 new-instance v0, Lcom/squareup/okhttp/OkHttpClient; invoke-direct {v0}, Lcom/squareup/okhttp/OkHttpClient;-><init>()V .line 41 .local v0, "client":Lcom/squareup/okhttp/OkHttpClient; sget-object v1, Ljava/util/concurrent/TimeUnit;->SECONDS:Ljava/util/concurrent/TimeUnit; invoke-virtual {v0, v4, v5, v1}, Lcom/squareup/okhttp/OkHttpClient;- >setConnectTimeout(JLjava/util/concurrent/TimeUnit;)V Smali #2 35
  • 36. | .line 42 sget-object v1, Ljava/util/concurrent/TimeUnit;->SECONDS:Ljava/util/concurrent/TimeUnit; invoke-virtual {v0, v4, v5, v1}, Lcom/squareup/okhttp/OkHttpClient;- >setReadTimeout(JLjava/util/concurrent/TimeUnit;)V .line 44 new-instance v1, Lretrofit/Retrofit$Builder; invoke-direct {v1}, Lretrofit/Retrofit$Builder;-><init>()V .line 45 invoke-virtual {p0}, Lpl/pola_app/PolaApplication;->getResources()Landroid/content/res/Resources; move-result-object v2 const v3, 0x7f08002f invoke-virtual {v2, v3}, Landroid/content/res/Resources;->getString(I)Ljava/lang/String; move-result-object v2 invoke-virtual {v1, v2}, Lretrofit/Retrofit$Builder;- >baseUrl(Ljava/lang/String;)Lretrofit/Retrofit$Builder; move-result-object v1 Smali #3 36
  • 37. | .line 46 invoke-static {}, Lretrofit/GsonConverterFactory;->create()Lretrofit/GsonConverterFactory; move-result-object v2 invoke-virtual {v1, v2}, Lretrofit/Retrofit$Builder;- >addConverterFactory(Lretrofit/Converter$Factory;)Lretrofit/Retrofit$Builder; move-result-object v1 .line 47 invoke-virtual {v1, v0}, Lretrofit/Retrofit$Builder;- >client(Lcom/squareup/okhttp/OkHttpClient;)Lretrofit/Retrofit$Builder; move-result-object v1 .line 48 invoke-virtual {v1}, Lretrofit/Retrofit$Builder;->build()Lretrofit/Retrofit; move-result-object v1 sput-object v1, Lpl/pola_app/PolaApplication;->retrofit:Lretrofit/Retrofit; Smali #4 37
  • 38. | .line 49 return-void .line 37 .end local v0 # "client":Lcom/squareup/okhttp/OkHttpClient; :cond_0 new-instance v1, Lpl/pola_app/PolaApplication$CrashReportingTree; const/4 v2, 0x0 invoke-direct {v1, v2}, Lpl/pola_app/PolaApplication$CrashReportingTree;- ><init>(Lpl/pola_app/PolaApplication$1;)V invoke-static {v1}, Ltimber/log/Timber;->plant(Ltimber/log/Timber$Tree;)V goto :goto_0 .end method Smali #5 38
  • 39. | • Paste Applause.startNewSession(context, “app_key”) smali equivalent • Would be easy ... • But need to be very careful on smali registries - variable, fields, params Paste the snippet - method I
  • 40. | • Replace application:name in Android Manifest to LibraryApplication Paste the snippet - method II import android.app.Application class LibraryApplication extends Application {} import pl.pola_app.PolaApplication class LibraryApplication extends PolaApplication {} • Change base class of our LibraryApplication to one found in original Android Manifest • Ensure all super calls <application android:name=".PolApplication” ... > <application android:name=".LibraryApplication” ... >
  • 41. | • Replace super class in header to LibraryApplication Paste the snippet - method II (PolaApplication.smali) .method public onCreate()V .line 26 invoke-super {p0}, Landroid/app/Application; ->onCreate()V .method public onCreate()V .line 26 invoke-super {p0}, Lcom/example/LibraryApplication; ->onCreate()V • Change all super calls .class public Lpl/pola_app/PolaApplication; .super Landroid/app/Application; .source "PolaApplication.java" .class public Lpl/pola_app/PolaApplication; .super Lcom/example/LibraryApplication; .source "PolaApplication.java"
  • 42. | Steps 1.Decompile and unpack 2.Resources 3.Android Manifest 4.Merge codebase 5.Find place to run the snippet 6.Paste the snippet 7.Sign the binary 8.Limitations
  • 43. | • The seal is broken Sign the binary md5(“pola.apk”) != md5(“new_pola.apk”) • I am using a dummy certificate in order to run the binary on device • However some of the main application functionalities won’t work • For example Google Service are checking if certificate’s fingerprint equals the one set in developer console.
  • 44. | Steps 1.Decompile and unpack 2.Resources 3.Android Manifest 4.Merge codebase 5.Find place to run the snippet 6.Paste the snippet 7.Sign the binary 8.Limitations
  • 45. | • 64k method limit. Adding your library might hit the ceiling. A semi-solution is the check if app supports multi-dex and utilize more smali dirs • Custom attributes are compiled a bit different. We’ve agreed not to use it to have simpler instrumentation script. • Dexguard uses not genuine aapt compiler where apktool uses the one from Google which is more strict with file naming. Limitations
  • 48. | THANK YOU Thanks and catch me on evening beer session

Editor's Notes

  • #4: Describe business case behind the project. Mind that it was done in Applause
  • #5: Take app from Google play Plus Take your library Produces Android application
  • #8: Why apktool where there are many others decompile tools? Works both ways and uses smali language -> text file not a binary (bytecode), easier to modify Add animation to arrows
  • #9: JVM to heap based register based jest dalvik
  • #11: A snippet from onCreate method
  • #13: A snippet from onCreate method
  • #14: Go go soldier !
  • #19: As shown before we have to extend the resource table 7f04002e Map values.xml (aar) to values.xml (apktool)
  • #20: Once resources are packed we have to address them in Java code. Android uses R.java to create the link. For strings there is R$strings.java file which has all mappings. Old_resource_name -> new value from previous step Repeat the step for each of resource types
  • #25: If your library has entries in Manifest we should add them too. Add all new Activities/Services etc. Extend permissions Merging two xml is easy.
  • #27: Apktool uses smali language as output and input. Application has been decompiled and inside there is a `smali` directory where you find all smali files. Aar contains a classes.jar - a container full of compiled java code *.class Use `dx` to compile to dex format. Use `smali` to convert dex to smali. Copy output to decompiled app `smali` dir
  • #29: Apktool uses smali language as output and input. Application has been decompiled and inside there is a `smali` directory where you find all smali files. Aar contains a classes.jar - a container full of compiled java code *.class Use `dx` to compile to dex format. Use `smali` to convert dex to smali. Copy output to decompiled app `smali` dir
  • #30: Since we have AndroidManifest.xml we can locate Application class which is going to be started by Android OS.
  • #32: Most of the apps have some kind of init in Application.onCreate() We must put our code before it Rename original onCreate() method to temp_onCreate() in order to not mess with method variables Paste smali representation of our snippet
  • #33: Most of the apps have some kind of init in Application.onCreate() We must put our code before it Rename original onCreate() method to temp_onCreate() in order to not mess with method variables Paste smali representation of our snippet
  • #35: JVM to heap based register based jest dalvik
  • #36: Most of the apps have some kind of init in Application.onCreate() We must put our code before it Rename original onCreate() method to temp_onCreate() in order to not mess with method variables Paste smali representation of our snippet
  • #37: Most of the apps have some kind of init in Application.onCreate() We must put our code before it Rename original onCreate() method to temp_onCreate() in order to not mess with method variables Paste smali representation of our snippet
  • #38: Most of the apps have some kind of init in Application.onCreate() We must put our code before it Rename original onCreate() method to temp_onCreate() in order to not mess with method variables Paste smali representation of our snippet
  • #39: Most of the apps have some kind of init in Application.onCreate() We must put our code before it Rename original onCreate() method to temp_onCreate() in order to not mess with method variables Paste smali representation of our snippet
  • #46: Compare out of an application using multidex. Classes.dex -> smali Classes2.dex -> smali2 Aar content should go to new smali dir. You code snippet should take plane in `smali` dir (first one). Classloaders and stuff