Rev - Android
by halloworld
- 輔大資工-大三學生
- 本名:黃品翰 (halloworld)
Whoami
Title Text
Play商店
奇怪的store
APK
你的應用程式來源?
OWASP Moblie Top-10
程式碼
編譯
執行檔
一般流程
逆向
你有聽過逆向嗎?
- 能夠了解整個app的邏輯以及行為
-
拿到所需的資訊,或許能看到API?
- (e.g. 知道API,針對API進行攻擊)
-
修改邏輯、打包
- e.g. 繞過廣告、升級付費會員
逆向的目的
怎麼逆?
helloworld.apk
你可以先試試解壓縮他
大概看一下
配置文件
簽名訊息
資源
(被壓縮的檔案)
Dalvik
bytecode
需用到的
函式庫
編譯需用的
資源
資源
(原生的檔案)
AndroidManifest.xml
ApkTool
- 能夠把apk拆包,打包的工具
- https://ibotpeaches.github.io/Apktool/install/
怎用?
apktool d <file.apk> -o <dir>
apktool d helloworld.apk -o ./helloworld
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.helloworld">
<application android:allowBackup="true" android:debuggable="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name">
<activity android:label="@string/app_name" android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
- activity 描述app使用介面
- intent-filter 配置
- action 動作 (程式入口點)
- category 分類
-
是應用程式中最優先被執行的Activity
-
META-INF
- 目的:安裝app時做檢查及校驗
- 加密使用private key加密
- MANIFEST.MF
- apk中的文件,都有hash都會對應到文件
-
CERT.SF
- 第二道防線,對前一步的每一條內容再hash(sha1)接著進行base64
-
CERT.RSA
- 存了public key(公鑰),以及加密算法
Smali
JVM
- Java sourcecode => Java bytecode
- 接者轉成機械指令,在JVM上執行
- 跨平台windows, linux, MacOS...
Dalvik Virtual Machine
- 由Dan Bornstein編寫
- 適合記憶體和處理器速度有限的系統
Smali
- 把.dex的bytecode,轉成人類可讀的語言
- 透過smali,可以將程式邏輯進行竄改
B---byte
C---char
D---double
F---float
I---int
J---long
S---short
V---void
Z---boolean
[XXX---array
Lxxx/yyy---object
架構大概是這樣
disassemble
assemble
Smali
Smali 例子
foo ()V
void foo()
foo (III)Z
foo (Z[I[ILjava/lang/String;J)Ljava/lang/String
boolean foo(int, int, int)
String foo (boolean, int[], int[], String, long)
Smali Register
Smali Function
private:invoke-direct
public, protected: invoke-virtual
static:invoke-static
parent: invoke-super
example: invoke-xxx {參數},Class;->function(type)
# eg.
public static native String fenugreek(String str);
---------------------------------------------------
invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->fenugreek(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
return-object v1
Lab link
Tools in
Z:/NISRA/20200505_Rev-Android/tools.7z
Lab: Android setting
找出紅色部分的文字
他就是Flag
Android Studio with apk
helloworld.apk
試著改Smali code
把文字改成NISRA
改文字
Sublime
Smali Syntax highlight
ctrl + shift + p
Open
Sublime
smali
阿改完之後如何打包?
打包完的apk會放在dist底下
apktool b <dir> -o <*.apk>
apktool b ./Helloworld -o ./NISRA.apk
我打包成功了,阿怎麼裝失敗
Apk Sign
keytool -genkey -alias demo.keystore -keyalg RSA -validity 40000 -keystore demo.keystore
jarsigner -verbose -keystore demo.keystore Helloworld.apk demo.keystore
keytool
-genkey 產生key
-alias 別名
-keystore 指定keystroe名字
-keyalg 密鑰算法
-validity 有效天數
jarsigner
- verbose 詳細輸出
- keystore 證書儲存路徑
Jadx-GUI
通常會從Jadx-GUI看code
再去找Smali相對的位址
不過通常都會被混淆過
PicoCTF2019
droids–系列題目
droids:0
觀察Logcat
利用Android Studio
droids:1
hint:password的位置(res)?
.line 12
.local v0, "password":Ljava/lang/String;
invoke-virtual {p0, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
apktool\one\res\values\strings.xml
droids:2
public static String getFlag(String input, Context ctx) {
String[] witches = {"weatherwax", "ogg", "garlick", "nitt", "aching", "dismass"};
int second = 3 - 3;
int third = (3 / 3) + second;
int fourth = (third + third) - second;
int fifth = 3 + fourth;
int sixth = (fifth + second) - third;
String str = ".";
if (input.equals("".concat(witches[fifth]).concat(str).concat(witches[third]).concat(str).concat(witches[second]).concat(str).concat(witches[sixth]).concat(str).concat(witches[3]).concat(str).concat(witches[fourth]))) {
return sesame(input);
}
return "NOPE";
}
hint:Jadx-GUI
public class FlagstaffHill {
public static native String sesame(String str);
public static String getFlag(String input, Context ctx) {
// 0 1 2 3 4 5
String[] witches = {"weatherwax", "ogg", "garlick", "nitt", "aching", "dismass"};
int second = 0;
int third = 1;
int fourth = 2;
int fifth = 5;
int sixth = 4;
String str = ".";
if (input.equals("".concat(witches[fifth]).concat(str).concat(witches[third]).concat(str).concat(witches[second]).concat(str).concat(witches[sixth]).concat(str).concat(witches[3]).concat(str).concat(witches[fourth]))) {
return sesame(input);
//dismass.ogg.weatherwax.aching.nitt.garlick
}
return "NOPE";
}
}
droids:3
public class FlagstaffHill {
public static native String cilantro(String str);
public static String nope(String input) {
return "don't wanna";
}
public static String yep(String input) {
return cilantro(input);
}
public static String getFlag(String input, Context ctx) {
return nope(input);
}
}
public class FlagstaffHill {
public static native String cilantro(String str);
public static String getFlag(String input, Context ctx) {
return nope(input); - > yep(input);
}
}
-
把getFlag, return的function換成yep
-
Jadx不能編輯 => 請改Smali code
-
拆包、封裝、運行
.method public static getFlag(Ljava/lang/String;Landroid/content/Context;)Ljava/lang/String;
.locals 1
.param p0, "input" # Ljava/lang/String;
.param p1, "ctx" # Landroid/content/Context;
.line 19
invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->yep(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
.line 20
.local v0, "flag":Ljava/lang/String;
return-object v0
.end method
droids:4
public static String getFlag(String input, Context ctx) {
String str = "aaa";
StringBuilder ace = new StringBuilder(str);
StringBuilder jack = new StringBuilder(str);
StringBuilder queen = new StringBuilder(str);
StringBuilder king = new StringBuilder(str);
ace.setCharAt(0, (char) (ace.charAt(0) + 4));
ace.setCharAt(1, (char) (ace.charAt(1) + 19));
ace.setCharAt(2, (char) (ace.charAt(2) + 18));
jack.setCharAt(0, (char) (jack.charAt(0) + 7));
jack.setCharAt(1, (char) (jack.charAt(1) + 0));
jack.setCharAt(2, (char) (jack.charAt(2) + 1));
queen.setCharAt(0, (char) (queen.charAt(0) + 0));
queen.setCharAt(1, (char) (queen.charAt(1) + 11));
queen.setCharAt(2, (char) (queen.charAt(2) + 15));
king.setCharAt(0, (char) (king.charAt(0) + 14));
king.setCharAt(1, (char) (king.charAt(1) + 20));
king.setCharAt(2, (char) (king.charAt(2) + 15));
if (input.equals("".concat(queen.toString()).concat(jack.toString()).concat(ace.toString()).concat(king.toString()))) {
return "call it";
}
return "NOPE";
}
Text
public static String getFlag(String input, Context ctx) {
String str = "aaa";
StringBuilder ace = new StringBuilder(str);
StringBuilder jack = new StringBuilder(str);
StringBuilder queen = new StringBuilder(str);
StringBuilder king = new StringBuilder(str);
ace.setCharAt(0, (char) (a + 4));
ace.setCharAt(1, (char) (a + 19));
ace.setCharAt(2, (char) (a + 18));
jack.setCharAt(0, (char) (a + 7));
jack.setCharAt(1, (char) (a + 0));
jack.setCharAt(2, (char) (a + 1));
queen.setCharAt(0, (char) (a + 0));
queen.setCharAt(1, (char) (a + 11));
queen.setCharAt(2, (char) (a + 15));
king.setCharAt(0, (char) (a + 14));
king.setCharAt(1, (char) (a + 20));
king.setCharAt(2, (char) (a + 15));
if (input.equals("".concat(queen.toString()).concat(jack.toString()).concat(ace.toString()).concat(king.toString()))) {
return "call it";
}
return "NOPE";
}
整理一下code
print(chr(ord('a') + 0), end='')
print(chr(ord('a') + 11), end='')
print(chr(ord('a') + 15), end='')
print(chr(ord('a') + 7), end='')
print(chr(ord('a') + 0), end='')
print(chr(ord('a') + 1), end='')
print(chr(ord('a') + 4), end='')
print(chr(ord('a') + 19), end='')
print(chr(ord('a') + 18), end='')
print(chr(ord('a') + 14), end='')
print(chr(ord('a') + 20), end='')
print(chr(ord('a') + 15), end='')
# string
alphabetsoup
Text
public static String getFlag(String input, Context ctx) {
String str = "aaa";
StringBuilder ace = new StringBuilder(str);
StringBuilder jack = new StringBuilder(str);
StringBuilder queen = new StringBuilder(str);
StringBuilder king = new StringBuilder(str);
ace.setCharAt(0, (char) (a + 4));
ace.setCharAt(1, (char) (a + 19));
ace.setCharAt(2, (char) (a + 18));
jack.setCharAt(0, (char) (a + 7));
jack.setCharAt(1, (char) (a + 0));
jack.setCharAt(2, (char) (a + 1));
queen.setCharAt(0, (char) (a + 0));
queen.setCharAt(1, (char) (a + 11));
queen.setCharAt(2, (char) (a + 15));
king.setCharAt(0, (char) (a + 14));
king.setCharAt(1, (char) (a + 20));
king.setCharAt(2, (char) (a + 15));
if (input.equals("".concat(queen.toString()).concat(jack.toString()).concat(ace.toString()).concat(king.toString()))) {
return "call it";
}
return "NOPE";
}
WTF?
Text
public static String getFlag(String input, Context ctx) {
String str = "aaa";
StringBuilder ace = new StringBuilder(str);
StringBuilder jack = new StringBuilder(str);
StringBuilder queen = new StringBuilder(str);
StringBuilder king = new StringBuilder(str);
ace.setCharAt(0, (char) (a + 4));
ace.setCharAt(1, (char) (a + 19));
ace.setCharAt(2, (char) (a + 18));
jack.setCharAt(0, (char) (a + 7));
jack.setCharAt(1, (char) (a + 0));
jack.setCharAt(2, (char) (a + 1));
queen.setCharAt(0, (char) (a + 0));
queen.setCharAt(1, (char) (a + 11));
queen.setCharAt(2, (char) (a + 15));
king.setCharAt(0, (char) (a + 14));
king.setCharAt(1, (char) (a + 20));
king.setCharAt(2, (char) (a + 15));
if (input.equals("".concat(queen.toString()).concat(jack.toString()).concat(ace.toString()).concat(king.toString()))) {
return "call it";
}
return "NOPE";
}
WTF?
參考前題,找到如何觸發function
invoke-static {p0}, Lcom/hellocmu/picoctf/FlagstaffHill;->cardamom(Ljava/lang/String;)Ljava/lang/String;
move-result-object p0
return-object p0
smali中找到對應的code
return之前放上觸發function的code
老樣子,記得sign
jarsigner -verbose -keystore demo.keystore Helloworld.apk demo.keystore
jarsigner
- verbose 詳細輸出
- keystore 證書儲存路徑
如何防禦
-
使用ProGuard程式碼混淆
-
商用工具Arxan、Dexguard
Reference
Dalvik opcodes
Android 反編譯與防止反編譯
[2020NISRA_Class] Android Rev.
By halloworld
[2020NISRA_Class] Android Rev.
- 734