17. public class NetworkHttpUnauthorizedReceiver extends BroadcastReceiver
{
public void onReceive(Context context, Intent intent) {
int httpCode = intent.getIntExtra(IntentExtras.HTTP_CODE, -1);
if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
LoginActivity.logOut(context);
}
}
}
Anyone can send “unauthorized”
18. Possible way to solve it
- Exported = ‘false’ in AndroidManifest.xml
- Custom permissions with
protectionLevel=”signature”
- Dynamic register with LocalBroadcastManager
24. How he did it
Static Analysis
• APKTool, Smali/Baksmali, BytecodeViewer, JEB ($), IDA Pro ($$) …
Network Analysis
• mitmproxy, charles, burpsuite, wireshark
30. Code Protection
● Name obfuscation
● String encryption
● Class encryption
● Resources, asset and native library encryption Control flow and
arithmetic obfuscation
● Hide calls through reflection
31. public String encryptSensitiveMessage() {
String nuclearLaunchCode = "abc123";
String encryptionKey = "secretkey";
return CryptoEngine.encrypt(nuclearLaunchCode, encryptionKey);
}
Example
34. public String a() {
String a = e.f("YWJjMTIz");
String b = e.f("c2VjcmV0a2V5");
Class c = Class.forName(e.f("Q3J5cHRvRW5naW5l"));
Method d = c.getMethod(e.f("ZW5jcnlwdA=="), String.class, String.class);
return (String) d.invoke(null, a, b);
}
Name obfuscation
46. message Person {
// The customer's full name.
required string name = 1;
// The customer's ID number.
required int32 id = 2;
// Email address for the customer.
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
// The user's phone number.
required string number = 1;
// The type of phone stored here.
optional PhoneType type = 2 [default = HOME];
}
// A list of the user's phone numbers.
repeated PhoneNumber phone = 4;
}
47. public final class Person extends Message {
/** The customer's full name. */
@ProtoField(tag = 1, type = STRING, label = REQUIRED)
public final String name;
/** The customer's ID number. */
@ProtoField(tag = 2, type = INT32, label = REQUIRED)
public final Integer id;
/** Email address for the customer. */
@ProtoField(tag = 3, type = STRING)
public final String email;
/** A list of the user's phone numbers. */
@ProtoField(tag = 4, label = REPEATED)
public final List<PhoneNumber> phone;
48. byte[] data = person.toByteArray();
Wire wire = new Wire();
Person newPerson = wire.parseFrom(data, Person.class);
56. Challenge
Connect the code ,key and API.
Goal: every single change in code will generate different key and this
key will be used to sign API request.
59. Reflection
Ask application with API - Who are you?
Generate challenge for each user randomly.Load it from server.
What can be challenged?
Resource IDs,
Class fields,
Classes
Method results
60. NDK possible to reverse-engineer too but really really hard!
https://www.hex-rays.com/products/decompiler/
65. In order to download your expansion files
from Google Play, the user must have
acquired your application from Google
Play.
66. Combination - Part 1 Authorization
Answer to challenge
Obtain random
challenge for
current session
Application Side
Server Side
Download the
challenge
Using
Reflection
answer
challenge Send as
bytecode data
using
flatbuffer/wire
Compare
&
decide
67. Combination - Every request
Build
.apk
Extract MD5
from .apk
Store MD5 on server +
hash function (RSA,
AES)
Run-Time
Building Release
Load dex library.
Obtain MD5 from
dex/.so lib
Sign URL with MD5,
Token, TimeStamp and
challenge response Send data using
flatbuffer/wire
Compare url
signature
68. Compare
signature
and decide
Build
.apk
Extract MD5
from .apk
Store MD5 on
server per built
version
Run-Time
Building Release
Load dex library with
challenge.
Obtain MD5 from
dex/.so lib
Sign API Request with
MD5, Token,
TimeStamp and
challenge response
Answer to challenge
Obtain random
challenge for
current session
Server Side
Generate signature
based on stored MD5,
Challenge Answer,
TimeStamp & Token
Send data using
flatbuffer/wire