๋„ค์ดํ‹ฐ๋ธŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—ญ๋ถ„์„

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

์ถ”๊ฐ€ ์ •๋ณด: https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html

Android ์•ฑ์€ ์ผ๋ฐ˜์ ์œผ๋กœ C ๋˜๋Š” C++๋กœ ์ž‘์„ฑ๋œ ๋„ค์ดํ‹ฐ๋ธŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(.so)๋ฅผ ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ์ž‘์—…์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•…์„ฑ์ฝ”๋“œ ์ œ์ž‘์ž๋“ค๋„ ์ด๋Ÿฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์•…์šฉํ•ฉ๋‹ˆ๋‹ค. ELF shared objects๋Š” ์—ฌ์ „ํžˆ DEX/OAT ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ณด๋‹ค ๋””์ปดํŒŒ์ผํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ด ํŽ˜์ด์ง€๋Š” Android .so ํŒŒ์ผ์˜ ์—ญ๋ถ„์„์„ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“œ๋Š” ์‹ค์šฉ์ ์ธ ์›Œํฌํ”Œ๋กœ์™€ ์ตœ๊ทผ(2023โ€“2025) ๋„๊ตฌ ๊ฐœ์„  ์‚ฌํ•ญ์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.


๊ฐ“ ์ถ”์ถœํ•œ libfoo.so์— ๋Œ€ํ•œ ๋น ๋ฅธ ๋ถ„๋ฅ˜ ์›Œํฌํ”Œ๋กœ

  1. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”์ถœ
# From an installed application
adb shell "run-as <pkg> cat lib/arm64-v8a/libfoo.so" > libfoo.so
# Or from the APK (zip)
unzip -j target.apk "lib/*/libfoo.so" -d extracted_libs/
  1. ์•„ํ‚คํ…์ฒ˜ ๋ฐ ๋ณดํ˜ธ ์„ค์ • ์‹๋ณ„
file libfoo.so        # arm64 or arm32 / x86
readelf -h libfoo.so  # OS ABI, PIE, NX, RELRO, etc.
checksec --file libfoo.so  # (peda/pwntools)
  1. ๋‚ด๋ณด๋‚ธ ์‹ฌ๋ณผ ๋ฐ JNI ๋ฐ”์ธ๋”ฉ ๋‚˜์—ด
readelf -s libfoo.so | grep ' Java_'     # dynamic-linked JNI
strings libfoo.so   | grep -i "RegisterNatives" -n   # static-registered JNI
  1. ๋””์ปดํŒŒ์ผ๋Ÿฌ์— ๋กœ๋“œ (Ghidra โ‰ฅ 11.0, IDA Pro, Binary Ninja, Hopper or Cutter/Rizin) ๋ฐ ์ž๋™ ๋ถ„์„ ์‹คํ–‰. ์ƒˆ๋กœ์šด Ghidra ๋ฒ„์ „์€ AArch64 decompiler๋ฅผ ๋„์ž…ํ•˜์—ฌ PAC/BTI stubs์™€ MTE tags๋ฅผ ์ธ์‹ํ•˜๋ฏ€๋กœ Android 14 NDK๋กœ ๋นŒ๋“œ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถ„์„์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  2. ์ •์  vs ๋™์  ์—ญ๋ถ„์„ ๊ฒฐ์ •: stripped, obfuscated ์ฝ”๋“œ๋“ค์€ ์ข…์ข… instrumentation์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค (Frida, ptrace/gdbserver, LLDB).

Dynamic Instrumentation (Frida โ‰ฅ 16)

Frida์˜ 16 ์‹œ๋ฆฌ์ฆˆ๋Š” ๋Œ€์ƒ์ด ์ตœ์‹  Clang/LLD ์ตœ์ ํ™”๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋„์›€์ด ๋˜๋Š” ์—ฌ๋Ÿฌ Android ์ „์šฉ ๊ฐœ์„ ์‚ฌํ•ญ์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค:

  • thumb-relocator๋Š” ์ด์ œ LLD์˜ aggressive alignment(--icf=all)๋กœ ์ƒ์„ฑ๋œ ์ž‘์€ ARM/Thumb ํ•จ์ˆ˜๋ฅผ hookํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ELF import slots ์—ด๊ฑฐ ๋ฐ ์žฌ๋ฐ”์ธ๋”ฉ์ด Android์—์„œ ๋™์ž‘ํ•˜์—ฌ, inline hooks๊ฐ€ ๊ฑฐ๋ถ€๋  ๋•Œ ๋ชจ๋“ˆ๋ณ„ dlopen()/dlsym() ํŒจ์นญ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • Android 14์—์„œ ์•ฑ์ด --enable-optimizations๋กœ ์ปดํŒŒ์ผ๋  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์ƒˆ๋กœ์šด ART quick-entrypoint์— ๋Œ€ํ•œ Java hooking์ด ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ: RegisterNatives๋ฅผ ํ†ตํ•ด ๋“ฑ๋ก๋œ ๋ชจ๋“  ํ•จ์ˆ˜๋ฅผ ์—ด๊ฑฐํ•˜๊ณ  ๋Ÿฐํƒ€์ž„์— ๊ทธ ์ฃผ์†Œ๋ฅผ ๋คํ”„ํ•˜๋Š” ๋ฐฉ๋ฒ•:

Java.perform(function () {
var Runtime = Java.use('java.lang.Runtime');
var register = Module.findExportByName(null, 'RegisterNatives');
Interceptor.attach(register, {
onEnter(args) {
var envPtr  = args[0];
var clazz   = Java.cast(args[1], Java.use('java.lang.Class'));
var methods = args[2];
var count   = args[3].toInt32();
console.log('[+] RegisterNatives on ' + clazz.getName() + ' -> ' + count + ' methods');
// iterate & dump (JNI nativeMethod struct: name, sig, fnPtr)
}
});
});

Frida will work out of the box on PAC/BTI-enabled devices (Pixel 8/Android 14+) as long as you use frida-server 16.2 or later โ€“ earlier versions failed to locate padding for inline hooks.

Dumping runtime-decrypted native libraries from memory (Frida soSaver)

When a protected APK keeps native code encrypted or only maps it at runtime (packers, downloaded payloads, generated libs), attach Frida and dump the mapped ELF directly from process memory.

soSaver workflow (Python host + TS/JS Frida agent):

  • Hooks dlopen and android_dlopen_ext to detect load-time library mapping and performs an initial sweep of already loaded modules.
  • Periodically scans the process memory mappings for ELF headers to catch modules loaded through non-standard mappers that never hit the loader APIs.
  • Reads each module in blocks from memory and streams the bytes through Frida messages to the host; if a region cannot be read, it falls back to reading from the on-disk path when available.
  • Saves the reconstructed .so files and prints per-module extraction stats, providing artifacts for static RE.

Run (root + frida-server, Python โ‰ฅ3.8, uv):

git clone https://github.com/TheQmaks/sosaver.git
cd sosaver && uv sync
source .venv/bin/activate    # .venv\Scripts\activate on Windows

# target by package or PID; choose output/verbosity
sosaver com.example.app
sosaver 1234 -o /tmp/so-dumps --debug

์ด ๋ฐฉ๋ฒ•์€ โ€œonly decrypted in RAMโ€ ๋ณดํ˜ธ๋ฅผ ๋ณต๊ตฌ๋œ ๋ผ์ด๋ธŒ ๋งตํ•‘ ์ด๋ฏธ์ง€๋กœ ์šฐํšŒํ•˜์—ฌ, ํŒŒ์ผ์‹œ์Šคํ…œ ๋ณต์‚ฌ๋ณธ์ด ๋‚œ๋…ํ™”๋˜์–ด ์žˆ๊ฑฐ๋‚˜ ์—†์–ด๋„ IDA/Ghidra์—์„œ ์˜คํ”„๋ผ์ธ ๋ถ„์„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์ „ ๋กœ๋“œ๋œ .so๋ฅผ ํ†ตํ•œ ํ”„๋กœ์„ธ์Šค ๋กœ์ปฌ JNI ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ (SoTap)

๊ณ ๊ธ‰ ๊ณ„์ธก(instrumentation)์ด ๊ณผํ•˜๊ฑฐ๋‚˜ ์ฐจ๋‹จ๋œ ๊ฒฝ์šฐ์—๋„, ๋Œ€์ƒ ํ”„๋กœ์„ธ์Šค ๋‚ด๋ถ€์— ์ž‘์€ ๋กœ๊ฑฐ๋ฅผ ์‚ฌ์ „ ๋กœ๋“œํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ์ˆ˜์ค€์˜ ๊ฐ€์‹œ์„ฑ์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. SoTap์€ ๋™์ผ ์•ฑ ํ”„๋กœ์„ธ์Šค ๋‚ด์˜ ๋‹ค๋ฅธ JNI (.so) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋Ÿฐํƒ€์ž„ ๋™์ž‘์„ ๊ธฐ๋กํ•˜๋Š” ๊ฒฝ๋Ÿ‰ Android native (.so) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค (no root required).

Key properties:

  • ์ดˆ๊ธฐ์— ์ดˆ๊ธฐํ™”๋˜์–ด ๋กœ๋“œํ•œ ํ”„๋กœ์„ธ์Šค ๋‚ด์˜ JNI/native ์ƒํ˜ธ์ž‘์šฉ์„ ๊ด€์ฐฐํ•ฉ๋‹ˆ๋‹ค.
  • ์—ฌ๋Ÿฌ ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ๊ฒฝ๋กœ์— ๋กœ๊ทธ๋ฅผ ์ง€์† ์ €์žฅํ•˜๊ณ  ์ €์žฅ ๊ณต๊ฐ„์ด ์ œํ•œ๋  ๋•Œ๋Š” Logcat์œผ๋กœ ์šฐ์•„ํ•˜๊ฒŒ ํด๋ฐฑํ•ฉ๋‹ˆ๋‹ค.
  • Source-customizable: edit sotap.c to extend/adjust what gets logged and rebuild per ABI.

Setup (repack the APK):

  1. Drop the proper ABI build into the APK so the loader can resolve libsotap.so:
  • lib/arm64-v8a/libsotap.so (for arm64)
  • lib/armeabi-v7a/libsotap.so (for arm32)
  1. Ensure SoTap loads before other JNI libs. Inject a call early (e.g., Application subclass static initializer or onCreate) so the logger is initialized first. Smali snippet example:
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
  1. Rebuild/sign/install, run the app, then collect logs.

Log paths (checked in order):

/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

Notes and troubleshooting:

  • ABI ์ •๋ ฌ์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค. ๋ถˆ์ผ์น˜ํ•˜๋ฉด UnsatisfiedLinkError๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ๋กœ๊ฑฐ๊ฐ€ ๋กœ๋“œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ํ˜„๋Œ€ Android์—์„œ๋Š” ์ €์žฅ์†Œ ์ œ์•ฝ์ด ํ”ํ•ฉ๋‹ˆ๋‹ค; ํŒŒ์ผ ์“ฐ๊ธฐ๊ฐ€ ์‹คํŒจํ•˜๋”๋ผ๋„ SoTap์€ ์—ฌ์ „ํžˆ Logcat์„ ํ†ตํ•ด ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • ๋™์ž‘/์ถœ๋ ฅ ์ˆ˜์ค€(verbosity)์€ ์‚ฌ์šฉ์žํ™”ํ•˜๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค; sotap.c๋ฅผ ํŽธ์ง‘ํ•œ ํ›„ ์†Œ์Šค์—์„œ ์žฌ๋นŒ๋“œํ•˜์„ธ์š”.

์ด ์ ‘๊ทผ๋ฒ•์€ malware triage ๋ฐ JNI ๋””๋ฒ„๊น…์—์„œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์„ธ์Šค ์‹œ์ž‘ ์‹œ์ ์˜ ๋„ค์ดํ‹ฐ๋ธŒ ํ˜ธ์ถœ ํ๋ฆ„์„ ๊ด€์ฐฐํ•ด์•ผ ํ•˜๊ณ  ๋ฃจํŠธ/์‹œ์Šคํ…œ ์ „์—ญ ํ›…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์„ ๋•Œ ํŠนํžˆ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.


See also: inโ€‘memory native code execution via JNI

A common attack pattern is to download a raw shellcode blob at runtime and execute it directly from memory through a JNI bridge (no onโ€‘disk ELF). Details and readyโ€‘toโ€‘use JNI snippet here:

In Memory Jni Shellcode Execution


Recent vulnerabilities worth hunting for in APKs

์—ฐ๋„CVEAffected library๋ฉ”๋ชจ
2023CVE-2023-4863libwebp โ‰ค 1.3.1๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ์—์„œ WebP ์ด๋ฏธ์ง€๋ฅผ ๋””์ฝ”๋“œํ•  ๋•Œ ๋„๋‹ฌ ๊ฐ€๋Šฅํ•œ ํž™ ๋ฒ„ํผ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ. ์—ฌ๋Ÿฌ Android ์•ฑ์ด ์ทจ์•ฝํ•œ ๋ฒ„์ „์„ ๋ฒˆ๋“ค๋กœ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. APK ๋‚ด๋ถ€์— libwebp.so๊ฐ€ ๋ณด์ด๋ฉด ๋ฒ„์ „์„ ํ™•์ธํ•˜๊ณ  ์ต์Šคํ”Œ๋กœ์ž‡ ์‹œ๋„ ๋˜๋Š” ํŒจ์น˜ ์ ์šฉ์„ ๊ณ ๋ คํ•˜์„ธ์š”.
2024MultipleOpenSSL 3.x series์—ฌ๋Ÿฌ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „์„ฑ ๋ฐ ํŒจ๋”ฉ-์˜ค๋ผํด ๊ณ„์—ด ๋ฌธ์ œ. ๋งŽ์€ Flutter & ReactNative ๋ฒˆ๋“ค๋“ค์ด ์ž์ฒด libcrypto.so๋ฅผ ํฌํ•จํ•˜์—ฌ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค.

APK ๋‚ด๋ถ€์—์„œ ์„œ๋“œํŒŒํ‹ฐ .so ํŒŒ์ผ์„ ๋ฐœ๊ฒฌํ•˜๋ฉด, ํ•ญ์ƒ ํ•ด๋‹น ํ•ด์‹œ๋ฅผ ์—…์ŠคํŠธ๋ฆผ ๊ถŒ๊ณ ๋ฌธ๊ณผ ๊ต์ฐจ ํ™•์ธํ•˜์„ธ์š”. SCA (Software Composition Analysis)๋Š” ๋ชจ๋ฐ”์ผ์—์„œ ๋“œ๋ฌผ๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ž˜๋œ ์ทจ์•ฝํ•œ ๋นŒ๋“œ๊ฐ€ ๋„๋ฆฌ ํผ์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.


  • Pointer Authentication (PAC) & Branch Target Identification (BTI): Android 14๋Š” ์ง€์›๋˜๋Š” ARMv8.3+ ์‹ค๋ฆฌ์ฝ˜์—์„œ ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— PAC/BTI๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. Decompiler๋“ค์€ ์ด์ œ PAC ๊ด€๋ จ ์˜์‚ฌ-๋ช…๋ น์–ด๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค; ๋™์  ๋ถ„์„์—์„œ๋Š” Frida๊ฐ€ PAC๋ฅผ ์ œ๊ฑฐํ•œ ๋’ค ํŠธ๋žจํด๋ฆฐ์„ ์ฃผ์ž…ํ•˜์ง€๋งŒ, ์ปค์Šคํ…€ ํŠธ๋žจํด๋ฆฐ์€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ pacda/autibsp๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • MTE & Scudo hardened allocator: ๋ฉ”๋ชจ๋ฆฌ ํƒœ๊น…์€ ์˜ตํŠธ์ธ(opt-in)์ด์ง€๋งŒ ๋งŽ์€ Play-Integrity ์ธ์‹ ์•ฑ๋“ค์ด -fsanitize=memtag๋กœ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค; ํƒœ๊ทธ ์˜ค๋ฅ˜๋ฅผ ์บก์ฒ˜ํ•˜๋ ค๋ฉด setprop arm64.memtag.dump 1๊ณผ ํ•จ๊ป˜ adb shell am start ...๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.
  • LLVM Obfuscator (opaque predicates, control-flow flattening): ์ƒ์šฉ ํŒจ์ปค๋“ค(์˜ˆ: Bangcle, SecNeo)์€ ์ ์  ๋” ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋ฅผ ๋ณดํ˜ธํ•ฉ๋‹ˆ๋‹ค. .rodata์— ๊ฐ€์งœ ์ œ์–ด ํ๋ฆ„๊ณผ ์•”ํ˜ธํ™”๋œ ๋ฌธ์ž์—ด ๋ธ”๋กญ์ด ์žˆ์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜์„ธ์š”.

Neutralizing early native initializers (.init_array) and JNI_OnLoad for early instrumentation (ARM64 ELF)

๋†’์ด ๋ณดํ˜ธ๋œ ์•ฑ๋“ค์€ ์ข…์ข… ๋งค์šฐ ์ดˆ๊ธฐ ๋‹จ๊ณ„์— .init_array๋ฅผ ํ†ตํ•ด ์‹คํ–‰๋˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ƒ์„ฑ์ž(constructor)์— ๋ฃจํŠธ/์—๋ฎฌ๋ ˆ์ดํ„ฐ/๋””๋ฒ„๊ทธ ๊ฒ€์‚ฌ๋ฅผ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋“ค์€ JNI_OnLoad๋ณด๋‹ค ํ›จ์”ฌ ์ด์ „, Java ์ฝ”๋“œ ์‹คํ–‰ ์ด์ „์— ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์•”๋ฌต์  ์ดˆ๊ธฐํ™”์ž๋“ค์„ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ”๊ฟ” ์ œ์–ด๊ถŒ์„ ํšŒ๋ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • DYNAMIC ํ…Œ์ด๋ธ”์—์„œ INIT_ARRAY/INIT_ARRAYSZ๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ ๋กœ๋”๊ฐ€ .init_array ์—”ํŠธ๋ฆฌ๋ฅผ ์ž๋™ ์‹คํ–‰ํ•˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • RELATIVE ์žฌ๋ฐฐ์น˜(relocations)์—์„œ ์ƒ์„ฑ์ž ์ฃผ์†Œ๋ฅผ ํ•ด์„ํ•˜์—ฌ ์ผ๋ฐ˜ ํ•จ์ˆ˜ ์‹ฌ๋ณผ(์˜ˆ: INIT0)๋กœ ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • JNI_OnLoad์˜ ์ด๋ฆ„์„ JNI_OnLoad0์œผ๋กœ ๋ฐ”๊ฟ” ART๊ฐ€ ์•”๋ฌต์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

Android/arm64์—์„œ ์ด ๋ฐฉ๋ฒ•์ด ์ž‘๋™ํ•˜๋Š” ์ด์œ 

  • AArch64์—์„œ .init_array ์—”ํŠธ๋ฆฌ๋Š” ์ข…์ข… R_AARCH64_RELATIVE ์žฌ๋ฐฐ์น˜์— ์˜ํ•ด ๋กœ๋“œ ์‹œ์ ์— ์ฑ„์›Œ์ง€๋ฉฐ, addend๋Š” .text ๋‚ด๋ถ€์˜ ๋Œ€์ƒ ํ•จ์ˆ˜ ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค.
  • .init_array์˜ ๋ฐ”์ดํŠธ๋Š” ์ •์  ์ƒํƒœ์—์„œ ๋น„์–ด ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค; ๋™์  ๋ง์ปค๋Š” ์žฌ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์ค‘์— ํ•ด์„๋œ ์ฃผ์†Œ๋ฅผ ์”๋‹ˆ๋‹ค.

์ƒ์„ฑ์ž ๋Œ€์ƒ ์‹๋ณ„

  • AArch64์—์„œ ์ •ํ™•ํ•œ ELF ํŒŒ์‹ฑ์„ ์œ„ํ•ด Android NDK ํˆด์ฒด์ธ์„ ์‚ฌ์šฉํ•˜์„ธ์š”:
# Adjust paths to your NDK; use the aarch64-linux-android-* variants
readelf -W -a ./libnativestaticinit.so | grep -n "INIT_ARRAY" -C 4
readelf -W --relocs ./libnativestaticinit.so
  • .init_array ๊ฐ€์ƒ ์ฃผ์†Œ ๋ฒ”์œ„ ์•ˆ์— ๋“ค์–ด์˜ค๋Š” ์žฌ๋ฐฐ์น˜๋ฅผ ์ฐพ์œผ์„ธ์š”; ํ•ด๋‹น R_AARCH64_RELATIVE์˜ addend๊ฐ€ ์ƒ์„ฑ์ž์ž…๋‹ˆ๋‹ค(์˜ˆ: 0xA34, 0x954).
  • ํ•ด๋‹น ์ฃผ์†Œ ์ฃผ๋ณ€์„ ์—ญ์–ด์…ˆ๋ธ”ํ•˜์—ฌ ํ™•์ธํ•˜์„ธ์š”:
objdump -D ./libnativestaticinit.so --start-address=0xA34 | head -n 40

ํŒจ์น˜ ๊ณ„ํš

  1. DYNAMIC ํƒœ๊ทธ์—์„œ INIT_ARRAY ๋ฐ INIT_ARRAYSZ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์„น์…˜์„ ์‚ญ์ œํ•˜์ง€ ๋งˆ์„ธ์š”.
  2. ์ƒ์„ฑ์ž ์ฃผ์†Œ์— GLOBAL DEFAULT FUNC ์‹ฌ๋ณผ INIT0๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ˆ˜๋™์œผ๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  3. JNI_OnLoad โ†’ JNI_OnLoad0๋กœ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜์—ฌ ART๊ฐ€ ์•”๋ฌต์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

Validation after patch

readelf -W -d libnativestaticinit.so.patched | egrep -i 'init_array|fini_array|flags'
readelf -W -s libnativestaticinit.so.patched | egrep 'INIT0|JNI_OnLoad0'

LIEF๋กœ ํŒจ์น˜ํ•˜๊ธฐ (Python)

์Šคํฌ๋ฆฝํŠธ: INIT_ARRAY/INIT_ARRAYSZ ์ œ๊ฑฐ, export INIT0, JNI_OnLoadโ†’JNI_OnLoad0 ์ด๋ฆ„ ๋ณ€๊ฒฝ ```python import lief

b = lief.parse(โ€œlibnativestaticinit.soโ€)

Locate .init_array VA range

init = b.get_section(โ€˜.init_arrayโ€™) va, sz = init.virtual_address, init.size

Compute constructor address from RELATIVE relocation landing in .init_array

ctor = None for r in b.dynamic_relocations: if va <= r.address < va + sz: ctor = r.addend break if ctor is None: raise RuntimeError(โ€œNo R_*_RELATIVE relocation found inside .init_arrayโ€)

Remove auto-run tags so loader skips .init_array

for tag in (lief.ELF.DYNAMIC_TAGS.INIT_ARRAYSZ, lief.ELF.DYNAMIC_TAGS.INIT_ARRAY): try: b.remove(b[tag]) except Exception: pass

Add exported FUNC symbol INIT0 at constructor address

sym = lief.ELF.Symbol() sym.name = โ€˜INIT0โ€™ sym.value = ctor sym.size = 0 sym.binding = lief.ELF.SYMBOL_BINDINGS.GLOBAL sym.type = lief.ELF.SYMBOL_TYPES.FUNC sym.visibility = lief.ELF.SYMBOL_VISIBILITY.DEFAULT

Place symbol in .text index

text = b.get_section(โ€˜.textโ€™) for idx, sec in enumerate(b.sections): if sec == text: sym.shndx = idx break b.add_dynamic_symbol(sym)

Rename JNI_OnLoad -> JNI_OnLoad0 to block implicit ART init

j = b.get_symbol(โ€˜JNI_OnLoadโ€™) if j: j.name = โ€˜JNI_OnLoad0โ€™

b.write(โ€˜libnativestaticinit.so.patchedโ€™)

</details>

๋ฉ”๋ชจ ๋ฐ ์‹คํŒจํ•œ ์ ‘๊ทผ๋ฒ•(์ด์‹์„ฑ)
- `.init_array` ๋ฐ”์ดํŠธ๋ฅผ 0์œผ๋กœ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์„น์…˜ ๊ธธ์ด๋ฅผ 0์œผ๋กœ ์„ค์ •ํ•ด๋„ ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: the dynamic linker๊ฐ€ relocations์„ ํ†ตํ•ด ์ด๋ฅผ ๋‹ค์‹œ ์ฑ„์›๋‹ˆ๋‹ค.
- `INIT_ARRAY`/`INIT_ARRAYSZ`๋ฅผ 0์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ํƒœ๊ทธ ๋ถˆ์ผ์น˜๋กœ ์ธํ•ด loader๊ฐ€ ์†์ƒ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น DYNAMIC ํ•ญ๋ชฉ๋“ค์„ ๊น”๋”ํ•˜๊ฒŒ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
- `.init_array` ์„น์…˜์„ ์™„์ „ํžˆ ์‚ญ์ œํ•˜๋ฉด loader๊ฐ€ ์ถฉ๋Œํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
- ํŒจ์น˜ ํ›„์—๋Š” ํ•จ์ˆ˜/๋ ˆ์ด์•„์›ƒ ์ฃผ์†Œ๊ฐ€ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค; ํŒจ์น˜๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ํŒจ์น˜๋œ ํŒŒ์ผ์—์„œ `.rela.dyn` addends๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ์ž(constructor)๋ฅผ ํ•ญ์ƒ ์žฌ๊ณ„์‚ฐํ•˜์„ธ์š”.

์ตœ์†Œํ•œ์˜ ART/JNI ์ดˆ๊ธฐํ™”๋กœ INIT0 ๋ฐ JNI_OnLoad0 ํ˜ธ์ถœํ•˜๊ธฐ
- JNIInvocation์„ ์‚ฌ์šฉํ•ด ๋…๋ฆฝ ์‹คํ–‰ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์ž‘์€ ART VM ์ปจํ…์ŠคํŠธ๋ฅผ ๊ตฌ๋™ํ•˜์„ธ์š”. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์–ด๋–ค Java ์ฝ”๋“œ๋ณด๋‹ค ๋จผ์ € ์ˆ˜๋™์œผ๋กœ `INIT0()`์™€ `JNI_OnLoad0(vm)`๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
- ํƒ€๊นƒ APK/classes๋ฅผ classpath์— ํฌํ•จ์‹œ์ผœ `RegisterNatives`๊ฐ€ Java ํด๋ž˜์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜์„ธ์š”.

<details>
<summary>์ตœ์†Œ ํ—ˆ๋‹ˆ์Šค (CMake ๋ฐ C) โ€” INIT0 โ†’ JNI_OnLoad0 โ†’ Java ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ</summary>
```cmake
# CMakeLists.txt
project(caller)
cmake_minimum_required(VERSION 3.8)
include_directories(AFTER ${CMAKE_SOURCE_DIR}/include)
link_directories(${CMAKE_SOURCE_DIR}/lib)
find_library(log-lib log REQUIRED)
add_executable(caller "caller.c")
add_library(jenv SHARED "jnihelper.c")
target_link_libraries(caller jenv nativestaticinit)
// caller.c
#include <jni.h>
#include "jenv.h"
JavaCTX ctx;
void INIT0();
void JNI_OnLoad0(JavaVM* vm);
int main(){
char *jvmopt = "-Djava.class.path=/data/local/tmp/base.apk"; // include app classes
if (initialize_java_environment(&ctx,&jvmopt,1)!=0) return -1;
INIT0();                   // manual constructor
JNI_OnLoad0(ctx.vm);       // manual JNI init
jclass c = (*ctx.env)->FindClass(ctx.env, "eu/nviso/nativestaticinit/MainActivity");
jmethodID m = (*ctx.env)->GetStaticMethodID(ctx.env,c,"stringFromJNI","()Ljava/lang/String;");
jstring s = (jstring)(*ctx.env)->CallStaticObjectMethod(ctx.env,c,m);
const char* p = (*ctx.env)->GetStringUTFChars(ctx.env,s,NULL);
printf("Native string: %s\n", p);
cleanup_java_env(&ctx);
}
# Build (adjust NDK/ABI)
cmake -DANDROID_PLATFORM=31 \
-DCMAKE_TOOLCHAIN_FILE=$HOME/Android/Sdk/ndk/26.1.10909125/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a ..
make

์ผ๋ฐ˜์ ์ธ ํ•จ์ •:

  • ์ƒ์„ฑ์ž ์ฃผ์†Œ๋Š” ๋ฆฌ๋ ˆ์ด์•„์›ƒ ๋•Œ๋ฌธ์— ํŒจ์น˜ ํ›„ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค; ์ตœ์ข… ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ .rela.dyn์œผ๋กœ ํ•ญ์ƒ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜์„ธ์š”.
  • -Djava.class.path๊ฐ€ RegisterNatives ํ˜ธ์ถœ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“  ํด๋ž˜์Šค๋ฅผ ํฌํ•จํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.
  • ๋™์ž‘์€ NDK/loader ๋ฒ„์ „์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค; ์ผ๊ด€๋˜๊ฒŒ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๋‹จ๊ณ„๋Š” INIT_ARRAY/INIT_ARRAYSZ DYNAMIC ํƒœ๊ทธ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ