浅谈Mac应用逆向破解

视频讲解:https://www.youtube.com/watch?v=WcgTJhmloMk

0x01 Mac应用分类

  • 文件格式
    • 解释器脚本格式
    • 通用二进制格式
    • Mach-O 格式
  • 常见编写方式
    • 原生Object-C、Swift
    • 其他编程语言
    • Electron

0x02 Mac逆向常用工具

  • 反汇编
    • Hopper Disassembler
    • IDA Pro
  • hook工具
    • Frida
  • 头文件提取
    • class-dump

0x03 破解的切入点

  • 关键词搜索
  • 根据提示找判断点
  • 根据激活找校验

0x04 小技巧

  • dump 头文件

    • class-dump -S -s -H -o ./Headers filename
  • 快速获取方法在进程中的调用

    • frida-trace -m "-[* methodname*]" 进程
  • 文件中查询字符串

    • grep -r -i "string" ./
  • frida 脚本

    • hook方法调用信息
const targetClass = ObjC.classes.%s;
let methodName = "%s";
Interceptor.attach(targetClass[methodName].implementation, {
  onEnter(args) {
    console.log("\n================================");
    const reciver = ObjC.Object(args[0]);
    console.log("Target class: " + reciver);
    console.log("Target class address: " + ptr(args[0]));
    let ivars = reciver.$ivars;
    for(let k in ivars){
      let v = ivars[k];
      console.log(`ivars:[${k}] -> [${v}]`);
    }
    console.log("Target superClass: " + reciver.$superClass);
    const sel = ObjC.selectorAsString(args[1]);
    console.log("Hooked the target method: " + sel);
    let index = 0;
    let arg_num = methodName.split(":").length - 1;
    if(arg_num > 0){
      while(index < arg_num){
        index = index + 1;
        let obj = ObjC.Object(args[index + 1]);
        console.log("Argument" + String(index) + ": " + obj.toString());
      }
    }
  },
  onLeave(retval) {
    const ob1 = ObjC.Object(retval);
    console.log("Retval: " + retval);
    console.log("ObjC Retval: " + ob1.toString());
    console.log("Type: " + ob1.$className);
    console.log("SuperClass: " + ob1.$superClass);
    console.log("");
  }
});
  • 枚举类所拥有的方法
const className = "%s";
const info = {}
const hookOwnMethods = ObjC.classes[className].$ownMethods;
const hookAllMethods = ObjC.classes[className].$methods;
const hookClasses = ObjC.classes;
info["ownMethods"] = hookOwnMethods;
//info["methods"] = hookAllMethods;
//info["allClasses"] = hookClasses;
console.log(JSON.stringify(info))
  • 枚举进程所拥有的模块
var modules = Process.enumerateModules();
for(var i=0;i<modules.length;i++){
  console.log(`== Name: ${modules[i].name}  <${modules[i].base}>`);
}
  • 根据模块和偏移获取方法
/**
    * 根据module名字和目标方法的偏移地址获得方法的绝对地址
    */
function get_func_addr(module, offset) {
  // 根据名字获取module地址
  var base_addr = Module.findBaseAddress(module);
  console.log("base_addr: " + base_addr);
  console.log(hexdump(ptr(base_addr), {
    length: 16,
    header: true,
    ansi: true
  }));
  var func_addr = base_addr.add(offset);
  var return_addr;
  if (Process.arch == 'arm')
    return_addr = func_addr.add(1);  //如果是32位地址+1
  else
    return_addr = func_addr;
  console.log('func_addr: ' + return_addr);
  console.log(hexdump(ptr(return_addr), {
    length: 16,
    header: true,
    ansi: true
  }));
  return return_addr;
}
let moduleName = "%s";
let offset = %s
// 获取目标函数的绝对地址
var func_addr = get_func_addr(moduleName, offset);
Interceptor.attach(ptr(func_addr), {
  onEnter: function(args) {
    console.log("====onEnter=====");
    let index = 0;
    for(let arg of args){
      console.log("arg" + String(index)+ ": " + arg);
    }
    // console.log("arg0: " + args[0]);
    // console.log(hexdump(ptr(args[0]), {
    //     length: 64,
    //     header: false,
    //     ansi: false
    // }))
    // console.log("arg1: " + args[1]);
    // console.log("arg2: " + args[2]);
  },
  onLeave: function(retval) {
    console.log("====onLeave=====");
    console.log("retval: " + retval);
    // console.log(hexdump(ptr(retval), {
    //     length: 64,
    //     header: true,
    //     ansi: true
    // }))
  }
});
  • json转换为Object-C
var NSString = ObjC.classes.NSString;
var NSJSONSerialization = ObjC.classes.NSJSONSerialization;
function json_to_objc(data){
  let strData = NSString.stringWithString_(data).dataUsingEncoding_(0x4);
  return NSJSONSerialization.JSONObjectWithData_options_error_(strData,0x1,ptr(0x0));
}
console.log(json_to_objc('%s'));

0x05 Typora Crack

  • 关键词
    • license/License
    • hasLicense
  • 提示
    • 未激活
    • UNREGISTERED
  • 激活api
    • api/client/activate

0x06 Reference

Mach-O 文件格式探索 · GitBook (desgard.com)

MacOS逆向 - 『脱壳破解区』 (52pojie.cn)

Frida入门 (wolai)

https://frida.re/docs/examples/macos/

https://developer.apple.com/cn/search/?type=Documentation&q=

ios逆向工具Hopper Disassembler的基本使用功能整理(持续更新)_小手琴师的博客-CSDN博客_hopper disassembler