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

This commit is contained in:
Translator 2025-09-04 02:40:17 +00:00
parent 48d672cf31
commit 49005c74f8
3 changed files with 211 additions and 123 deletions

View File

@ -24,8 +24,8 @@ sudo apt-get install -y yara
```
#### 규칙 준비
Use this script to download and merge all the yara malware rules from github: [https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9](https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9)\
Create the _**rules**_ directory and execute it. This will create a file called _**malware_rules.yar**_ which contains all the yara rules for malware.
이 스크립트를 사용하여 github에서 모든 yara malware rules를 다운로드하고 병합하세요: [https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9](https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9)\
_**rules**_ 디렉토리를 생성하고 스크립트를 실행하세요. 그러면 malware에 대한 모든 yara rules를 포함하는 _**malware_rules.yar**_ 파일이 생성됩니다.
```bash
wget https://gist.githubusercontent.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9/raw/4ec711d37f1b428b63bed1f786b26a0654aa2f31/malware_yara_rules.py
mkdir rules
@ -36,9 +36,9 @@ python malware_yara_rules.py
yara -w malware_rules.yar image #Scan 1 file
yara -w malware_rules.yar folder #Scan the whole folder
```
#### YaraGen: 악성코드 확인 및 규칙 생성
#### YaraGen: malware 확인 및 규칙 생성
You can use the tool [**YaraGen**](https://github.com/Neo23x0/yarGen) to generate yara rules from a binary. Check out these tutorials: [**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/)
도구 [**YaraGen**](https://github.com/Neo23x0/yarGen)을(를) 사용하여 binary에서 yara rules를 생성할 수 있습니다. 다음 튜토리얼을 확인하세요: [**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/
@ -49,7 +49,7 @@ python3.exe yarGen.py --excludegood -m ../../mals/
```
sudo apt-get install -y clamav
```
#### 스캔
#### Scan
```bash
sudo freshclam #Update rules
clamscan filepath #Scan 1 file
@ -57,26 +57,25 @@ clamscan folderpath #Scan the whole folder
```
### [Capa](https://github.com/mandiant/capa)
**Capa**는 실행 파일에서 잠재적으로 악의적인 **기능**을 감지합니다: PE, ELF, .NET. 따라서 Att\&ck 전술이나 다음과 같은 의심스러운 기능을 찾습니다:
**Capa**는 실행 파일(PE, ELF, .NET)에서 잠재적으로 악성인 **capabilities**를 탐지합니다. 따라서 Att\&ck tactics와 같은 항목이나 다음과 같은 의심스러운 **capabilities**를 찾아냅니다:
- OutputDebugString 오류 확인
- 서비스로 실행
- 프로세스 생성
[**Github repo**](https://github.com/mandiant/capa)에서 다운로드하세요.
다음에서 구하세요: [**Github repo**](https://github.com/mandiant/capa).
### IOCs
IOC는 침해 지표(Indicator Of Compromise)를 의미합니다. IOC는 잠재적으로 원치 않는 소프트웨어나 확인된 **악성코드**를 식별하는 **조건의 집합**입니다. 블루 팀은 이러한 정의를 사용하여 **시스템**과 **네트워크**에서 이러한 종류의 악성 파일을 **검색**합니다.\
러한 정의를 공유하는 것은 매우 유용합니다. 컴퓨터에서 악성코드가 식별되고 해당 악성코드에 대한 IOC가 생성되면, 다른 블루 팀이 이를 사용하여 악성코드를 더 빠르게 식별할 수 있습니다.
IOC는 Indicator Of Compromise의 약자입니다. IOC는 잠재적으로 원치 않는 소프트웨어나 확정된 **malware**를 식별하는 **식별하는 조건들**의 집합입니다. Blue Teams는 이러한 정의를 사용해 자신들의 **시스템**과 **네트워크**에서 해당 유형의 악성 파일을 **검색**합니다.\
정의들을 공유하는 것은 매우 유용합니다. 어떤 컴퓨터에서 malware가 식별되어 그 malware에 대한 IOC가 만들어지면, 다른 Blue Teams는 이를 사용해 malware를 더 빠르게 식별할 수 있습니다.
IOC를 생성하거나 수정하는 도구는 [**IOC Editor**](https://www.fireeye.com/services/freeware/ioc-editor.html)**입니다.**\
[**Redline**](https://www.fireeye.com/services/freeware/redline.html)과 같은 도구를 사용하여 **장치에서 정의된 IOC를 검색**할 수 있습니다.
IOC를 생성하거나 수정하는 도구로는 [**IOC Editor**](https://www.fireeye.com/services/freeware/ioc-editor.html)**.**\\ [**Redline**](https://www.fireeye.com/services/freeware/redline.html)과 같은 도구를 사용하여 장치에서 정의된 IOC를 **검색할 수 있습니다**.
### Loki
[**Loki**](https://github.com/Neo23x0/Loki)는 간단한 침해 지표를 위한 스캐너입니다.\
탐지는 네 가지 탐지 방법을 기반으로 합니다:
[**Loki**](https://github.com/Neo23x0/Loki)는 Simple Indicators of Compromise용 스캐너입니다.\\
탐지는 네 가지 탐지 방법에 기반합니다:
```
1. File Name IOC
Regex match on full file path/name
@ -92,41 +91,41 @@ 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/)는 GNU GPLv2 라이센스 하에 출시된 리눅스용 악성코드 스캐너로, 공유 호스팅 환경에서 직면하는 위협을 중심으로 설계되었습니다. 이 도구는 네트워크 엣지 침입 탐지 시스템의 위협 데이터를 사용하여 공격에 적극적으로 사용되는 악성코드를 추출하고 탐지를 위한 서명을 생성합니다. 또한, 위협 데이터는 LMD 체크아웃 기능과 악성코드 커뮤니티 리소스에서 사용자 제출을 통해 파생됩니다.
[**Linux Malware Detect (LMD)**](https://www.rfxn.com/projects/linux-malware-detect/)는 GNU GPLv2 라이선스로 배포되는 Linux용 malware 스캐너로, 공유 호스팅 환경에서 직면하는 위협을 중심으로 설계되었습니다. 네트워크 엣지 침입 탐지 시스템으로부터의 위협 데이터를 사용하여 공격에 실제로 사용되는 malware를 추출하고 탐지용 시그니처를 생성합니다. 또한 위협 데이터는 LMD checkout 기능을 통한 사용자 제출과 malware 커뮤니티 리소스에서도 얻습니다.
### rkhunter
[**rkhunter**](http://rkhunter.sourceforge.net)와 같은 도구는 파일 시스템에서 가능한 **루트킷**과 악성코드를 확인하는 데 사용할 수 있습니다.
[**rkhunter**](http://rkhunter.sourceforge.net)와 같은 도구는 파일 시스템에서 가능한 **rootkits** 및 malware를 검사하는 데 사용할 수 있습니다.
```bash
sudo ./rkhunter --check -r / -l /tmp/rkhunter.log [--report-warnings-only] [--skip-keypress]
```
### FLOSS
[**FLOSS**](https://github.com/mandiant/flare-floss)는 다양한 기술을 사용하여 실행 파일 내에서 난독화된 문자열을 찾으려고 시도하는 도구입니다.
[**FLOSS**](https://github.com/mandiant/flare-floss)는 다양한 기법을 사용해 실행 파일 내부의 obfuscated strings를 찾아내려고 시도하는 도구입니다.
### PEpper
[PEpper](https://github.com/Th3Hurrican3/PEpper)는 실행 파일 내의 일부 기본 사항(바이너리 데이터, 엔트로피, URL 및 IP, 일부 yara 규칙)을 확인합니다.
[PEpper ](https://github.com/Th3Hurrican3/PEpper)checks 일부 실행 파일 내부의 기본 항목들(바이너리 데이터, entropy, URLs and IPs, 일부 yara rules)을 검사합니다.
### PEstudio
[PEstudio](https://www.winitor.com/download)는 Windows 실행 파일에 대한 정보(수입, 수출, 헤더 등)를 얻을 수 있는 도구이며, 또한 바이러스 토탈을 확인하고 잠재적인 Att\&ck 기술을 찾습니다.
[PEstudio](https://www.winitor.com/download)는 imports, exports, headers 등 Windows 실행 파일의 정보를 얻을 수 있게 해주며, virus total을 확인하고 잠재적인 Att\&ck techniques를 찾아냅니다.
### Detect It Easy(DiE)
[**DiE**](https://github.com/horsicq/Detect-It-Easy/)는 파일이 **암호화**되어 있는지 감지하고 **패커**를 찾는 도구입니다.
[**DiE**](https://github.com/horsicq/Detect-It-Easy/)는 파일이 **encrypted** 되었는지 감지하고 **packers**도 찾아내는 도구입니다.
### NeoPI
[**NeoPI**](https://github.com/CiscoCXSecurity/NeoPI)는 텍스트/스크립트 파일 내에서 **난독화** 및 **암호화**된 콘텐츠를 감지하기 위해 다양한 **통계적 방법**을 사용하는 Python 스크립트입니다. NeoPI의 의도된 목적은 **숨겨진 웹 셸 코드**의 **탐지**를 돕는 것입니다.
[**NeoPI** ](https://github.com/CiscoCXSecurity/NeoPI)은 텍스트/스크립트 파일 내의 **obfuscated****encrypted** 내용을 감지하기 위해 다양한 **statistical methods**를 사용하는 Python 스크립트입니다. NeoPI의 목적은 **detection of hidden web shell code**을 돕는 것입니다.
### **php-malware-finder**
[**PHP-malware-finder**](https://github.com/nbs-system/php-malware-finder)**난독화된**/**의심스러운 코드**와 **악성코드**/웹 셸에서 자주 사용되는 **PHP** 함수를 사용하는 파일을 감지하기 위해 최선을 다합니다.
[**PHP-malware-finder**](https://github.com/nbs-system/php-malware-finder)**obfuscated**/**dodgy code**뿐만 아니라 자주 악용되는 **PHP** 함수들을 사용하는 파일(예: **malwares**/webshells)을 탐지하려 최선을 다합니다.
### Apple Binary Signatures
일부 **악성코드 샘플**을 확인할 때는 항상 바이너리의 **서명**을 확인해야 하며, 서명한 **개발자**가 이미 **악성코드**와 **관련**되어 있을 수 있습니다.
어떤 **malware sample**을 확인할 때는 서명한 **developer**가 이미 **related** with **malware**일 수 있으므로 항상 바이너리의 **check the signature**를 해야 합니다.
```bash
#Get signer
codesign -vv -d /bin/ls 2>&1 | grep -E "Authority|TeamIdentifier"
@ -137,29 +136,41 @@ codesign --verify --verbose /Applications/Safari.app
#Check if the signature is valid
spctl --assess --verbose /Applications/Safari.app
```
## 탐지 기
## 탐지 기
### 파일 스태킹
### File Stacking
웹 서버의 **파일**이 포함된 폴더가 **마지막으로 업데이트된 날짜**를 알고 있다면, **웹 서버의 모든 파일**이 **생성 및 수정된 날짜**를 **확인**하고, 어떤 날짜가 **의심스럽다면**, 해당 파일을 확인하세요.
만약 웹 서버의 **파일**들을 포함한 어떤 폴더가 **마지막으로 업데이트된 날짜**를 알고 있다면, 웹 서버 내 모든 **파일들의 생성 및 수정 날짜**를 **확인**하고 어떤 날짜가 **의심스럽다면**, 해당 파일을 조사하라.
### 기준선
### Baselines
폴더의 파일이 **수정되지 않아야 한다면**, 폴더의 **원본 파일**의 **해시**를 계산하고 **현재** 파일과 **비교**할 수 있습니다. 수정된 것은 **의심스럽습니다**.
폴더의 파일이 **변경되지 않았어야 한다면**, 그 폴더의 **원본 파일**들의 **해시(hash)**를 계산하여 **현재** 파일들과 **비교**할 수 있다. 변경된 항목은 **의심스러울** 것이다.
### 통계 분석
### Statistical Analysis
정보가 로그에 저장될 때, 각 웹 서버의 파일이 얼마나 자주 접근되었는지와 같은 **통계를 확인**할 수 있습니다. 웹 셸이 가장 많이 접근될 수 있습니다.
정보가 로그에 저장되어 있다면, 웹 서버의 각 파일이 몇 번 접근되었는지 같은 통계를 **확인**할 수 있다 — web shell이 가장 많이 접근된 파일 중 하나일 수 있다.
---
## 동적 제어 흐름 디오브스큐레이션 해제 (JMP/CALL RAX 디스패처)
### Android in-app native telemetry (no root)
현대의 악성코드 패밀리는 제어 흐름 그래프 (CFG) 난독화를 심각하게 악용합니다: 직접적인 점프/호출 대신 런타임에 목적지를 계산하고 `jmp rax` 또는 `call rax`를 실행합니다. 작은 *디스패처* (일반적으로 아홉 개의 명령어)는 CPU의 `ZF`/`CF` 플래그에 따라 최종 대상을 설정하여 정적 CFG 복구를 완전히 방해합니다.
Android에서는 다른 JNI 라이브러리가 초기화되기 전에 작은 로거 라이브러리를 프리로딩함으로써 타깃 앱 프로세스 내부의 native 코드를 계측할 수 있다. 이는 시스템 전역 훅이나 root 없이 native 동작에 대한 조기 가시성을 제공한다. 널리 사용되는 방법은 SoTap이다: 적절한 ABI용 libsotap.so를 APK에 넣고 System.loadLibrary("sotap") 호출을 초기에 삽입(예: static initializer 또는 Application.onCreate)한 뒤 내부/외부 경로 또는 Logcat을 통해 로그를 수집한다.
이 기술은 SLOW#TEMPEST 로더에 의해 보여지며, IDAPython과 Unicorn CPU 에뮬레이터에만 의존하는 세 단계의 워크플로우로 무력화할 수 있습니다.
설정 세부사항과 로그 경로는 Android native reversing 페이지를 참조하라:
### 1. 모든 간접 점프 / 호출 찾기
{{#ref}}
../../../mobile-pentesting/android-app-pentesting/reversing-native-libraries.md
{{#endref}}
---
## Deobfuscating Dynamic Control-Flow (JMP/CALL RAX Dispatchers)
Modern malware families heavily abuse Control-Flow Graph (CFG) obfuscation: 대신 직접적인 jump/call 대신 실행 시 목적지를 계산하여 `jmp rax` 또는 `call rax`를 실행한다. 작은 *dispatcher*(대개 9개 명령어)가 CPU의 `ZF`/`CF` 플래그에 따라 최종 타겟을 설정하여 정적 CFG 복구를 완전히 무력화한다.
이 기법은 SLOW#TEMPEST loader에서 보여진 바와 같이, IDAPython과 Unicorn CPU emulator만을 사용한 세 단계 워크플로로 무력화할 수 있다.
### 1. Locate every indirect jump / call
```python
import idautils, idc
@ -168,7 +179,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. 디스패처 바이트 코드 추출
### 2. 디스패처 바이트코드 추출
```python
import idc
@ -199,9 +210,9 @@ mu.reg_write(UC_X86_REG_RAX, 0)
mu.emu_start(BASE, BASE+len(code))
return mu.reg_read(UC_X86_REG_RAX)
```
`run(code,0,0)``run(code,1,1)`을 실행하여 *false**true* 분기 대상을 얻습니다.
다음 명령을 실행하여 `run(code,0,0)``run(code,1,1)`로부터 *false**true* 분기 대상을 얻는다.
### 4. 직접 점프 / 호출 패치하
### 4. 직접적인 jump / call을 패치하여 되돌리
```python
import struct, ida_bytes
@ -210,27 +221,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))
```
패치 후, IDA가 함수를 다시 분석하도록 강제하여 전체 CFG와 Hex-Rays 출력을 복원합니다:
패치 후, 전체 CFG와 Hex-Rays 출력이 복원되도록 IDA에 함수를 재분석하도록 강제하십시오:
```python
import ida_auto, idaapi
idaapi.reanalyze_function(idc.get_func_attr(ea, idc.FUNCATTR_START))
```
### 5. 간접 API 호출 레이블 지정
### 5. 간접 API 호출 레이블 지정
모든 `call rax`의 실제 목적지가 알려지면 IDA에 그것이 무엇인지 알려줄 수 있으므로 매개변수 유형 및 변수 이름이 자동으로 복구됩니다:
`call rax`의 실제 목적지를 알게 되면 IDA에 이를 알려 매개변수 타입 및 변수 이름이 자동으로 복원되도록 할 수 있다:
```python
idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+
```
### Practical benefits
### 실용적 이점
* 실제 CFG를 복원 → 디컴파일이 *10* 줄에서 수천 줄로 증가합니다.
* 문자열 교차 참조 및 xrefs를 가능하게 하여 행동 재구성이 간단해집니다.
* 스크립트는 재사용 가능: 동일한 트릭으로 보호된 로더에 드롭할 수 있습니다.
* 실제 CFG를 복원 → decompilation이 *10* 줄에서 수천 줄로 증가.
* string-cross-reference & xrefs를 활성화하여 행위 재구성이 쉬워짐.
* Scripts는 재사용 가능: 같은 트릭으로 보호된 어떤 loader에도 넣어 재사용할 수 있음.
---
## References
## 참고자료
- [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
{{#include ../../banners/hacktricks-training.md}}
**자세한 정보는 다음을 확인하세요:** [**https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html**](https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html)
안드로이드 앱은 성능이 중요한 작업을 위해 일반적으로 C 또는 C++로 작성된 네이티브 라이브러리를 사용할 수 있습니다. 악성코드 제작자들도 ELF 공유 객체가 DEX/OAT 바이트코드보다 디컴파일하기 더 어렵기 때문에 이러한 라이브러리를 악용합니다. 이 페이지는 안드로이드 `.so` 파일 리버싱을 더 쉽게 만드는 *실용적인* 워크플로우와 *최근* 도구 개선(2023-2025)에 중점을 둡니다.
**For further information check:** [**https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html**](https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html)
Android 앱은 일반적으로 성능이 중요한 작업을 위해 C 또는 C++로 작성된 native libraries를 사용할 수 있습니다. Malware 제작자들도 이러한 라이브러리를 악용하는데, ELF shared objects는 여전히 DEX/OAT byte-code보다 디컴파일하기 더 어렵기 때문입니다.
이 페이지는 Android `.so` 파일을 역분석하기 쉽게 해주는 *실용적인* 워크플로우와 *최근* 툴링 개선사항(2023-2025)에 초점을 맞춥니다.
---
### 새로 가져온 `libfoo.so`에 대한 빠른 분류 워크플로우
### Quick triage-workflow for a freshly pulled `libfoo.so`
1. **라이브러리 추출**
```bash
# 설치된 애플리케이션에서
# From an installed application
adb shell "run-as <pkg> cat lib/arm64-v8a/libfoo.so" > libfoo.so
# 또는 APK(압축 파일)에서
# Or from the APK (zip)
unzip -j target.apk "lib/*/libfoo.so" -d extracted_libs/
```
2. **아키텍처 및 보호 확인**
```bash
file libfoo.so # arm64 또는 arm32 / x86
readelf -h libfoo.so # OS ABI, PIE, NX, RELRO
file libfoo.so # arm64 or arm32 / x86
readelf -h libfoo.so # OS ABI, PIE, NX, RELRO, etc.
checksec --file libfoo.so # (peda/pwntools)
```
3. **내보낸 심볼 및 JNI 바인딩 나열**
```bash
readelf -s libfoo.so | grep ' Java_' # 동적 연결 JNI
strings libfoo.so | grep -i "RegisterNatives" -n # 정적 등록 JNI
readelf -s libfoo.so | grep ' Java_' # dynamic-linked JNI
strings libfoo.so | grep -i "RegisterNatives" -n # static-registered JNI
```
4. **디컴파일러에 로드** (Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper 또는 Cutter/Rizin)하고 자동 분석을 실행합니다. 최신 Ghidra 버전은 PAC/BTI 스텁과 MTE 태그를 인식하는 AArch64 디컴파일러를 도입하여 Android 14 NDK로 빌드된 라이브러리 분석을 크게 개선했습니다.
5. **정적 리버싱과 동적 리버싱 결정:** 스트립되거나 난독화된 코드는 종종 *계측* (Frida, ptrace/gdbserver, LLDB)이 필요합니다.
4. **Decompiler에 로드** (Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper or Cutter/Rizin) 및 자동 분석 실행.
Newer Ghidra versions introduced an AArch64 decompiler that recognises PAC/BTI stubs and MTE tags, greatly improving analysis of libraries built with the Android 14 NDK.
5. **정적 vs 동적 역분석 결정:** stripped, obfuscated 코드의 경우 종종 *instrumentation*이 필요합니다 (Frida, ptrace/gdbserver, LLDB).
---
### 동적 계측 (Frida ≥ 16)
### Dynamic Instrumentation (Frida ≥ 16)
Frida의 16 시리즈는 대상이 최신 Clang/LLD 최적화를 사용할 때 도움이 되는 여러 안드로이드 전용 개선 사항을 도입했습니다:
Fridas 16-series brought several Android-specific improvements that help when the target uses modern Clang/LLD optimisations:
* `thumb-relocator`는 이제 LLD의 공격적 정렬(`--icf=all`)로 생성된 *작은 ARM/Thumb 함수*를 *후킹*할 수 있습니다.
* *ELF 가져오기 슬롯*을 나열하고 다시 바인딩하는 것이 안드로이드에서 작동하여 인라인 후크가 거부될 때 모듈별 `dlopen()`/`dlsym()` 패칭을 가능하게 합니다.
* Java 후킹은 안드로이드 14에서 `--enable-optimizations`로 컴파일된 앱에서 사용되는 새로운 **ART 빠른 진입점**에 대해 수정되었습니다.
* `thumb-relocator`는 이제 LLD의 공격적 정렬(`--icf=all`)로 생성된 *작은 ARM/Thumb 함수에 hook*할 수 있습니다.
* Enumerating and rebinding *ELF import slots*는 Android에서 동작하며, inline hooks가 거부될 때 모듈별로 `dlopen()`/`dlsym()` 패치가 가능하게 합니다.
* Java hooking은 Android 14에서 `--enable-optimizations`로 컴파일된 앱이 사용하는 새로운 **ART quick-entrypoint**에 대해 수정되었습니다.
예: `RegisterNatives`를 통해 등록된 모든 함수 나열 및 런타임에서 주소 덤프:
Example: enumerating all functions registered through `RegisterNatives` and dumping their addresses at runtime:
```javascript
Java.perform(function () {
var Runtime = Java.use('java.lang.Runtime');
@ -58,38 +61,76 @@ console.log('[+] RegisterNatives on ' + clazz.getName() + ' -> ' + count + ' met
});
});
```
Frida는 frida-server 16.2 이상을 사용하면 PAC/BTI가 활성화된 장치(Pixel 8/Android 14+)에서 즉시 작동합니다. 이전 버전은 인라인 훅을 위한 패딩을 찾지 못했습니다. citeturn5search2turn5search0
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.
### preloaded .so를 통한 프로세스 로컬 JNI 텔레메트리 (SoTap)
완전한 기능의 instrumentation이 과잉이거나 차단된 경우, 대상 프로세스 내부에 작은 로거를 preload하여 네이티브 수준의 가시성을 확보할 수 있습니다. SoTap은 동일 앱 프로세스 내의 다른 JNI (.so) 라이브러리들의 런타임 동작을 로깅하는 경량 Android native (.so) 라이브러리입니다(루트 권한 불필요).
Key properties:
- 일찍 초기화되어 이를 로드하는 프로세스 내부의 JNI/native 상호작용을 관찰합니다.
- 쓰기 가능한 여러 경로에 로그를 지속하며, 저장소가 제한될 경우 Logcat으로 우아하게 폴백합니다.
- 소스 커스터마이징 가능: sotap.c를 편집해 기록할 내용을 확장/조정한 뒤 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)
2) 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:
```smali
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
```
3) 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가 발생하고 logger가 로드되지 않습니다.
- 현대 Android에서는 저장소 제약이 흔합니다; 파일 쓰기가 실패하면 SoTap은 여전히 Logcat을 통해 출력합니다.
- 동작/출력 레벨은 사용자화하도록 설계되었습니다; sotap.c를 수정한 뒤 소스에서 재빌드하세요.
이 접근법은 프로세스 시작 시점부터 네이티브 호출 흐름을 관찰해야 하는 malware triage와 JNI 디버깅에 유용합니다. 다만 root/시스템 전체 훅을 사용할 수 없는 경우에 해당합니다.
---
### APK에서 추적할 가치가 있는 최근 취약점
### Recent vulnerabilities worth hunting for in APKs
| 연도 | CVE | 영향을 받는 라이브러리 | 비고 |
| 연도 | CVE | 영향받는 라이브러리 | 설명 |
|------|-----|------------------|-------|
|2023|CVE-2023-4863|`libwebp` ≤ 1.3.1|WebP 이미지를 디코딩하는 네이티브 코드에서 접근 가능한 힙 버퍼 오버플로우. 여러 Android 앱이 취약한 버전을 포함하고 있습니다. APK 내에서 `libwebp.so`를 발견하면 버전을 확인하고 익스플로잇 또는 패치 시도를 하십시오.| citeturn2search0|
|2024|다수|OpenSSL 3.x 시리즈|여러 메모리 안전성 및 패딩 오라클 문제. 많은 Flutter 및 ReactNative 번들이 자체 `libcrypto.so`를 포함합니다.|
|2023|CVE-2023-4863|`libwebp` ≤ 1.3.1|네이티브 코드에서 WebP 이미지를 디코딩할 때 도달 가능한 힙 버퍼 오버플로우. 여러 Android 앱이 취약한 버전을 번들링하고 있습니다. APK 안에서 `libwebp.so`를 발견하면 버전을 확인하고 익스플로잇 또는 패치 시도를 하세요.| |
|2024|Multiple|OpenSSL 3.x series|여러 메모리 안전성 및 padding-oracle 문제. 많은 Flutter & ReactNative 번들들이 자체 `libcrypto.so`를 포함합니다.|
APK 내에서 *서드파티* `.so` 파일을 발견하면 항상 해시를 업스트림 권고 사항과 교차 확인하십시오. SCA(소프트웨어 구성 분석)는 모바일에서 드물기 때문에 구식 취약 빌드가 만연합니다.
APK 내부에서 *third-party* `.so` 파일을 발견하면 항상 해당 해시를 upstream 권고사항과 대조하세요. 모바일에서는 SCA (Software Composition Analysis)가 드물기 때문에 구버전의 취약한 빌드가 널리 퍼져 있습니다.
---
### 안티 리버싱 및 하드닝 트렌드 (Android 13-15)
### Anti-Reversing & Hardening trends (Android 13-15)
* **포인터 인증(PAC) 및 분기 대상 식별(BTI):** Android 14는 지원되는 ARMv8.3+ 실리콘의 시스템 라이브러리에서 PAC/BTI를 활성화합니다. 디컴파일러는 이제 PAC 관련 의사 명령어를 표시합니다. 동적 분석을 위해 Frida는 PAC을 제거한 후 트램폴린을 주입하지만, 사용자 정의 트램폴린은 필요에 따라 `pacda`/`autibsp`를 호출해야 합니다.
* **MTE 및 Scudo 강화 할당기:** 메모리 태깅은 선택 사항이지만 많은 Play-Integrity 인식 앱이 `-fsanitize=memtag`로 빌드됩니다. 태그 오류를 캡처하려면 `setprop arm64.memtag.dump 1``adb shell am start ...`를 사용하십시오.
* **LLVM 난독화기(불투명한 조건, 제어 흐름 평탄화):** 상업적 패커(예: Bangcle, SecNeo)는 Java뿐만 아니라 *네이티브* 코드를 점점 더 보호합니다. `.rodata`에서 잘못된 제어 흐름 및 암호화된 문자열 블롭을 기대하십시오.
* **Pointer Authentication (PAC) & Branch Target Identification (BTI):** Android 14은 지원되는 ARMv8.3+ 실리콘에서 시스템 라이브러리에 PAC/BTI를 활성화합니다. 디컴파일러는 이제 PAC 관련 의사명령을 표시하고; 동적 분석에서는 Frida가 PAC를 제거한 뒤 트램폴린을 주입하지만, 사용자 지정 트램폴린은 필요한 경우 pacda/autibsp를 호출해야 합니다.
* **MTE & Scudo hardened allocator:** memory-tagging은 옵트인입니다만 많은 Play-Integrity 인식 앱이 `-fsanitize=memtag`로 빌드합니다; 태그 폴트를 캡처하려면 `setprop arm64.memtag.dump 1``adb shell am start ...`를 사용하세요.
* **LLVM Obfuscator (opaque predicates, control-flow flattening):** 상용 packer들(예: Bangcle, SecNeo)은 점점 *native* 코드를 보호합니다(Java만이 아님); `.rodata`에 잘못된 제어 흐름과 암호화된 문자열 블랍이 있을 것으로 예상하세요.
---
### 리소스
### Resources
- **ARM 어셈블리 학습:** [Azeria Labs ARM Assembly Basics](https://azeria-labs.com/writing-arm-assembly-part-1/)
- **JNI 및 NDK 문서:** [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/)
- **네이티브 라이브러리 디버깅:** [JEB 디컴파일러를 사용하여 Android 네이티브 라이브러리 디버깅](https://medium.com/@shubhamsonani/how-to-debug-android-native-libraries-using-jeb-decompiler-eec681a22cf3)
- **Learning ARM Assembly:** [Azeria Labs ARM Assembly Basics](https://azeria-labs.com/writing-arm-assembly-part-1/)
- **JNI & NDK Documentation:** [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 Native Libraries:** [Debug Android Native Libraries Using JEB Decompiler](https://medium.com/@shubhamsonani/how-to-debug-android-native-libraries-using-jeb-decompiler-eec681a22cf3)
### 참고 문헌
### References
- Frida 16.x 변경 로그 (Android 후킹, 작은 함수 재배치) [frida.re/news](https://frida.re/news/) citeturn5search0
- `libwebp` 오버플로우 CVE-2023-4863에 대한 NVD 권고 사항 [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 advisory for `libwebp` overflow CVE-2023-4863 [nvd.nist.gov](https://nvd.nist.gov/vuln/detail/CVE-2023-4863)
- SoTap: Lightweight in-app JNI (.so) behavior logger [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
- SoTap Releases [github.com/RezaArbabBot/SoTap/releases](https://github.com/RezaArbabBot/SoTap/releases)
- How to work with SoTap? [t.me/ForYouTillEnd/13](https://t.me/ForYouTillEnd/13)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,85 +1,86 @@
# Smali - Decompiling/\[Modifying]/Compiling
# Smali - 디컴파일/[수정]/컴파일
{{#include ../../banners/hacktricks-training.md}}
때때로 숨겨진 정보를 접근하기 위해 애플리케이션 코드를 수정하는 것이 흥미로울 수 있습니다(아마도 잘 난독화된 비밀번호나 플래그). 그러므로 apk를 디컴파일하고 코드를 수정한 후 다시 컴파일하는 것이 흥미로울 수 있습니다.
때때로 애플리케이션 코드를 수정하여 숨겨진 정보를 얻는 것이 흥미로울 수 있습니다(예: 잘 난독화된 비밀번호나 플래그). 이럴 때 apk를 디컴파일하고 코드를 수정한 뒤 다시 컴파일하는 것이 유용합니다.
**Opcodes reference:** [http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html](http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html)
## Fast Way
## 빠른 방법
**Visual Studio Code**와 [APKLab](https://github.com/APKLab/APKLab) 확장을 사용하면 **자동으로 디컴파일**, 수정, **재컴파일**, 서명 및 애플리케이션을 설치할 수 있습니다. 어떤 명령도 실행할 필요가 없습니다.
Visual Studio Code와 [APKLab](https://github.com/APKLab/APKLab) 확장 기능을 사용하면, 어떤 명령도 실행하지 않고도 애플리케이션을 **자동으로 디컴파일**, 수정, **재컴파일**, 서명 및 설치할 수 있습니다.
또한 이 작업을 많이 용이하게 하는 **스크립트**는 [**https://github.com/ax/apk.sh**](https://github.com/ax/apk.sh)입니다.
또한 이 작업을 훨씬 쉽게 해주는 또 다른 **script**는 [**https://github.com/ax/apk.sh**](https://github.com/ax/apk.sh) 입니다.
## Decompile the APK
## APK 디컴파일
APKTool을 사용하면 **smali 코드와 리소스**에 접근할 수 있습니다:
APKTool을 사용하면 **smali code and resources**에 접근할 수 있습니다:
```bash
apktool d APP.apk
```
If **apktool**에서 오류가 발생하면, [**최신 버전**](https://ibotpeaches.github.io/Apktool/install/)을 설치해 보세요.
만약 **apktool**이 어떤 오류를 발생시키면, [ installing the **latest version**](https://ibotpeaches.github.io/Apktool/install/ )을 시도해 보세요
**확인해야 할 흥미로운 파일은**:
살펴봐야 할 몇 가지 **흥미로운 파일들**:
- _res/values/strings.xml_ (및 res/values/* 내의 모든 xml)
- _res/values/strings.xml_ (and all xmls inside res/values/*)
- _AndroidManifest.xml_
- 확장자가 _.sqlite_ 또는 _.db_인 모든 파일
- Any file with extension _.sqlite_ or _.db_
`apktool`**애플리케이션을 디코딩하는 데 문제가 있다면**, [https://ibotpeaches.github.io/Apktool/documentation/#framework-files](https://ibotpeaches.github.io/Apktool/documentation/#framework-files)를 확인하거나 **`-r`** 인수를 사용해 보세요 (리소스를 디코딩하지 않음). 그런 다음, 문제가 리소스에 있었고 소스 코드에 없었다면, 문제는 발생하지 않을 것입니다 (리소스도 디컴파일되지 않습니다).
만약 `apktool`이 애플리케이션 디코딩에 **문제가** 있다면 [https://ibotpeaches.github.io/Apktool/documentation/#framework-files](https://ibotpeaches.github.io/Apktool/documentation/#framework-files) 를 확인하거나 인자 **`-r`**(리소스 디코딩 안 함)을 사용해 보세요. 그러면 문제가 소스 코드가 아니라 리소스에 있었다면 해당 문제는 발생하지 않습니다(리소스도 디컴파일하지 않습니다).
## Smali 코드 변경
## smali 코드 변경
**명령어를 변경**하거나, 일부 변수의 **값을 변경**하거나, **새 명령어를 추가**할 수 있습니다. 저는 [**VS Code**](https://code.visualstudio.com)를 사용하여 Smali 코드를 변경하며, **smalise 확장 프로그램**을 설치하면 편집기가 **명령어가 잘못되었는지** 알려줍니다.\
**예제**는 여기에서 찾을 수 있습니다:
명령(instructions)을 **변경**하거나, 일부 변수의 **값(value)**을 바꾸거나, 새로운 명령을 **추가**할 수 있습니다. 저는 Smali 코드를 [**VS Code**](https://code.visualstudio.com)로 수정합니다. 그 다음 **smalise extension**를 설치하면 에디터가 어떤 **instruction이 잘못되었는지** 알려줍니다.\
몇 가지 **예제**는 다음에서 찾을 수 있습니다:
- [Smali 변경 예제](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)
또는 [**아래에서 설명된 Smali 변경 사항을 확인할 수 있습니다**](smali-changes.md#modifying-smali).
또는 [**아래의 일부 Smali 변경 설명을 확인해 보세요**](smali-changes.md#modifying-smali).
## APK 재컴파일
코드를 수정한 후, 다음을 사용하여 코드를 **재컴파일**할 수 있습니다:
코드를 수정한 후 다음을 사용해 코드를 **재컴파일**할 수 있습니다:
```bash
apktool b . #In the folder generated when you decompiled the application
```
새 APK_**dist**_ 폴더 **내부**에서 **컴파일**됩니다.
새 APK_**dist**_ 폴더 **내부**에서 **컴파일**합니다.
만약 **apktool**이 **오류**를 발생시키면, [**최신 버전**](https://ibotpeaches.github.io/Apktool/install/)을 설치해 보세요.
만약 **apktool**이 **오류**를 발생시키면, [ installing the **latest version**](https://ibotpeaches.github.io/Apktool/install/)을 시도해 보세요
### **새 APK 서명하기**
### **새 APK 서명**
그런 다음, **키를 생성**해야 합니다(비밀번호와 무작위로 입력할 수 있는 몇 가지 정보가 요청됩니다):
그런 다음, **키를 생성**해야 합니다 (비밀번호와 무작위로 채워도 되는 몇 가지 정보를 입력하라는 요청을 받습니다):
```bash
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>
```
마지막으로, **서명** 새 APK:
마지막으로 새 APK에 **서명**하세요:
```bash
jarsigner -keystore key.jks path/to/dist/* <your-alias>
```
### 새로운 애플리케이션 최적화
### 새 애플리케이션 최적화
**zipalign**은 Android 애플리케이션 (APK) 파일에 중요한 최적화를 제공하는 아카이브 정렬 도구입니다. [More information here](https://developer.android.com/studio/command-line/zipalign).
**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
```
### **새 APK 서명하기 (다시?)**
### **새 APK에 (다시?) 서명하기**
만약 **apksigner**를 사용하고 싶다면, **zipalign으로 최적화를 적용한 후 apk를 서명해야 합니다**. 하지만 **jarsigner로 애플리케이션을 한 번만 서명해야 한다는 점에 유의하세요** (zipalign 이전) 또는 **aspsigner로 서명해야 합니다** (zipalign 이후).
jarsigner 대신 [**apksigner**](https://developer.android.com/studio/command-line/)를 **선호한다면**, **zipaling으로 최적화를 적용한 후** APK에 **서명해야 합니다**. 하지만 jarsigner로(zipalign 이전에) 또는 aspsigner로(zipaling 이후에) **애플리케이션을 한 번만 서명하면 된다는 점에 유의하세요**.
```bash
apksigner sign --ks key.jks ./dist/mycompiled.apk
```
## Smali 수정
## Smali 수정하기
다음 Hello World Java 코드:
다음 Hello World Java 코드의 경우:
```java
public static void printHelloWorld() {
System.out.println("Hello World")
}
```
스말리 코드는 다음과 같습니다:
Smali 코드는 다음과 같습니다:
```java
.method public static printHelloWorld()V
.registers 2
@ -89,13 +90,13 @@ invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
return-void
.end method
```
Smali 명령어 집합은 [여기](https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions)에서 확인할 수 있습니다.
Smali 명령어 집합은 [here](https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions)에서 확인할 수 있습니다.
### 경량 변경
### 가벼운 변경
### 함수 내 변수의 초기 값 수정
### 함수 내 변수의 초기값 수정
일부 변수_const_ 오프코드를 사용하여 함수의 시작 부분에서 정의되며, 해당 변수의 값을 수정하거나 새로운 변수를 정의할 수 있습니다:
일부 변수들은 함수 시작 부분에서 opcode _const_를 사용해 정의됩니다. 해당 값을 수정하거나 새 변수를 정의할 수 있습니다:
```bash
#Number
const v9, 0xf4240
@ -126,7 +127,7 @@ iput v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save v0 inside
if-ne v0, v9, :goto_6 #If not equals, go to: :goto_6
goto :goto_6 #Always go to: :goto_6
```
### 더 큰 변
### 더 큰 변경사항
### 로깅
```bash
@ -137,19 +138,19 @@ 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>"
```
추천 사항:
권장사항:
- 함수 내에서 선언된 변수를 사용할 경우 (선언된 v0, v1, v2...) 이 줄들을 _.local \<number>_와 변수 선언(_const v0, 0x1_) 사이에 넣으세요.
- 함수 코드 중간에 로깅 코드를 넣고 싶다면:
- 선언된 변수의 수에 2를 추가하세요: 예: _.locals 10_에서 _.locals 12_로.
- 새로운 변수는 이미 선언된 변수의 다음 숫자가 되어야 합니다 (이 예에서는 _v10_과 _v11_이어야 하며, v0에서 시작한다는 것을 기억하세요).
- 로깅 함수의 코드를 변경하고 _v5_와 _v1_ 대신 _v10_과 _v11_을 사용하세요.
- 함수 안에서 선언된 변수를 사용할 예정이라면 (선언된 v0,v1,v2...) 이 줄들을 _.local <number>_와 변수 선언들 (_const v0, 0x1_) 사이에 넣으세요.
- 함수 코드 중간에 로깅 코드를 넣고 싶다면:
- 선언된 변수 수에 2를 더하세요: Ex: from _.locals 10_ to _.locals 12_
- 새 변수들은 이미 선언된 변수들의 다음 번호여야 합니다 (이 예에서는 _v10_과 _v11_이어야 합니다, v0부터 시작하는 것을 기억하세요).
- 로깅 함수의 코드를 변경하여 _v10_과 _v11_을 _v5_와 _v1_ 대신 사용하세요.
### 토스트
### Toasting
함수 시작 부분에서 _.locals_의 수에 3을 추가하는 것을 잊지 마세요.
함수 시작 부분에 있는 _.locals_의 숫자에 3을 더하는 것을 잊지 마세요.
이 코드는 **함수의 중간에** 삽입되도록 준비되었습니다 (**변수**의 **숫자**는 필요에 따라 **변경**하세요). 이 코드는 **this.o**의 **값**을 **가져와서**, **String**으로 **변환**한 다음 **그 값으로** **토스트**를 **만들** 것입니다.
이 코드는 **함수의 중간**에 삽입되도록 준비되어 있습니다 (**변경**해야 하는 **변수**의 수를 필요에 따라 조정하세요). 이 코드는 **this.o의 값**을 **변환**하여 **String**으로 만들고, 그 값으로 **toast**를 **만듭니다**.
```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
```
### 시작 시 네이티브 라이브러리 로드 (System.loadLibrary)
때때로 다른 JNI 라이브러리보다 먼저 초기화되도록 네이티브 라이브러리를 미리 로드해야 할 때가 있습니다(예: 프로세스 로컬 telemetry/logging을 활성화하기 위해). 정적 초기화자(static initializer)나 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) 동작 로거 [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
{{#include ../../banners/hacktricks-training.md}}