Translated ['src/mobile-pentesting/android-app-pentesting/smali-changes.

This commit is contained in:
Translator 2025-09-04 02:39:27 +00:00
parent 3f43948515
commit c08d9d119e
3 changed files with 200 additions and 111 deletions

View File

@ -2,7 +2,7 @@
{{#include ../../banners/hacktricks-training.md}}
## Forensik CheatSheets
## Forensik-CheatSheets
[https://www.jaiminton.com/cheatsheet/DFIR/#](https://www.jaiminton.com/cheatsheet/DFIR/)
@ -18,38 +18,38 @@
### Yara
#### Installieren
#### Installation
```bash
sudo apt-get install -y yara
```
#### Regeln vorbereiten
Verwenden Sie dieses Skript, um alle Yara-Malware-Regeln von GitHub herunterzuladen und zusammenzuführen: [https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9](https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9)\
Erstellen Sie das _**rules**_-Verzeichnis und führen Sie es aus. Dies erstellt eine Datei namens _**malware_rules.yar**_, die alle Yara-Regeln für Malware enthält.
Verwende dieses Skript, um alle yara malware rules von github herunterzuladen und zusammenzuführen: [https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9](https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9)\
Erstelle das _**rules**_ Verzeichnis und führe das Skript aus. Dadurch wird eine Datei namens _**malware_rules.yar**_ erstellt, die alle yara malware rules enthält.
```bash
wget https://gist.githubusercontent.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9/raw/4ec711d37f1b428b63bed1f786b26a0654aa2f31/malware_yara_rules.py
mkdir rules
python malware_yara_rules.py
```
#### Scannen
#### Scan
```bash
yara -w malware_rules.yar image #Scan 1 file
yara -w malware_rules.yar folder #Scan the whole folder
```
#### YaraGen: Überprüfen Sie auf Malware und erstellen Sie Regeln
#### YaraGen: Auf malware prüfen und yara rules erstellen
Sie können das Tool [**YaraGen**](https://github.com/Neo23x0/yarGen) verwenden, um Yara-Regeln aus einer Binärdatei zu generieren. Schauen Sie sich diese Tutorials an: [**Teil 1**](https://www.nextron-systems.com/2015/02/16/write-simple-sound-yara-rules/), [**Teil 2**](https://www.nextron-systems.com/2015/10/17/how-to-write-simple-but-sound-yara-rules-part-2/), [**Teil 3**](https://www.nextron-systems.com/2016/04/15/how-to-write-simple-but-sound-yara-rules-part-3/)
Du kannst das Tool [**YaraGen**](https://github.com/Neo23x0/yarGen) verwenden, um yara rules aus einer Binärdatei zu generieren. Sieh dir diese Tutorials an: [**Part 1**](https://www.nextron-systems.com/2015/02/16/write-simple-sound-yara-rules/), [**Part 2**](https://www.nextron-systems.com/2015/10/17/how-to-write-simple-but-sound-yara-rules-part-2/), [**Part 3**](https://www.nextron-systems.com/2016/04/15/how-to-write-simple-but-sound-yara-rules-part-3/)
```bash
python3 yarGen.py --update
python3.exe yarGen.py --excludegood -m ../../mals/
```
### ClamAV
#### Install
#### Installation
```
sudo apt-get install -y clamav
```
#### Scannen
#### Scan
```bash
sudo freshclam #Update rules
clamscan filepath #Scan 1 file
@ -57,25 +57,25 @@ clamscan folderpath #Scan the whole folder
```
### [Capa](https://github.com/mandiant/capa)
**Capa** erkennt potenziell bösartige **Fähigkeiten** in ausführbaren Dateien: PE, ELF, .NET. Es wird Dinge wie Att\&ck-Taktiken oder verdächtige Fähigkeiten wie:
**Capa** erkennt potenziell bösartige **Fähigkeiten** in ausführbaren Dateien: PE, ELF, .NET. Es findet daher Dinge wie Att\&ck-Taktiken oder verdächtige Fähigkeiten wie:
- Überprüfung auf OutputDebugString-Fehler
- als Dienst ausführen
- Prozess erstellen
- check for OutputDebugString error
- run as a service
- create process
Holen Sie es sich im [**Github-Repo**](https://github.com/mandiant/capa).
Holen Sie es sich im [**Github repo**](https://github.com/mandiant/capa).
### IOCs
IOC bedeutet Indicator Of Compromise. Ein IOC ist eine Reihe von **Bedingungen, die** potenziell unerwünschte Software oder bestätigte **Malware** identifizieren. Blue Teams verwenden diese Art von Definition, um **nach dieser Art von bösartigen Dateien** in ihren **Systemen** und **Netzwerken** zu **suchen**.\
Diese Definitionen zu teilen ist sehr nützlich, da, wenn Malware auf einem Computer identifiziert wird und ein IOC für diese Malware erstellt wird, andere Blue Teams es verwenden können, um die Malware schneller zu identifizieren.
IOC bedeutet Indicator Of Compromise. Ein IOC ist eine Menge von **Bedingungen, die** bestimmte potenziell unerwünschte Software oder bestätigte **Malware** identifizieren. Blue Teams verwenden diese Art von Definition, um **nach dieser Art von bösartigen Dateien** in ihren **Systemen** und **Netzwerken** zu suchen.\
Das Teilen dieser Definitionen ist sehr nützlich, denn wenn Malware auf einem Computer identifiziert und ein IOC für diese Malware erstellt wird, können andere Blue Teams es nutzen, um die Malware schneller zu identifizieren.
Ein Tool zum Erstellen oder Ändern von IOCs ist [**IOC Editor**](https://www.fireeye.com/services/freeware/ioc-editor.html)**.**\
Sie können Tools wie [**Redline**](https://www.fireeye.com/services/freeware/redline.html) verwenden, um **nach definierten IOCs auf einem Gerät** zu **suchen**.
Sie können Tools wie [**Redline**](https://www.fireeye.com/services/freeware/redline.html) verwenden, um **nach definierten IOCs auf einem Gerät zu suchen**.
### Loki
[**Loki**](https://github.com/Neo23x0/Loki) ist ein Scanner für einfache Indicators of Compromise.\
[**Loki**](https://github.com/Neo23x0/Loki) ist ein Scanner für einfache IOCs.\
Die Erkennung basiert auf vier Erkennungsmethoden:
```
1. File Name IOC
@ -92,11 +92,11 @@ Compares process connection endpoints with C2 IOCs (new since version v.10)
```
### Linux Malware Detect
[**Linux Malware Detect (LMD)**](https://www.rfxn.com/projects/linux-malware-detect/) ist ein Malware-Scanner für Linux, der unter der GNU GPLv2-Lizenz veröffentlicht wurde und auf die Bedrohungen in gemeinsam genutzten Hosting-Umgebungen ausgerichtet ist. Er verwendet Bedrohungsdaten von Netzwerkgrenz-Intrusionserkennungssystemen, um Malware zu extrahieren, die aktiv in Angriffen verwendet wird, und generiert Signaturen zur Erkennung. Darüber hinaus stammen Bedrohungsdaten auch aus Benutzereinsendungen mit der LMD-Checkout-Funktion und Ressourcen der Malware-Community.
[**Linux Malware Detect (LMD)**](https://www.rfxn.com/projects/linux-malware-detect/) ist ein Malware-Scanner für Linux, veröffentlicht unter der GNU GPLv2-Lizenz, der für die Bedrohungen in gemeinsam genutzten Hosting-Umgebungen entwickelt wurde. Er nutzt Threat-Daten aus network edge intrusion detection systems, um Malware zu extrahieren, die aktiv in Angriffen eingesetzt wird, und erzeugt Signaturen zur Erkennung. Zusätzlich stammen Threat-Daten auch aus user submissions über die LMD checkout-Funktion und aus malware community resources.
### rkhunter
Tools wie [**rkhunter**](http://rkhunter.sourceforge.net) können verwendet werden, um das Dateisystem auf mögliche **Rootkits** und Malware zu überprüfen.
Werkzeuge wie [**rkhunter**](http://rkhunter.sourceforge.net) können verwendet werden, um das Dateisystem auf mögliche **rootkits** und Malware zu überprüfen.
```bash
sudo ./rkhunter --check -r / -l /tmp/rkhunter.log [--report-warnings-only] [--skip-keypress]
```
@ -106,27 +106,27 @@ sudo ./rkhunter --check -r / -l /tmp/rkhunter.log [--report-warnings-only] [--sk
### PEpper
[PEpper ](https://github.com/Th3Hurrican3/PEpper) überprüft einige grundlegende Dinge in der ausführbaren Datei (binäre Daten, Entropie, URLs und IPs, einige Yara-Regeln).
[PEpper ](https://github.com/Th3Hurrican3/PEpper)prüft einige grundlegende Dinge in der ausführbaren Datei (Binärdaten, Entropie, URLs und IPs, einige Yara-Regeln).
### PEstudio
[PEstudio](https://www.winitor.com/download) ist ein Tool, das Informationen über Windows-Ausführungsdateien wie Importe, Exporte, Header bereitstellt, aber auch Virus Total überprüft und potenzielle Att\&ck-Techniken findet.
[PEstudio](https://www.winitor.com/download) ist ein Tool, das Informationen zu Windows-Executables wie imports, exports, headers liefert, prüft außerdem virus total und findet potenzielle Att\&ck techniques.
### Detect It Easy(DiE)
[**DiE**](https://github.com/horsicq/Detect-It-Easy/) ist ein Tool, um zu erkennen, ob eine Datei **verschlüsselt** ist und auch **Packers** zu finden.
[**DiE**](https://github.com/horsicq/Detect-It-Easy/) ist ein Tool, um zu erkennen, ob eine Datei **encrypted** ist und außerdem **packers** findet.
### NeoPI
[**NeoPI** ](https://github.com/CiscoCXSecurity/NeoPI) ist ein Python-Skript, das eine Vielzahl von **statistischen Methoden** verwendet, um **obfuskierte** und **verschlüsselte** Inhalte in Text-/Skriptdateien zu erkennen. Der beabsichtigte Zweck von NeoPI ist es, bei der **Erkennung von verstecktem Webshell-Code** zu helfen.
[**NeoPI** ](https://github.com/CiscoCXSecurity/NeoPI) ist ein Python-Skript, das eine Vielzahl von **statistical methods** nutzt, um **obfuscated** und **encrypted** content in Text-/Script-Dateien zu erkennen. Der Zweck von NeoPI ist, bei der **detection of hidden web shell code** zu unterstützen.
### **php-malware-finder**
[**PHP-malware-finder**](https://github.com/nbs-system/php-malware-finder) gibt sein Bestes, um **obfuskierte**/**verdächtige Codes** sowie Dateien zu erkennen, die häufig in **Malware**/Webshells verwendete **PHP**-Funktionen nutzen.
[**PHP-malware-finder**](https://github.com/nbs-system/php-malware-finder) versucht so gut wie möglich, **obfuscated**/**dodgy code** zu erkennen sowie Dateien, die **PHP**-Funktionen verwenden, die oft in **malwares**/webshells vorkommen.
### Apple Binary Signatures
Beim Überprüfen einer **Malware-Probe** sollten Sie immer die **Signatur** der Binärdatei überprüfen, da der **Entwickler**, der sie signiert hat, möglicherweise bereits mit **Malware** **verbunden** ist.
Beim Überprüfen eines **malware sample** solltest du immer die **Signatur** der Binary prüfen, da der **developer**, der sie signiert hat, bereits mit **malware** in Verbindung stehen könnte.
```bash
#Get signer
codesign -vv -d /bin/ls 2>&1 | grep -E "Authority|TeamIdentifier"
@ -137,29 +137,41 @@ codesign --verify --verbose /Applications/Safari.app
#Check if the signature is valid
spctl --assess --verbose /Applications/Safari.app
```
## Detection Techniques
## Erkennungstechniken
### File Stacking
Wenn Sie wissen, dass ein Ordner mit den **Dateien** eines Webservers **am bestimmten Datum zuletzt aktualisiert wurde**. **Überprüfen** Sie das **Datum**, an dem alle **Dateien** im **Webserver erstellt und geändert** wurden, und wenn ein Datum **verdächtig** ist, überprüfen Sie diese Datei.
Wenn du weißt, dass ein Ordner mit den **Dateien** eines Webservers **zu einem bestimmten Datum zuletzt aktualisiert** wurde, **prüfe** das **Datum**, an dem alle **Dateien** im **Webserver** erstellt und geändert wurden. Wenn ein Datum **verdächtig** ist, untersuche diese Datei.
### Baselines
Wenn die Dateien eines Ordners **nicht geändert worden sein sollten**, können Sie den **Hash** der **ursprünglichen Dateien** des Ordners berechnen und diese mit den **aktuellen** vergleichen. Alles, was geändert wurde, wird **verdächtig** sein.
Wenn die Dateien eines Ordners **nicht verändert worden sein sollten**, kannst du die **Hashes** der **Originaldateien** des Ordners berechnen und mit den **aktuellen** vergleichen. Alles, was verändert wurde, ist **verdächtig**.
### Statistical Analysis
### Statistische Analyse
Wenn die Informationen in Protokollen gespeichert sind, können Sie **Statistiken überprüfen, wie oft jede Datei eines Webservers aufgerufen wurde, da eine Web-Shell eine der häufigsten sein könnte**.
Wenn die Informationen in Logs gespeichert sind, kannst du **Statistiken prüfen, z. B. wie oft jede Datei eines Webservers aufgerufen wurde**, da eine web shell zu den am häufigsten aufgerufenen gehören könnte.
---
## Deobfuscating Dynamic Control-Flow (JMP/CALL RAX Dispatchers)
### Android in-app native telemetry (no root)
Moderne Malware-Familien missbrauchen stark die Obfuskation von Control-Flow-Graphen (CFG): Anstelle eines direkten Sprungs/Calls berechnen sie das Ziel zur Laufzeit und führen ein `jmp rax` oder `call rax` aus. Ein kleiner *Dispatcher* (typischerweise neun Anweisungen) legt das endgültige Ziel je nach CPU `ZF`/`CF`-Flags fest, wodurch die statische CFG-Wiederherstellung vollständig unterbrochen wird.
Auf Android kannst du nativen Code im Prozess der Ziel-App instrumentieren, indem du vor der Initialisierung anderer JNI-Libs eine kleine Logger-Library preloadest. Das ermöglicht frühe Einsicht in natives Verhalten ohne systemweite Hooks oder Root. Ein verbreiteter Ansatz ist SoTap: lege libsotap.so für die richtige ABI in das APK und injiziere einen System.loadLibrary("sotap")-Aufruf früh (z. B. statischer Initializer oder Application.onCreate), und sammle dann Logs aus internen/externen Pfaden oder als Logcat-Fallback.
Die Technik demonstriert durch den SLOW#TEMPEST-Loader kann mit einem dreistufigen Workflow besiegt werden, der nur auf IDAPython und den Unicorn CPU-Emulator angewiesen ist.
Siehe die Seite zum Android native reversing für Setup-Details und Log-Pfade:
### 1. Locate every indirect jump / call
{{#ref}}
../../../mobile-pentesting/android-app-pentesting/reversing-native-libraries.md
{{#endref}}
---
## Deobfuskation dynamischer Control-Flow (JMP/CALL RAX Dispatchers)
Moderne Malware-Familien missbrauchen stark Control-Flow Graph (CFG) Obfuskation: statt eines direkten jump/call berechnen sie das Ziel zur Laufzeit und führen ein `jmp rax` oder `call rax` aus. Ein kleiner *Dispatcher* (typischerweise neun Instruktionen) setzt das finale Ziel abhängig von den CPU-`ZF`/`CF` Flags und zerstört damit vollständig die statische CFG-Rekonstruktion.
Die Technik demonstriert vom SLOW#TEMPEST loader lässt sich mit einem dreistufigen Workflow umgehen, der nur auf IDAPython und dem Unicorn CPU-Emulator basiert.
### 1. Lokalisieren Sie jeden indirekten jump / call
```python
import idautils, idc
@ -168,7 +180,7 @@ mnem = idc.print_insn_mnem(ea)
if mnem in ("jmp", "call") and idc.print_operand(ea, 0) == "rax":
print(f"[+] Dispatcher found @ {ea:X}")
```
### 2. Extrahieren Sie den Dispatcher-Bytecode
### 2. Dispatcher-Bytecode extrahieren
```python
import idc
@ -183,7 +195,7 @@ size = jmp_ea + idc.get_item_size(jmp_ea) - start
code = idc.get_bytes(start, size)
open(f"{start:X}.bin", "wb").write(code)
```
### 3. Emulieren Sie es zweimal mit Unicorn
### 3. Emuliere es zweimal mit Unicorn
```python
from unicorn import *
from unicorn.x86_const import *
@ -199,9 +211,9 @@ mu.reg_write(UC_X86_REG_RAX, 0)
mu.emu_start(BASE, BASE+len(code))
return mu.reg_read(UC_X86_REG_RAX)
```
Führen Sie `run(code,0,0)` und `run(code,1,1)` aus, um die Ziele des *false* und *true* Zweigs zu erhalten.
Führe `run(code,0,0)` und `run(code,1,1)` aus, um die *false* und *true* branch targets zu erhalten.
### 4. Patchen Sie einen direkten Sprung / Aufruf zurück
### 4. Einen direkten jump / call zurückpatchen
```python
import struct, ida_bytes
@ -210,27 +222,28 @@ op = 0xE8 if is_call else 0xE9 # CALL rel32 or JMP rel32
disp = target - (ea + 5) & 0xFFFFFFFF
ida_bytes.patch_bytes(ea, bytes([op]) + struct.pack('<I', disp))
```
Nach dem Patchen IDA zwingen, die Funktion erneut zu analysieren, damit das vollständige CFG und die Hex-Rays-Ausgabe wiederhergestellt werden:
Nach dem Patchen IDA dazu zwingen, die Funktion neu zu analysieren, damit der vollständige CFG und die Hex-Rays-Ausgabe wiederhergestellt werden:
```python
import ida_auto, idaapi
idaapi.reanalyze_function(idc.get_func_attr(ea, idc.FUNCATTR_START))
```
### 5. Indirekte API-Aufrufe kennzeichnen
### 5. Label indirect API calls
Sobald das tatsächliche Ziel jedes `call rax` bekannt ist, können Sie IDA mitteilen, was es ist, damit die Parametertypen und Variablennamen automatisch wiederhergestellt werden:
Sobald das echte Ziel jedes `call rax` bekannt ist, können Sie IDA mitteilen, was es ist, damit Parametertypen & Variablennamen automatisch wiederhergestellt werden:
```python
idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+
```
### Praktische Vorteile
* Stellt das echte CFG wieder her → Die Dekompilierung geht von *10* Zeilen auf Tausende.
* Ermöglicht String-Cross-Referenzen & xrefs, wodurch die Verhaltensrekonstruktion trivial wird.
* Skripte sind wiederverwendbar: Einfach in jeden Loader einfügen, der durch denselben Trick geschützt ist.
* Stellt das echte CFG wieder her → decompilation geht von *10* Zeilen auf Tausende.
* Ermöglicht string-cross-reference & xrefs, making behaviour reconstruction trivial.
* Scripts sind wiederverwendbar: Einfach in jeden loader legen, der durch denselben Trick geschützt ist.
---
## Referenzen
- [Unit42 Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques](https://unit42.paloaltonetworks.com/slow-tempest-malware-obfuscation/)
- SoTap: Lightweight in-app JNI (.so) behavior logger [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,47 +1,50 @@
# Reversing Native Libraries
# Reverse Engineering nativer Bibliotheken
{{#include ../../banners/hacktricks-training.md}}
**Für weitere Informationen siehe:** [**https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html**](https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html)
Android-Apps können native Bibliotheken verwenden, die typischerweise in C oder C++ geschrieben sind, für leistungskritische Aufgaben. Malware-Ersteller missbrauchen ebenfalls diese Bibliotheken, da ELF Shared Objects immer noch schwieriger zu dekompilieren sind als DEX/OAT Bytecode. Diese Seite konzentriert sich auf *praktische* Workflows und *aktuelle* Verbesserungen der Werkzeuge (2023-2025), die das Reversing von Android `.so`-Dateien erleichtern.
Android-Apps können native Bibliotheken verwenden, typischerweise in C oder C++, für performance-kritische Aufgaben. Malware-Autoren missbrauchen diese Bibliotheken ebenfalls, weil ELF shared objects nach wie vor schwerer zu dekompilieren sind als DEX/OAT-Bytecode.
Diese Seite konzentriert sich auf *praktische* Workflows und *aktuelle* Tool-Verbesserungen (2023-2025), die das Reversing von Android `.so`-Dateien erleichtern.
---
### Schneller Triage-Workflow für eine frisch extrahierte `libfoo.so`
### Schneller Triage-Workflow für eine frisch gezogene `libfoo.so`
1. **Extrahiere die Bibliothek**
1. **Bibliothek extrahieren**
```bash
# Aus einer installierten Anwendung
# From an installed application
adb shell "run-as <pkg> cat lib/arm64-v8a/libfoo.so" > libfoo.so
# Oder aus der APK (zip)
# Or from the APK (zip)
unzip -j target.apk "lib/*/libfoo.so" -d extracted_libs/
```
2. **Architektur & Schutzmaßnahmen identifizieren**
2. **Architektur & Schutzmechanismen identifizieren**
```bash
file libfoo.so # arm64 oder arm32 / x86
file libfoo.so # arm64 or arm32 / x86
readelf -h libfoo.so # OS ABI, PIE, NX, RELRO, etc.
checksec --file libfoo.so # (peda/pwntools)
```
3. **Exportierte Symbole & JNI-Bindungen auflisten**
3. **Exportierte Symbole & JNI-Bindings auflisten**
```bash
readelf -s libfoo.so | grep ' Java_' # dynamisch verlinktes JNI
strings libfoo.so | grep -i "RegisterNatives" -n # statisch registriertes JNI
readelf -s libfoo.so | grep ' Java_' # dynamic-linked JNI
strings libfoo.so | grep -i "RegisterNatives" -n # static-registered JNI
```
4. **In einen Decompiler laden** (Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper oder Cutter/Rizin) und die Auto-Analyse ausführen. Neuere Ghidra-Versionen haben einen AArch64-Decompiler eingeführt, der PAC/BTI-Stubs und MTE-Tags erkennt, was die Analyse von Bibliotheken, die mit dem Android 14 NDK erstellt wurden, erheblich verbessert.
5. **Entscheide über statisches vs. dynamisches Reversing:** gestrippter, obfuszierter Code benötigt oft *Instrumentation* (Frida, ptrace/gdbserver, LLDB).
4. **In einen Decompiler laden** (Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper or Cutter/Rizin) und Auto-Analyse ausführen.
Neuere Ghidra-Versionen haben einen AArch64-Decompiler eingeführt, der PAC/BTI-Stubs und MTE-Tags erkennt und die Analyse von Bibliotheken, die mit dem Android 14 NDK gebaut wurden, deutlich verbessert.
5. **Abwägen: static vs dynamic reversing:** stripped, obfuscated code benötigt oft *instrumentation* (Frida, ptrace/gdbserver, LLDB).
---
### Dynamische Instrumentation (Frida ≥ 16)
Die 16er-Serie von Frida brachte mehrere Android-spezifische Verbesserungen, die helfen, wenn das Ziel moderne Clang/LLD-Optimierungen verwendet:
Fridas 16-Serie brachte mehrere Android-spezifische Verbesserungen, die helfen, wenn das Ziel moderne Clang/LLD-Optimierungen verwendet:
* `thumb-relocator` kann jetzt *kleine ARM/Thumb-Funktionen* hooken, die durch LLDs aggressive Ausrichtung (`--icf=all`) erzeugt werden.
* Das Auflisten und Neuzuweisen von *ELF-Import-Slots* funktioniert auf Android und ermöglicht das Patchen von `dlopen()`/`dlsym()` pro Modul, wenn Inline-Hooks abgelehnt werden.
* Das Java-Hooking wurde für den neuen **ART Quick-Entry-Point** behoben, der verwendet wird, wenn Apps mit `--enable-optimizations` auf Android 14 kompiliert werden.
* `thumb-relocator` kann jetzt *tiny ARM/Thumb functions* hooken, die von LLDs aggressiver Alignment-Strategie (`--icf=all`) erzeugt werden.
* Enumerating and rebinding *ELF import slots* funktioniert auf Android, wodurch pro Modul `dlopen()`/`dlsym()`-Patching möglich ist, wenn inline hooks abgelehnt werden.
* Java hooking wurde für den neuen **ART quick-entrypoint** behoben, der verwendet wird, wenn Apps mit `--enable-optimizations` auf Android 14 kompiliert werden.
Beispiel: Auflisten aller Funktionen, die über `RegisterNatives` registriert sind, und Dumpen ihrer Adressen zur Laufzeit:
Beispiel: Alle durch `RegisterNatives` registrierten Funktionen aufzählen und deren Adressen zur Laufzeit ausgeben:
```javascript
Java.perform(function () {
var Runtime = Java.use('java.lang.Runtime');
@ -58,38 +61,76 @@ console.log('[+] RegisterNatives on ' + clazz.getName() + ' -> ' + count + ' met
});
});
```
Frida funktioniert sofort auf PAC/BTI-fähigen Geräten (Pixel 8/Android 14+), solange Sie frida-server 16.2 oder höher verwenden frühere Versionen konnten das Padding für Inline-Hooks nicht finden. citeturn5search2turn5search0
Frida funktioniert sofort auf PAC/BTI-aktivierten Geräten (Pixel 8/Android 14+), solange du frida-server 16.2 oder neuer verwendest ältere Versionen konnten das padding für inline hooks nicht finden.
### Prozesslokale JNI-Telemetrie durch vorgeladenes .so (SoTap)
Wenn vollwertige Instrumentation übertrieben oder blockiert ist, kannst du trotzdem native Sichtbarkeit gewinnen, indem du einen kleinen Logger im Zielprozess vorgeladst. SoTap ist eine leichte Android-native (.so) Bibliothek, die das Laufzeitverhalten anderer JNI (.so) Libraries innerhalb desselben App-Prozesses protokolliert (kein Root erforderlich).
Wesentliche Eigenschaften:
- Initialisiert früh und beobachtet JNI/native Interaktionen innerhalb des Prozesses, der es lädt.
- Speichert Logs über mehrere beschreibbare Pfade mit einem sanften Fallback zu Logcat, wenn Speicher eingeschränkt ist.
- Quellcode-anpassbar: editier sotap.c, um zu erweitern/anzupassen, was geloggt wird, und rebuild pro ABI.
Setup (APK neu packen):
1) Lege den richtigen ABI-Build in das APK, damit der Loader libsotap.so auflösen kann:
- lib/arm64-v8a/libsotap.so (für arm64)
- lib/armeabi-v7a/libsotap.so (für arm32)
2) Stelle sicher, dass SoTap vor anderen JNI-Libs geladen wird. Injiziere einen frühen Aufruf (z. B. Application subclass static initializer oder onCreate), damit der Logger zuerst initialisiert wird. Smali-Snippet Beispiel:
```smali
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
```
3) Rebuild/sign/install, starte die App und sammle dann die Logs.
Log-Pfade (der Reihe nach geprüft):
```
/data/user/0/%s/files/sotap.log
/data/data/%s/files/sotap.log
/sdcard/Android/data/%s/files/sotap.log
/sdcard/Download/sotap-%s.log
# If all fail: fallback to Logcat only
```
Hinweise und Fehlerbehebung:
- ABI-Ausrichtung ist obligatorisch. Eine Nichtübereinstimmung löst UnsatisfiedLinkError aus und der Logger wird nicht geladen.
- Speicherbeschränkungen sind bei modernen Android-Geräten üblich; falls Dateischreibvorgänge fehlschlagen, gibt SoTap weiterhin Ausgaben über Logcat aus.
- Das Verhalten/der Detailgrad ist zur Anpassung gedacht; nach dem Bearbeiten von sotap.c aus dem Quellcode neu bauen.
Dieser Ansatz ist nützlich für Malware-Triage und JNI-Debugging, wenn das Beobachten nativer Aufrufflüsse vom Prozessstart an kritisch ist, aber systemweite Hooks oder Root nicht verfügbar sind.
---
### Jüngste Schwachstellen, die in APKs zu suchen sind
### Kürzlich veröffentlichte Verwundbarkeiten, nach denen es sich in APKs zu suchen lohnt
| Jahr | CVE | Betroffene Bibliothek | Anmerkungen |
|------|-----|-----------------------|-------------|
|2023|CVE-2023-4863|`libwebp` ≤ 1.3.1|Heap-Pufferüberlauf, der von nativen Code erreicht werden kann, der WebP-Bilder decodiert. Mehrere Android-Apps bündeln verwundbare Versionen. Wenn Sie eine `libwebp.so` in einer APK sehen, überprüfen Sie die Version und versuchen Sie eine Ausnutzung oder Patchen.| citeturn2search0|
|2024|Mehrere|OpenSSL 3.x-Serie|Mehrere Probleme mit Speichersicherheit und Padding-Oracle. Viele Flutter- und ReactNative-Bundles liefern ihre eigene `libcrypto.so` mit.|
| Year | CVE | Affected library | Notes |
|------|-----|------------------|-------|
|2023|CVE-2023-4863|`libwebp` ≤ 1.3.1|Heap buffer overflow reachable from native code that decodes WebP images. Several Android apps bundle vulnerable versions. When you see a `libwebp.so` inside an APK, check its version and attempt exploitation or patching.| |
|2024|Multiple|OpenSSL 3.x series|Several memory-safety and padding-oracle issues. Many Flutter & ReactNative bundles ship their own `libcrypto.so`.|
Wenn Sie *drittanbieter* `.so`-Dateien in einer APK entdecken, überprüfen Sie immer deren Hash gegen upstream-Beratungen. SCA (Software Composition Analysis) ist auf Mobilgeräten unüblich, sodass veraltete verwundbare Builds weit verbreitet sind.
Wenn Sie *third-party* `.so`-Dateien in einem APK entdecken, gleichen Sie deren Hash immer mit den Upstream-Advisories ab. SCA (Software Composition Analysis) ist auf Mobilgeräten unüblich, daher sind veraltete verwundbare Builds weit verbreitet.
---
### Anti-Reversing & Härtungstrends (Android 13-15)
* **Pointer Authentication (PAC) & Branch Target Identification (BTI):** Android 14 aktiviert PAC/BTI in Systembibliotheken auf unterstütztem ARMv8.3+ Silizium. Decompiler zeigen jetzt PAC-bezogene Pseudo-Anweisungen an; für die dynamische Analyse injiziert Frida Trampolins *nachdem* PAC entfernt wurde, aber Ihre benutzerdefinierten Trampolins sollten `pacda`/`autibsp` aufrufen, wo nötig.
* **MTE & Scudo gehärteter Allokator:** Memory-Tagging ist optional, aber viele Play-Integrity-bewusste Apps werden mit `-fsanitize=memtag` erstellt; verwenden Sie `setprop arm64.memtag.dump 1` plus `adb shell am start ...`, um Tag-Fehler zu erfassen.
* **LLVM Obfuscator (opake Prädikate, Kontrollfluss-Glättung):** Kommerzielle Packprogramme (z. B. Bangcle, SecNeo) schützen zunehmend *native* Codes, nicht nur Java; erwarten Sie falschen Kontrollfluss und verschlüsselte String-Blobs in `.rodata`.
* **Pointer Authentication (PAC) & Branch Target Identification (BTI):** Android 14 aktiviert PAC/BTI in Systembibliotheken auf unterstützter ARMv8.3+ Hardware. Decompiler zeigen jetzt PACbezogene Pseudo-Instruktionen; für dynamische Analyse injiziert Frida Trampolines *nach dem Entfernen von PAC*, aber Ihre eigenen Trampolines sollten, wo nötig, `pacda`/`autibsp` aufrufen.
* **MTE & Scudo hardened allocator:** Memory-Tagging ist optional (opt-in), aber viele Play-Integrity-bewusste Apps werden mit `-fsanitize=memtag` gebaut; verwenden Sie `setprop arm64.memtag.dump 1` plus `adb shell am start ...`, um Tag-Fehler zu erfassen.
* **LLVM Obfuscator (opaque predicates, control-flow flattening):** kommerzielle Packer (z. B. Bangcle, SecNeo) schützen zunehmend *native* Code, nicht nur Java; erwarten Sie falsche Kontrollflüsse und verschlüsselte String-Blobs in `.rodata`.
---
### Ressourcen
- **ARM-Assembly lernen:** [Azeria Labs ARM Assembly Basics](https://azeria-labs.com/writing-arm-assembly-part-1/)
- **JNI & NDK-Dokumentation:** [Oracle JNI Spec](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html) · [Android JNI Tipps](https://developer.android.com/training/articles/perf-jni) · [NDK-Anleitungen](https://developer.android.com/ndk/guides/)
- **ARM Assembly lernen:** [Azeria Labs ARM Assembly Basics](https://azeria-labs.com/writing-arm-assembly-part-1/)
- **JNI- & NDK-Dokumentation:** [Oracle JNI Spec](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html) · [Android JNI Tips](https://developer.android.com/training/articles/perf-jni) · [NDK Guides](https://developer.android.com/ndk/guides/)
- **Debugging nativer Bibliotheken:** [Debug Android Native Libraries Using JEB Decompiler](https://medium.com/@shubhamsonani/how-to-debug-android-native-libraries-using-jeb-decompiler-eec681a22cf3)
### Referenzen
- Frida 16.x Änderungsprotokoll (Android-Hooking, tiny-function relocation) [frida.re/news](https://frida.re/news/) citeturn5search0
- NVD-Beratungen für `libwebp` Überlauf CVE-2023-4863 [nvd.nist.gov](https://nvd.nist.gov/vuln/detail/CVE-2023-4863) citeturn2search0
- Frida 16.x change-log (Android hooking, tiny-function relocation) [frida.re/news](https://frida.re/news/)
- NVD-Hinweis für den `libwebp`-Overflow CVE-2023-4863 [nvd.nist.gov](https://nvd.nist.gov/vuln/detail/CVE-2023-4863)
- SoTap: Leichter In-App JNI (.so)-Verhaltenslogger [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
- SoTap Releases [github.com/RezaArbabBot/SoTap/releases](https://github.com/RezaArbabBot/SoTap/releases)
- Wie man mit SoTap arbeitet? [t.me/ForYouTillEnd/13](https://t.me/ForYouTillEnd/13)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,79 +1,80 @@
# Smali - Decompiling/\[Modifying]/Compiling
# Smali - Decompilieren/[Modifizieren]/Kompilieren
{{#include ../../banners/hacktricks-training.md}}
Manchmal ist es interessant, den Anwendungscode zu modifizieren, um auf versteckte Informationen zuzugreifen (vielleicht gut obfuskierte Passwörter oder Flags). Dann könnte es interessant sein, die apk zu dekompilieren, den Code zu ändern und ihn neu zu kompilieren.
**Opcodes reference:** [http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html](http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html)
Manchmal ist es interessant, den Anwendungscode zu modifizieren, um für dich auf versteckte Informationen zuzugreifen (z. B. stark obfuskierte Passwörter oder Flags). Dann kann es sinnvoll sein, die apk zu dekompilieren, den Code zu ändern und sie wieder zu kompilieren.
## Fast Way
**Opcodes-Referenz:** [http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html](http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html)
Mit **Visual Studio Code** und der [APKLab](https://github.com/APKLab/APKLab) Erweiterung können Sie die Anwendung **automatisch dekompilieren**, modifizieren, **neu kompilieren**, signieren und installieren, ohne einen Befehl auszuführen.
## Schnelle Methode
Ein weiteres **Skript**, das diese Aufgabe erheblich erleichtert, ist [**https://github.com/ax/apk.sh**](https://github.com/ax/apk.sh)
Mit **Visual Studio Code** und der Erweiterung [APKLab](https://github.com/APKLab/APKLab) kannst du die Anwendung **automatisch dekompilieren**, ändern, **wieder kompilieren**, signieren und installieren, ohne einen Befehl auszuführen.
## Decompile the APK
Ein weiteres **Skript**, das diese Aufgabe stark erleichtert, ist [**https://github.com/ax/apk.sh**](https://github.com/ax/apk.sh)
Mit APKTool können Sie auf den **smali code und die Ressourcen** zugreifen:
## APK dekompilieren
Mit APKTool kannst du auf die **smali code and resources** zugreifen:
```bash
apktool d APP.apk
```
Wenn **apktool** einen Fehler anzeigt, versuche[ die **neueste Version**](https://ibotpeaches.github.io/Apktool/install/) zu installieren.
Wenn **apktool** einen Fehler ausgibt, versuche[ die **neueste Version** zu installieren](https://ibotpeaches.github.io/Apktool/install/)
Einige **interessante Dateien, die du dir ansehen solltest, sind**:
- _res/values/strings.xml_ (und alle xmls in res/values/\*)
- _res/values/strings.xml_ (und alle XML-Dateien in res/values/*)
- _AndroidManifest.xml_
- Jede Datei mit der Erweiterung _.sqlite_ oder _.db_
Wenn `apktool` **Probleme beim Dekodieren der Anwendung** hat, schaue dir [https://ibotpeaches.github.io/Apktool/documentation/#framework-files](https://ibotpeaches.github.io/Apktool/documentation/#framework-files) an oder versuche das Argument **`-r`** zu verwenden (Ressourcen nicht dekodieren). Dann, wenn das Problem in einer Ressource und nicht im Quellcode lag, wirst du das Problem nicht haben (du wirst auch die Ressourcen nicht dekompilieren).
Wenn `apktool` **Probleme beim Dekodieren der Anwendung** hat, siehe [https://ibotpeaches.github.io/Apktool/documentation/#framework-files] oder versuche das Argument **`-r`** zu verwenden (Ressourcen nicht dekompilieren). Dann, wenn das Problem in einer Ressource und nicht im Quellcode lag, wirst du das Problem nicht haben (du wirst die Ressourcen auch nicht dekompilieren).
## Smali-Code ändern
Du kannst **Anweisungen ändern**, den **Wert** einiger Variablen ändern oder **neue Anweisungen hinzufügen**. Ich ändere den Smali-Code mit [**VS Code**](https://code.visualstudio.com), du installierst dann die **smalise Erweiterung** und der Editor wird dir sagen, ob eine **Anweisung falsch ist**.\
Du kannst **Anweisungen** ändern, den **Wert** einiger Variablen ändern oder neue Anweisungen **hinzufügen**. Ich ändere den Smali-Code mit [**VS Code**](https://code.visualstudio.com), anschließend installierst du die **smalise extension** und der Editor zeigt dir, ob eine **Anweisung inkorrekt ist**.\
Einige **Beispiele** findest du hier:
- [Beispiele für Smali-Änderungen](smali-changes.md)
- [Smali changes examples](smali-changes.md)
- [Google CTF 2018 - Shall We Play a Game?](google-ctf-2018-shall-we-play-a-game.md)
Oder du kannst [**unten einige Smali-Änderungen erklärt ansehen**](smali-changes.md#modifying-smali).
Or you can [**check below some Smali changes explained**](smali-changes.md#modifying-smali).
## APK neu kompilieren
Nachdem du den Code geändert hast, kannst du den Code mit **rekompilieren**:
Nach dem Ändern des Codes kannst du den Code mit folgendem Befehl **neu kompilieren**:
```bash
apktool b . #In the folder generated when you decompiled the application
```
Es wird die neue APK **im** _**dist**_ Ordner **kompilieren**.
Die neue APK wird **kompiliert** **innerhalb** des _**dist**_ Ordners.
Wenn **apktool** einen **Fehler** ausgibt, versuche[ die **neueste Version**](https://ibotpeaches.github.io/Apktool/install/) zu installieren.
Wenn **apktool** einen **Fehler** wirft, versuche[ installing the **latest version**](https://ibotpeaches.github.io/Apktool/install/)
### **Signiere die neue APK**
Dann musst du einen **Schlüssel** **generieren** (du wirst nach einem Passwort und einigen Informationen gefragt, die du zufällig ausfüllen kannst):
Dann musst du einen **key generieren** (du wirst nach einem Passwort und einigen Informationen gefragt, die du zufällig ausfüllen kannst):
```bash
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>
```
Schließlich **signiere** die neue APK:
Schließlich, **signiere** die neue APK:
```bash
jarsigner -keystore key.jks path/to/dist/* <your-alias>
```
### Neue Anwendung optimieren
**zipalign** ist ein Archiv-Ausrichtungswerkzeug, das wichtige Optimierungen für Android-Anwendungen (APK) bietet. [More information here](https://developer.android.com/studio/command-line/zipalign).
**zipalign** ist ein Tool zur Ausrichtung von Archiven, das wichtige Optimierungen für Android-Anwendungsdateien (APK) bereitstellt. [Weitere Informationen](https://developer.android.com/studio/command-line/zipalign).
```bash
zipalign [-f] [-v] <alignment> infile.apk outfile.apk
zipalign -v 4 infile.apk
```
### **Signiere die neue APK (noch einmal?)**
### **Die neue APK signieren (nochmal?)**
Wenn du **bevorzugst**, [**apksigner**](https://developer.android.com/studio/command-line/) anstelle von jarsigner zu verwenden, **solltest du die apk signieren**, nachdem du **die Optimierung mit** zipalign angewendet hast. ABER BEACHTE, DASS DU DIE ANWENDUNG NUR EINMAL **MIT jarsigner** (vor zipalign) ODER MIT aspsigner (nach zipalign) **SIGNIEREN MUSST**.
Wenn du **lieber** [**apksigner**](https://developer.android.com/studio/command-line/) statt jarsigner verwendest, **solltest du die APK signieren** nachdem du **die Optimierung mit** zipaling angewendet hast. ABER BEACHTE, DASS DU DIE ANWENDUNG NUR EINMAL MIT jarsigner (vor zipalign) ODER MIT aspsigner (nach zipaling) SIGNIEREN MUSST.
```bash
apksigner sign --ks key.jks ./dist/mycompiled.apk
```
## Modifying Smali
## Smali ändern
Für den folgenden Hello World Java-Code:
Für den folgenden Hello-World-Java-Code:
```java
public static void printHelloWorld() {
System.out.println("Hello World")
@ -89,13 +90,13 @@ invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
return-void
.end method
```
Das Smali-Befehlssatz ist [hier](https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions) verfügbar.
Der Smali-Instruktionssatz ist verfügbar [here](https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions).
### Leichte Änderungen
### Ändern der Anfangswerte einer Variablen innerhalb einer Funktion
### Ändern der Anfangswerte einer Variable innerhalb einer Funktion
Einige Variablen werden zu Beginn der Funktion mit dem Opcode _const_ definiert, Sie können deren Werte ändern oder neue definieren:
Einige Variablen werden am Anfang der Funktion mit dem Opcode _const_ definiert, du kannst ihre Werte ändern oder neue definieren:
```bash
#Number
const v9, 0xf4240
@ -139,17 +140,17 @@ invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/Strin
```
Empfehlungen:
- Wenn Sie deklarierte Variablen innerhalb der Funktion verwenden möchten (deklarierte v0,v1,v2...), setzen Sie diese Zeilen zwischen die _.local \<number>_ und die Deklarationen der Variablen (_const v0, 0x1_)
- Wenn Sie den Logging-Code in die Mitte des Codes einer Funktion einfügen möchten:
- Fügen Sie 2 zur Anzahl der deklarierten Variablen hinzu: z.B. von _.locals 10_ zu _.locals 12_
- Die neuen Variablen sollten die nächsten Zahlen der bereits deklarierten Variablen sein (in diesem Beispiel sollten es _v10_ und _v11_ sein, denken Sie daran, dass es bei v0 beginnt).
- Ändern Sie den Code der Logging-Funktion und verwenden Sie _v10_ und _v11_ anstelle von _v5_ und _v1_.
- Wenn du deklarierte Variablen innerhalb der Funktion verwenden willst (deklariert v0,v1,v2...) füge diese Zeilen zwischen der _.local <number>_ und den Deklarationen der Variablen (_const v0, 0x1_) ein
- Wenn du den Logging-Code in die Mitte des Codes einer Funktion einfügen willst:
- Erhöhe die Anzahl der deklarierten Variablen um 2: z.B. von _.locals 10_ auf _.locals 12_
- Die neuen Variablen sollten die nächsten Nummern nach den bereits deklarierten Variablen sein (in diesem Beispiel sollten es _v10_ und _v11_ sein, denke daran, dass bei v0 begonnen wird).
- Ändere den Code der Logging-Funktion und verwende _v10_ und _v11_ statt _v5_ und _v1_.
### Toasting
Denken Sie daran, 3 zur Anzahl der _.locals_ zu Beginn der Funktion hinzuzufügen.
Denke daran, am Anfang der Funktion 3 zu der Anzahl der _.locals_ hinzuzufügen.
Dieser Code ist vorbereitet, um in die **Mitte einer Funktion** eingefügt zu werden (**ändern** Sie die Nummer der **Variablen** nach Bedarf). Er wird den **Wert von this.o** nehmen, ihn in **String** umwandeln und dann einen **Toast** mit seinem Wert **machen**.
Dieser Code ist dafür vorbereitet, in die **Mitte einer Funktion** eingefügt zu werden (**ändere** die Anzahl der **Variablen** nach Bedarf). Er nimmt den **Wert von this.o**, **wandelt** ihn in einen **String** und erstellt anschließend einen **toast** mit dessen Wert.
```bash
const/4 v10, 0x1
const/4 v11, 0x1
@ -161,4 +162,38 @@ invoke-static {p0, v11, v12}, Landroid/widget/Toast;->makeText(Landroid/content/
move-result-object v12
invoke-virtual {v12}, Landroid/widget/Toast;->show()V
```
### Laden einer nativen Bibliothek beim Start (System.loadLibrary)
Manchmal muss man eine native Bibliothek vorgeladen, damit sie sich vor anderen JNI-Libs initialisiert (z. B. um prozesslokale Telemetrie/Logging zu ermöglichen). Man kann einen Aufruf von System.loadLibrary() in einen statischen Initialisierer oder früh in Application.onCreate() injizieren. Beispiel smali für einen statischen Klassen-Initializer (<clinit>):
```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
```
Alternativ platzieren Sie dieselben beiden Anweisungen am Anfang Ihrer Application.onCreate(), um sicherzustellen, dass die library so früh wie möglich geladen wird:
```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
```
Hinweise:
- Stellen Sie sicher, dass die korrekte ABI-Variante der Bibliothek unter lib/<abi>/ vorhanden ist (z. B. arm64-v8a/armeabi-v7a), um UnsatisfiedLinkError zu vermeiden.
- Frühes Laden (class static initializer) garantiert, dass der native logger nachfolgende JNI-Aktivität beobachten kann.
## Referenzen
- SoTap: Leichtgewichtiger in-app JNI (.so) behavior logger [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
{{#include ../../banners/hacktricks-training.md}}