Chuẩn bị công cụ & môi trường
Để thực hành phân tích ngược trên nền tảng di động, hệ thống cài đặt nên bao gồm các thành phần sau:
- JADX: Công cụ giải mã và chuyển đổi APK sang mã nguồn Java tương thích. Người dùng chưa cấu hình JDK có thể lựa chọn phiên bản đi kèm JRE.
- IDA Pro: Môi trường phân tích mã máy (disassembler/decompiler) tiên tiến để xử lý thư viện
.so. - Frida: Bộ công cụ quan sát và sửa đổi thực thi nhị phân thời gian thực. Cài đặt wrapper Python thông qua
pip install frida frida-toolsvà thư viện cầu nối Java qua npm:npm install -g frida-java-bridge.
Trước khi triển khai, cần xác định kiến trúc vi xử lý của thiết bị đích bằng lệnh:
adb shell getprop ro.product.cpu.abi
Dựa trên kết quả trả về, hãy tải phiên bản frida-server tương ứng. Quá trình thử nghiệm được khuyến nghị thực hiện trên emulator đã bật quyền root hoặc thiết bị vật lý đã root để đảm bảo khả năng ghi đè và khởi chạy daemon.
Xử lý mã nguồn Java với JADX
Lấy một tệp APK mục tiêu, mở trực tiếp trong JADX. Quan sát luồng điều khiển tại lớp Activity chính, ta phát hiện điều kiện kích hoạt thông báo:
if (MainActivity.this.cnt == 1000) {
String token = String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107);
tv3.setText("SECCON{" + token + "}");
}
Mặc dù逻辑 đã rõ, việc thiếu định nghĩa thực tế của phương thức calc() cho thấy logic cốt lõi nằm ở lớp Native. Do đó, cần chuyển sang phân tích thư viện chia sẻ.
Khai thác thư viện Native qua IDA Pro
Rename phần mở rộng của APK thành .zip, giải nén và trích xuất tệp .so chứa mã gốc. Mở tệp này trong IDA Pro, tìm kiếm symbol JNI.
Phân tích bytecode cho thấy hàm mục tiêu chỉ bao gồm hai câu lệnh assembly cơ bản:
mov eax, 7
ret
Điều này khẳng định hàm calc() bị ràng buộc tĩnh (hardcoded) luôn trả về giá trị nguyên 7 bất kể tham số đầu vào. Thay thế giá trị này vào biểu thức ban đầu:(1000 + 7) × 107 = 107749. Kết quả dự kiến là SECCON{107749}.
Mặc dù đã có đáp án, việc sử dụng Frida để quan sát thực thi Runtime sẽ cung cấp cái nhìn trực quan hơn về cơ chế Hook.
Bố trí Frida-server & Kiểm tra kết nối
Push tệp frida-server vào thư mục /data/local/tmp/ trên thiết bị. Thư mục này yêu cầu ít đặc quyền hơn so với các đường dẫn hệ thống khác. Cấp quyền thực thi và khởi chạy daemon:
chmod 755 /data/local/tmp/frida-server
su -c "/data/local/tmp/frida-server &"
Kiểm tra kết nối và danh sách tiến trình bằng các lệnh thông dụng:
frida-ls-devices
frida-ps -D <device-id>
frida-ps -D <device-id> -ai
Với Python API, kiểm tra không gian tên module bằng import frida; dir(frida) để xác nhận các method khả dụng.
Triển khai Hook qua Python Script
Tạo tệp định nghĩa kịch bản TypeScript (hook_config.ts). Lưu ý rằng từ phiên bản 17.x trở lên, Frida tách biệt module cầu nối Java. Cần nhập thủ công thư viện này và biên dịch sang chuẩn JavaScript hỗ trợ:
// hook_config.ts
import Java from "frida-java-bridge";
const appPackage = "com.example.seccon2015.rock_paper_scissors";
Java.perform(() => {
const clsActivity = Java.use("com.example.seccon2015.rock_paper_scissors.MainActivity");
clsActivity.calc.implementation = function () {
console.log(`[HOOK] Phương thức calc() đang được gọi.`);
const originalValue = this.calc();
console.log(`[RESULT] Giá trị trả về: ${originalValue}`);
return originalValue;
};
});
Biên dịch tệp cấu hình:
npx frida-compile hook_config.ts -o agent_bundle.js
Viết script điều phối Python (runner.py) để đọc nội dung bundle và attach vào tiến trình:
import frida
import sys
import os
# Đường dẫn đến file JS đã biên dịch
SCRIPT_PATH = r"./agent_bundle.js"
def handle_runtime_event(message, payload_data):
if message["type"] == "send":
print(f"[INFO] Dữ liệu nhận: {message['payload']}")
else:
print(f"[WARN] Sự kiện Runtime: {message}")
def main():
target_ip = "192.168.137.8:5555" # Địa chỉ IP kết nối adb-over-tcp
device_target = frida.get_device(target_ip)
with open(SCRIPT_PATH, "r", encoding="utf-8") as f:
agent_source = f.read()
# Attach trực tiếp bằng tên package
session_handle = device_target.attach("com.example.seccon2015.rock_paper_scissors")
agent_script = session_handle.create_script(agent_source)
agent_script.on("message", handle_runtime_event)
print("[*] Đang kích hoạt engine phân tích...")
agent_script.load()
try:
sys.stdin.read()
except KeyboardInterrupt:
print("\n[*] Dừng tiến trình attach.")
if __name__ == "__main__":
main()
Chạy python runner.py. Khi tương tác GUI, log sẽ hiển thị trạng thái hook thành công.
Triển khai Hook qua Command Line Interface
Phương pháp CLI đơn giản hóa quy trình mà không cần tự xây dựng cầu nối Java phức tạp. Sử dụng đúng cú pháp script gốc từ tài liệu chính thức, Frida sẽ tự động load và inject vào tiến trình đích. Giao diện terminal sẽ phản hồi ngay lập tức khi sự kiện trigger xảy ra.
Giải phẫu kỹ thuật & Khắc phục lỗi thường gặp
Cơ chế Inject & Phân tầng Hook
Frida hoạt động dựa trên việc chèn thư viện động thông qua syscall ptrace, sau đó thiết lập kênh giao tiếp IPC với host. Mã JS chạy trên PC được truyền tới Agent bên trong tiến trình mục tiêu để thực thi.
• Hook lớp Java: Tương tác sâu với Dalvik/ART runtime. Cho phép bẫy tham số, thay đổi return value, hoặc truy vết callstack bằng Java.use().implementation.
• Hook lớp Native: Can thiệp trực tiếp vào vùng nhớ bộ sưu tập bằng Interceptor.attach(). Tối ưu hiệu năng khi phân tích thuật toán mật mã hoặc cơ chế bảo vệ nhị phân.
Vai trò của frida-java-bridge & Build Tool
Thư viện này đóng vai trò trung gian giữa Engine Frida và JVM. Từ phiên bản mới,开发者 phải import thủ công. frida-compile hoạt động như bundler, tổng hợp các module ES6/TypeScript thành một tệp JS đơn lẻ, hỗ trợ tính năng watch mode để debug nhanh chóng.
Xử lý lỗi Deployment
- Permission denied / File not found: Xác minh lại kiến trúc CPU (ví dụ:
arm64-v8avsx86_64) và đảm bảo chmod 755 đã áp dụng. Khởi chạy daemon thông quasunếu môi trường restricted. - SELinux Blocking: Tạm hoãn chế độ enforcing bằng
setenforce 0để tránh kernel filter chặn port RPC. - Version Mismatch: Phiên bản client (pip/npm) và server trên device phải trùng khớp. Kiểm tra bằng
frida --version. - Simulator Connectivity: Ưu tiên kết nối TCP/IP ổn định hơn USB. Thiết lập port forwarding:
adb forward tcp:27042 tcp:27042, sau đó attach quafrida -H 127.0.0.1:27042 -f <package>.