200 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Smali - 反编译/[修改]/编译
{{#include ../../banners/hacktricks-training.md}}
有时修改应用代码以访问隐藏信息(例如高度混淆的密码或 flags会很有用。此时反编译 apk、修改代码并重新编译可能很有价值。
**Opcodes 参考:** [http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html](http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html)
## 快速方法
使用 **Visual Studio Code** 和 [APKLab](https://github.com/APKLab/APKLab) 扩展,你可以 **自动反编译**、修改、**重新编译**、签名并安装应用,而无需执行任何命令。
另一个极大简化此任务的 **script** 是 [**https://github.com/ax/apk.sh**](https://github.com/ax/apk.sh)
## 反编译 APK
使用 APKTool 可以访问 **smali 代码和资源**
```bash
apktool d APP.apk
```
如果 **apktool** 给你任何错误,尝试[ installing the **latest version**](https://ibotpeaches.github.io/Apktool/install/)
一些**你应该查看的有趣文件**
- _res/values/strings.xml_ (以及 res/values/* 中的所有 xml)
- _AndroidManifest.xml_
- 任何扩展名为 _.sqlite__.db_ 的文件
如果 `apktool` 在**解码应用**时有问题,请查看 [https://ibotpeaches.github.io/Apktool/documentation/#framework-files](https://ibotpeaches.github.io/Apktool/documentation/#framework-files) 或尝试使用参数 **`-r`**(不要解码资源)。然后,如果问题出在资源而不是源代码,你就不会遇到这个问题(你也不会反编译这些资源)。
## 更改 smali 代码
你可以**更改****指令**、修改某些变量的**值**或**添加**新的指令。我使用 [**VS Code**](https://code.visualstudio.com) 来修改 Smali 代码,安装 **smalise extension** 后,编辑器会告诉你是否有任何**指令不正确**。\
一些**示例**可以在这里找到:
- [Smali changes examples](smali-changes.md)
- [Google CTF 2018 - Shall We Play a Game?](google-ctf-2018-shall-we-play-a-game.md)
Or you can [**check below some Smali changes explained**](smali-changes.md#modifying-smali).
## 重新编译 APK
修改代码后,你可以使用以下方式**重新编译**代码:
```bash
apktool b . #In the folder generated when you decompiled the application
```
它会在 _**dist**_ 文件夹中 **compile** 新的 APK。
如果 **apktool** 抛出 **error**,尝试[ installing the **latest version**](https://ibotpeaches.github.io/Apktool/install/)
### **为新的 APK 签名**
然后,你需要 **generate a key**(会要求你输入密码以及一些可以随机填写的信息):
```bash
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>
```
最后,**sign** 新的 APK:
```bash
jarsigner -keystore key.jks path/to/dist/* <your-alias>
```
### 优化新应用
**zipalign** 是一个归档对齐工具,可为 Android 应用 (APK) 文件提供重要的优化。 [更多信息](https://developer.android.com/studio/command-line/zipalign).
```bash
zipalign [-f] [-v] <alignment> infile.apk outfile.apk
zipalign -v 4 infile.apk
```
### **Sign the new APK (again?)**
如果你 **更愿意** 使用 [**apksigner**](https://developer.android.com/studio/command-line/) 而不是 jarsigner**你应该对 apk 进行签名**,在应用 **使用 zipaling 的优化** 之后。但请注意,你只需要使用 jarsigner在 zipalign 之前)或使用 aspsigner在 zipaling 之后)**对应用签名一次**。
```bash
apksigner sign --ks key.jks ./dist/mycompiled.apk
```
## 修改 Smali
对于下面的 Hello World Java 代码:
```java
public static void printHelloWorld() {
System.out.println("Hello World")
}
```
Smali 代码如下:
```java
.method public static printHelloWorld()V
.registers 2
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello World"
invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
return-void
.end method
```
Smali 指令集可在 [here](https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions).
### 轻微更改
### 修改函数内部变量的初始值
有些变量在函数开始处使用操作码 _const_ 定义,你可以修改它们的值,或者定义新的变量:
```bash
#Number
const v9, 0xf4240
const/4 v8, 0x1
#Strings
const-string v5, "wins"
```
### 基本操作
```bash
#Math
add-int/lit8 v0, v2, 0x1 #v2 + 0x1 and save it in v0
mul-int v0,v2,0x2 #v2*0x2 and save in v0
#Move the value of one object into another
move v1,v2
#Condtions
if-ge #Greater or equals
if-le #Less or equals
if-eq #Equals
#Get/Save attributes of an object
iget v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save this.o inside v0
iput v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save v0 inside this.o
#goto
:goto_6 #Declare this where you want to start a loop
if-ne v0, v9, :goto_6 #If not equals, go to: :goto_6
goto :goto_6 #Always go to: :goto_6
```
### 重大更改
### 日志记录
```bash
#Log win: <number>
iget v5, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Get this.o inside v5
invoke-static {v5}, Ljava/lang/String;->valueOf(I)Ljava/lang/String; #Transform number to String
move-result-object v1 #Move to v1
const-string v5, "wins" #Save "win" inside v5
invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I #Logging "Wins: <num>"
```
建议:
- 如果你打算在函数内部使用已声明的变量 (declared v0,v1,v2...),将这些行放在 _.local <number>_ 和变量声明 (_const v0, 0x1_) 之间
- 如果你想把日志代码放在函数代码的中间:
- 把已声明变量的数量加 2例如_.locals 10__.locals 12_
- 新变量应为已声明变量之后的连续编号(在此例中应为 _v10__v11_,记住编号从 v0 开始)。
- 更改日志函数的代码,使用 _v10__v11_ 替换 _v5__v1_
### Toast 提示
记得在函数开头将 _.locals_ 的数量增加 3。
此代码准备插入到函数的 **中间****根据需要更改变量的数量**)。它会获取 **this.o 的值**,将其 **转换****String**,然后用该值显示一个 **toast**
```bash
const/4 v10, 0x1
const/4 v11, 0x1
const/4 v12, 0x1
iget v10, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I
invoke-static {v10}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
move-result-object v11
invoke-static {p0, v11, v12}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v12
invoke-virtual {v12}, Landroid/widget/Toast;->show()V
```
### 在启动时加载本地库 (System.loadLibrary)
有时需要预先加载本地库,以便在其他 JNI 库之前完成初始化(例如,用于启用进程本地的遥测/日志记录)。你可以在静态初始化器或在 Application.onCreate() 的早期注入对 System.loadLibrary() 的调用。下面是用于静态类初始化器 (<clinit>) 的 smali 示例:
```smali
.class public Lcom/example/App;
.super Landroid/app/Application;
.method static constructor <clinit>()V
.registers 1
const-string v0, "sotap" # library name without lib...so prefix
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
return-void
.end method
```
或者,将相同的两条指令放在你的 Application.onCreate() 开始处,以确保该库尽早加载:
```smali
.method public onCreate()V
.locals 1
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
invoke-super {p0}, Landroid/app/Application;->onCreate()V
return-void
.end method
```
注意:
- 确保库的正确 ABI 变体存在于 lib/<abi>/ 下(例如 arm64-v8a/armeabi-v7a以避免 UnsatisfiedLinkError。
- 尽早加载class static initializer可以保证 native logger 能观察到后续的 JNI 活动。
## 参考
- SoTap轻量级应用内 JNI (.so) 行为 logger [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
{{#include ../../banners/hacktricks-training.md}}