Deepen your expertise in Mobile Security with 8kSec Academy. Master iOS and Android security through our self-paced courses and get certified:
Installing Frida
탈옥된 기기에 Frida 설치하는 단계:
Cydia/Sileo 앱을 엽니다.
관리 -> 소스 -> 편집 -> 추가로 이동합니다.
URL로 "https://build.frida.re"를 입력합니다.
새로 추가된 Frida 소스로 이동합니다.
Frida 패키지를 설치합니다.
Corellium을 사용하는 경우, https://github.com/frida/frida/releases에서 Frida 릴리스를 다운로드해야 합니다 (frida-gadget-[yourversion]-ios-universal.dylib.gz) 그리고 압축을 풀고 Frida가 요청하는 dylib 위치에 복사합니다, 예: /Users/[youruser]/.cache/frida/gadget-ios.dylib
설치 후, PC에서 frida-ls-devices 명령어를 사용하여 기기가 나타나는지 확인합니다 (PC가 기기에 접근할 수 있어야 합니다).
또한 **frida-ps -Uia**를 실행하여 전화기의 실행 중인 프로세스를 확인합니다.
Frida 서버가 설치되고 장치가 실행 중이며 연결된 상태에서 클라이언트가작동하는지확인하십시오:
frida-ls-devices# List devicesfrida-ps-Uia# Get running processes
Frida Trace
# Functions## Trace all functions with the word "log" in their namefrida-trace-U<program>-i"*log*"frida-trace-U<program>-i"*log*"|swiftdemangle# Demangle names# Objective-C## Trace all methods of all classesfrida-trace-U<program>-m"*[* *]"## Trace all methods with the word "authentication" from classes that start with "NE"frida-trace-U<program>-m"*[NE* *authentication*]"# Plug-In## To hook a plugin that is momentarely executed prepare Frida indicating the ID of the Plugin binaryfrida-trace-U-W<if-plugin-bin>-m'*[* *]'
// frida -U <program> -l /tmp/script.jsvar specificClass ="YourClassName";var filterMethod ="filtermethod";if (ObjC.available) {if (ObjC.classes.hasOwnProperty(specificClass)) {var methods =ObjC.classes[specificClass].$ownMethods;for (var i =0; i <methods.length; i++) {if (!filterMethod || methods[i].includes(filterClass)) {console.log(specificClass +': '+ methods[i]);}}} else {console.log("Class not found.");}} else {console.log("Objective-C runtime is not available.");}
함수 호출
// Find the address of the function to callconstfunc_addr=Module.findExportByName("<Prog Name>","<Func Name>");// Declare the function to callconstfunc=newNativeFunction(func_addr,"void", ["pointer","pointer","pointer"], {});var arg0 =null;// In this case to call this function we need to intercept a call to it to copy arg0Interceptor.attach(wg_log_addr, {onEnter:function(args) {arg0 =newNativePointer(args[0]);}});// Wait untill a call to the func occurswhile (! arg0) {Thread.sleep(1);console.log("waiting for ptr");}var arg1 =Memory.allocUtf8String('arg1');var txt =Memory.allocUtf8String('Some text for arg2');wg_log(arg0, arg1, txt);console.log("loaded");
Frida Fuzzing
Frida Stalker
문서에서: Stalker는 Frida의 코드 추적 엔진입니다. 이는 스레드를 따라가며, 실행되는 모든 함수, 모든 블록, 심지어 모든 명령어를 캡처할 수 있게 해줍니다.
console.log("loading");constwg_log_addr=Module.findExportByName("<Program>","<function_name>");constwg_log=newNativeFunction(wg_log_addr,"void", ["pointer","pointer","pointer"], {});Interceptor.attach(wg_log_addr, {onEnter:function(args) {console.log(`logging the following message: ${args[2].readCString()}`);Stalker.follow({events: {// only collect coverage for newly encountered blockscompile:true,},onReceive:function (events) {constbbs=Stalker.parse(events, {stringify:false,annotate:false});console.log("Stalker trace of write_msg_to_log: \n"+bbs.flat().map(DebugSymbol.fromAddress).join('\n'));}});},onLeave:function(retval) {Stalker.unfollow();Stalker.flush(); // this is important to get all events}});
디버깅 목적에서는 흥미롭지만, 퍼징을 위해 .follow() 및 **.unfollow()**를 지속적으로 사용하는 것은 매우 비효율적입니다.
# Get fpickergitclonehttps://github.com/ttdennis/fpickercdfpicker# Get Frida core devkit and prepare fpickerwget https://github.com/frida/frida/releases/download/16.1.4/frida-core-devkit-16.1.4-[yourOS]-[yourarchitecture].tar.xz
# e.g. https://github.com/frida/frida/releases/download/16.1.4/frida-core-devkit-16.1.4-macos-arm64.tar.xztar-xf./*tar.xzcplibfrida-core.alibfrida-core-[yourOS].a#libfrida-core-macos.a# Install fpickermakefpicker-[yourOS]# fpicker-macos# This generates ./fpicker# Install radamsa (fuzzer generator)brewinstallradamsa
FS 준비:
# From inside fpicker clonemkdir-pexamples/wg-log# Where the fuzzing script will bemkdir-pexamples/wg-log/out# For code coverage and crashesmkdir-pexamples/wg-log/in# For starting inputs# Create at least 1 input for the fuzzerechoHelloWorld>examples/wg-log/in/0
퍼저 스크립트 (examples/wg-log/myfuzzer.js):
examples/wg-log/myfuzzer.js
// Import the fuzzer base classimport { Fuzzer } from"../../harness/fuzzer.js";classWGLogFuzzerextendsFuzzer {constructor() {console.log("WGLogFuzzer constructor called")// Get and declare the function we are going to fuzzvar wg_log_addr =Module.findExportByName("<Program name>","<func name to fuzz>");var wg_log_func =newNativeFunction(wg_log_addr,"void", ["pointer","pointer","pointer"], {});// Initialize the objectsuper("<Program nane>", wg_log_addr, wg_log_func);this.wg_log_addr = wg_log_addr; // We cannot use "this" before calling "super"console.log("WGLogFuzzer in the middle");// Prepare the second argument to pass to the fuzz functionthis.tag =Memory.allocUtf8String("arg2");// Get the first argument we need to pass from a call to the functino we want to fuzzvar wg_log_global_ptr =null;console.log(this.wg_log_addr);Interceptor.attach(this.wg_log_addr, {onEnter:function(args) {console.log("Entering in the function to get the first argument");wg_log_global_ptr =newNativePointer(args[0]);}});while (! wg_log_global_ptr) {Thread.sleep(1)}this.wg_log_global_ptr = wg_log_global_ptr;console.log("WGLogFuzzer prepare ended")}// This function is called by the fuzzer with the first argument being a pointer into memory// where the payload is stored and the second the length of the input.fuzz(payload, len) {// Get a pointer to payload being a valid C string (with a null byte at the end)var payload_cstring =payload.readCString(len);this.payload =Memory.allocUtf8String(payload_cstring);// Debug and fuzzthis.debug_log(this.payload, len);// Pass the 2 first arguments we know the function needs and finally the payload to fuzzthis.target_function(this.wg_log_global_ptr,this.tag,this.payload);}}constf=newWGLogFuzzer();rpc.exports.fuzzer = f;
퍼저를 컴파일합니다:
# From inside fpicker clone## Compile from "myfuzzer.js" to "harness.js"frida-compileexamples/wg-log/myfuzzer.js-oharness.js
**radamsa**를 사용하여 fuzzer fpicker 호출:
# Indicate fpicker to fuzz a program with the harness.js script and which folders to usefpicker -v --fuzzer-mode active -e attach -p <Program to fuzz> -D usb -o examples/wg-log/out/ -i examples/wg-log/in/ -f harness.js --standalone-mutator cmd --mutator-command "radamsa"
# You can find code coverage and crashes in examples/wg-log/out/
이 경우 우리는 각 페이로드 후에 앱을 재시작하거나 상태를 복원하지 않습니다. 따라서 Frida가 충돌을 발견하면, 그 페이로드 이후의 다음 입력도 앱을 충돌시킬 수 있습니다 (앱이 불안정한 상태이기 때문에) 비록 입력이 앱을 충돌시켜서는 안 되더라도.
게다가, Frida는 iOS의 예외 신호에 후킹하므로, Frida가 충돌을 발견하면, 아마도 iOS 충돌 보고서가 생성되지 않을 것입니다.
이를 방지하기 위해, 예를 들어, 우리는 각 Frida 충돌 후에 앱을 재시작할 수 있습니다.
로그 및 충돌
macOS 콘솔 또는 log CLI를 사용하여 macOS 로그를 확인할 수 있습니다.
또한 **idevicesyslog**를 사용하여 iOS의 로그를 확인할 수 있습니다.
일부 로그는 정보를 생략하며 **<private>**를 추가합니다. 모든 정보를 표시하려면 https://developer.apple.com/bug-reporting/profiles-and-logs/에서 일부 프로파일을 설치해야 해당 개인 정보를 활성화할 수 있습니다.