Objection
설치
pip install objection
사용방법
objection -g [app name] explore
ios jailbreak disable
ios hooking watch method "[후킹 메소드]" --dump-return
ios hooking set return_value "[메소드]" 0x0
file download <FILENAME> : iOS > PC 파일 다운받기
file upload <local_file_path> : PC > iOS 파일 업로드
frida-trace
사용방법
open을 사용하는 클래스 추출
frida-trace -U -f [PACKAGE_NAME] -i “open*”
init으로 시작하는 클래스 추출
frida-trace -U -i “init*” [PID]
Class 내 모든 Method 추출
function hook_class_method(class_name, method_name)
{
var hook = ObjC.classes[class_name][method_name];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
console.log("[*] Detected call to: " + class_name + " -> " + method_name);
}
});
}
function run_hook_all_methods_of_specific_class(className_arg)
{
console.log("[*] Started: Hook all methods of a specific class");
console.log("[+] Class Name: " + className_arg);
//Your class name here
var className = className_arg;
//var methods = ObjC.classes[className].$methods;
var methods = ObjC.classes[className].$ownMethods;
for (var i = 0; i < methods.length; i++)
{
console.log("[-] "+methods[i]);
console.log("\t[*] Hooking into implementation");
//eval('var className2 = "'+className+'"; var funcName2 = "'+methods[i]+'"; var hook = eval(\'ObjC.classes.\'+className2+\'["\'+funcName2+\'"]\'); Interceptor.attach(hook.implementation, { onEnter: function(args) { console.log("[*] Detected call to: " + className2 + " -> " + funcName2); } });');
var className2 = className;
var funcName2 = methods[i];
hook_class_method(className2, funcName2);
console.log("\t[*] Hooking successful");
}
console.log("[*] Completed: Hook all methods of a specific class");
}
function hook_all_methods_of_specific_class(className_arg)
{
setImmediate(run_hook_all_methods_of_specific_class,[className_arg])
}
//Your class name goes here
hook_all_methods_of_specific_class("추출한 Class Name")
Method trace 스크립트 by hackcatml
// generic trace
function trace(pattern)
{
var type = (pattern.indexOf(" ") === -1) ? "module" : "objc"; // [A B]와 같이 공백이 있으면 objc, 없으면 모듈
var res = new ApiResolver(type);
var matches = res.enumerateMatchesSync(pattern);
var targets = uniqBy(matches, JSON.stringify);
targets.forEach(function(target) {
if (type === "objc")
traceObjC(target.address, target.name);
else if (type === "module")
traceModule(target.address, target.name);
});
}
// remove duplicates from array
function uniqBy(array, key)
{
var seen = {};
return array.filter(function(item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
// trace ObjC methods
function traceObjC(impl, name)
{
console.log("Tracing " + name);
Interceptor.attach(impl, {
onEnter: function(args) {
// debug only the intended calls
this.flag = 0;
// if (ObjC.Object(args[2]).toString() === "1234567890abcdef1234567890abcdef12345678")
this.flag = 1;
if (this.flag) {
console.warn("\n[+] entered " + name);
// print caller
console.log("\x1b[31mCaller:\x1b[0m \x1b[34m" + DebugSymbol.fromAddress(this.returnAddress) + "\x1b[0m\n");
// print args
console.log("\x1b[31margs[2]:\x1b[0m \x1b[34m" + args[2] + ", \x1b[32m" + ObjC.Object(args[2]) + "\x1b[0m")
console.log("\x1b[31margs[3]:\x1b[0m \x1b[34m" + args[3] + ", \x1b[32m" + ObjC.Object(args[3]) + "\x1b[0m")
// console.log("\x1b[31margs[4]:\x1b[0m \x1b[34m" + args[4] + ", \x1b[32m" + ObjC.Object(args[4]) + "\x1b[0m")
// print full backtrace
// console.log("\nBacktrace:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE)
// .map(DebugSymbol.fromAddress).join("\n"));
}
},
onLeave: function(retval) {
if (this.flag) {
// print retval
console.log("\n\x1b[31mretval:\x1b[0m \x1b[34m" + retval + "\x1b[0m");
console.warn("[-] exiting " + name);
}
}
});
}
// trace Module functions
function traceModule(impl, name)
{
console.log("Tracing " + name);
Interceptor.attach(impl, {
onEnter: function(args) {
// debug only the intended calls
this.flag = 0;
// var filename = Memory.readCString(ptr(args[0]));
// if (filename.indexOf("Bundle") === -1 && filename.indexOf("Cache") === -1) // exclusion list
// if (filename.indexOf("my.interesting.file") !== -1) // inclusion list
this.flag = 1;
if (this.flag) {
console.warn("\n*** entered " + name);
// print backtrace
console.log("\nBacktrace:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join("\n"));
}
},
onLeave: function(retval) {
if (this.flag) {
// print retval
console.log("\nretval: " + retval);
console.warn("\n*** exiting " + name);
}
}
});
}
// usage examples. 관심있는 클래스를 명시. 대소문자 구분
if (ObjC.available) {
trace("*[JailbreakDetection *]")
// trace("*[FireflySecurityUtil *]")
// trace("*[ *ncrypt*]");
// trace("*[* *]"); 모든 클래스 추적. 앱이 다운됨
// trace("exports:libSystem.B.dylib!CCCrypt");
// trace("exports:libSystem.B.dylib!open");
// trace("exports:*!open*");
} else {
send("error: Objective-C Runtime is not available!");
}
UI Dump by hackcatml
var window = ObjC.classes.UIWindow.keyWindow();
var rootControl = window.rootViewController();
var ui = window.recursiveDescription().toString();
var ui_autolayout = window['- _autolayoutTrace']().toString();
var control = rootControl['- _printHierarchy']().toString();
// 전체 UI 계층 출력하고 싶은 경우
// console.log("\n\x1b[31m" + ui + "\x1b[0m");
// Simplified recursiveDescription
// console.log("\n\x1b[34m" + ui_autolayout + "\x1b[0m");
// 현재 화면에 보여지는 UIController를 알고 싶은 경우
console.log("\n\x1b[32m" + control + "\x1b[0m");