๋„ค์ดํ‹ฐ๋ธŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฆฌ๋ฒ„์‹ฑ

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++๋กœ ์ž‘์„ฑ๋œ ๋„ค์ดํ‹ฐ๋ธŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ์ž‘์—…์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์•…์„ฑ์ฝ”๋“œ ์ œ์ž‘์ž๋“ค๋„ ELF shared objects๊ฐ€ DEX/OAT byte-code๋ณด๋‹ค ๋””์ปดํŒŒ์ผํ•˜๊ธฐ ๋” ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์•…์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ํŽ˜์ด์ง€๋Š” 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)์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋™์  ๊ณ„์ธก (Frida โ‰ฅ 16)

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

  • thumb-relocator๋Š” ์ด์ œ LLD์˜ aggressive alignment (--icf=all)๋กœ ์ƒ์„ฑ๋œ ์ž‘์€ ARM/Thumb ํ•จ์ˆ˜๋ฅผ hookํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Android์—์„œ ELF import slots๋ฅผ ์—ด๊ฑฐํ•˜๊ณ  ์žฌ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒƒ์ด ์ž‘๋™ํ•˜์—ฌ, 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๋Š” PAC/BTI-enabled devices (Pixel 8/Android 14+)์—์„œ frida-server 16.2 ์ด์ƒ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ„๋„ ์„ค์ • ์—†์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค โ€“ ์ด์ „ ๋ฒ„์ „์€ inline hooks์˜ ํŒจ๋”ฉ์„ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

Process-local JNI telemetry via preloaded .so (SoTap)

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

์ฃผ์š” ํŠน์ง•:

  • ์ดˆ๊ธฐ์— ์ดˆ๊ธฐํ™”๋˜๋ฉฐ, ๊ทธ๊ฒƒ์„ ๋กœ๋“œํ•˜๋Š” ํ”„๋กœ์„ธ์Šค ๋‚ด๋ถ€์˜ JNI/native ์ƒํ˜ธ์ž‘์šฉ์„ ๊ด€์ฐฐํ•ฉ๋‹ˆ๋‹ค.
  • ์—ฌ๋Ÿฌ ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ๊ฒฝ๋กœ์— ๋กœ๊ทธ๋ฅผ ์ €์žฅํ•˜๊ณ , ์ €์žฅ์†Œ๊ฐ€ ์ œํ•œ๋  ๋•Œ๋Š” Logcat์œผ๋กœ ์šฐ์•„ํ•˜๊ฒŒ ํด๋ฐฑํ•ฉ๋‹ˆ๋‹ค.
  • Source-customizable: sotap.c๋ฅผ ํŽธ์ง‘ํ•ด ๋กœ๊น… ํ•ญ๋ชฉ์„ ํ™•์žฅ/์กฐ์ •ํ•˜๊ณ  ABI๋ณ„๋กœ ์žฌ๋นŒ๋“œํ•˜์„ธ์š”.

์„ค์ • (repack the APK):

  1. ์ ์ ˆํ•œ ABI ๋นŒ๋“œ๋ฅผ APK์— ๋„ฃ์–ด ๋กœ๋”๊ฐ€ libsotap.so๋ฅผ ํ•ด์„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค:
  • lib/arm64-v8a/libsotap.so (for arm64)
  • lib/armeabi-v7a/libsotap.so (for arm32)
  1. SoTap์ด ๋‹ค๋ฅธ JNI ๋ผ์ด๋ธŒ๋ณด๋‹ค ๋จผ์ € ๋กœ๋“œ๋˜๋„๋ก ํ•˜์„ธ์š”. ๋กœ๊ฑฐ๊ฐ€ ๋จผ์ € ์ดˆ๊ธฐํ™”๋˜๋„๋ก ํ˜ธ์ถœ์„ ์ดˆ๊ธฐ์— ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: Application subclass static initializer ๋˜๋Š” onCreate). Smali ์Šค๋‹ˆํŽซ ์˜ˆ:
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
  1. ์žฌ๋นŒ๋“œ/์„œ๋ช…/์„ค์น˜ ํ›„ ์•ฑ์„ ์‹คํ–‰ํ•˜๊ณ  ๋กœ๊ทธ๋ฅผ ์ˆ˜์ง‘ํ•˜์„ธ์š”.

๋กœ๊ทธ ๊ฒฝ๋กœ (ํ™•์ธ ์ˆœ์„œ):

/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์„ ํ†ตํ•ด ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • ๋™์ž‘/์ถœ๋ ฅ ์ˆ˜์ค€์€ ์‚ฌ์šฉ์ž ๋งž์ถค์„ ์—ผ๋‘์— ๋‘” ๊ฒƒ์ž…๋‹ˆ๋‹ค; sotap.c๋ฅผ ํŽธ์ง‘ํ•œ ํ›„ ์†Œ์Šค์—์„œ ์žฌ๋นŒ๋“œํ•˜์„ธ์š”.

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


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

์ผ๋ฐ˜์ ์ธ ๊ณต๊ฒฉ ํŒจํ„ด์€ ๋Ÿฐํƒ€์ž„์— ์›์‹œ shellcode blob์„ ๋‹ค์šด๋กœ๋“œํ•˜์—ฌ JNI ๋ธŒ๋ฆฌ์ง€๋ฅผ ํ†ตํ•ด ๋””์Šคํฌ์— ELF๋ฅผ ์“ฐ์ง€ ์•Š๊ณ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ง์ ‘ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ๊ณผ ์ฆ‰์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” JNI ์Šค๋‹ˆํŽซ์€ ๋‹ค์Œ์„ ์ฐธ์กฐํ•˜์„ธ์š”:

In Memory Jni Shellcode Execution


APK์—์„œ ์ฐพ์•„๋ณผ ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š” ์ตœ๊ทผ ์ทจ์•ฝ์ 

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

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


์•ˆํ‹ฐ ๋ฆฌ๋ฒ„์‹ฑ ๋ฐ ํ•˜๋“œ๋‹ ํŠธ๋ Œ๋“œ (Android 13-15)

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

์ดˆ๊ธฐ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋‹ˆ์…œ๋ผ์ด์ €(.init_array)์™€ JNI_OnLoad ๋น„ํ™œ์„ฑํ™”๋กœ ์กฐ๊ธฐ ๊ณ„์ธก ํ™•๋ณด (ARM64 ELF)

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

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

์™œ Android/arm64์—์„œ ๋™์ž‘ํ•˜๋Š”๊ฐ€

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

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

  • AArch64์—์„œ ์ •ํ™•ํ•œ ELF ํŒŒ์‹ฑ์„ ์œ„ํ•ด Android NDK toolchain์„ ์‚ฌ์šฉํ•˜์„ธ์š”:
# 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 ์ œ๊ฑฐ, 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์œผ๋กœ ์„ค์ •ํ•ด๋„ ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: ๋™์  ๋ง์ปค๊ฐ€ relocations๋ฅผ ํ†ตํ•ด ์ด๋ฅผ ๋‹ค์‹œ ์ฑ„์›๋‹ˆ๋‹ค.
- `INIT_ARRAY`/`INIT_ARRAYSZ`๋ฅผ 0์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ํƒœ๊ทธ ๋ถˆ์ผ์น˜๋กœ ์ธํ•ด ๋กœ๋”๊ฐ€ ๊นจ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น DYNAMIC ์—”ํŠธ๋ฆฌ๋ฅผ ๊น”๋”ํžˆ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
- `.init_array` ์„น์…˜์„ ์™„์ „ํžˆ ์‚ญ์ œํ•˜๋ฉด ๋กœ๋”๊ฐ€ ์ถฉ๋Œํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
- ํŒจ์น˜ ํ›„์—๋Š” ํ•จ์ˆ˜/๋ ˆ์ด์•„์›ƒ ์ฃผ์†Œ๊ฐ€ ์ด๋™ํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ, ํŒจ์น˜๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ํŒจ์น˜๋œ ํŒŒ์ผ์˜ `.rela.dyn` addends์—์„œ ํ•ญ์ƒ ์ƒ์„ฑ์ž(constructor)๋ฅผ ์žฌ๊ณ„์‚ฐํ•˜์„ธ์š”.

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

<details>
<summary>INIT0 โ†’ JNI_OnLoad0 โ†’ Java method๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•œ ์ตœ์†Œ ํ•˜๋‹ˆ์Šค (CMake and C)</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

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

  • ์ƒ์„ฑ์ž ์ฃผ์†Œ๋Š” ์žฌ๋ฐฐ์น˜(re-layout)๋กœ ์ธํ•ด ํŒจ์น˜ ํ›„ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค; ์ตœ์ข… ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ํ•ญ์ƒ .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 ์ง€์›ํ•˜๊ธฐ