一、背景

在「iOS 逆向入门 - TikTok 调试」文章中介绍了使用 MonkeyDev 工具对 TikTok 进行调试。使用同样的方法对抖音进行调试的过程中遇到了几个问题,在这里记录一下。

二、动态库注入失败

按照 TikTok 的方式运行后,发现控制台没有打印 insert dylib success 的信息,猜测动态库注入失败了,果然在编译信息的输出中找到相关错误打印:

165c3e57dcade6ada8d0a2fd28b85ee5

85bf3e1b4d094b2ccd415bd033a11a68

在 MonkeyDev 的 change.log 中找到信息,尝试恢复使用 optool 工具注入动态库。

下载 optool 的二进制文件并复制到相应位置,然后修改以下代码:

# 注释原来的 MONKEYPARSER
# "$MONKEYPARSER" install -c load -p "@executable_path/Frameworks/lib""${TARGET_NAME}""Dylib.dylib" -t "${BUILD_APP_PATH}/${APP_BINARY}"
# 修改为 optool
OPTOOL="${MONKEYDEV_PATH}/bin/optool"
"$OPTOOL" install -c load -p "@executable_path/Frameworks/lib""${TARGET_NAME}""Dylib.dylib" -t "${BUILD_APP_PATH}/${APP_BINARY}"

d9d23d29c9046b42770d53780277c92a

重新编译运行后可发现控制台中出现 insert dylib success 信息,证明动态库已经注入成功。

8c7632998f7a2a5e45a9073f24fc2ae4

三、绕过反调试

动态库注入成功后,发现控制台没有了任何输出,且 lldb 已经断开,证明抖音使用了某种反调试手段。

1、反调试常规手段

1)ptrace

ptrace 被常用于防止 lldb 依附,原理是一个进程只能被 ptrace 只能被一次,先于别人调用 ptrace 则可以防止别人依附。

ptrace 有几种方式进行调用:

  1. 通过 dlsym 调用
  2. 通过汇编调用;
  3. 通过 syscall 调用,syscall(26,31,0,0)

2)sysctl

通过 sysctl 去查看当前进程的信息,看有没有这个标记位即可检查当前调试状态。

bool isBeingAttach() {
    size_t size = sizeof(struct kinfo_proc);
    struct kinfo_proc info;
    int ret = 0, name[4];
    memset(&info, 0, sizeof(struct kinfo_proc));
    
    name[0] = CTL_KERN;
    name[1] = KERN_PROC;
    name[2] = KERN_PROC_PID;
    name[3] = getpid();
    
    if (ret == (sysctl(name, 4, &info, &size, NULL, 0))) {
        return ret != 0;
    }
    return (info.kp_proc.p_flag & P_TRACED) ? YES : NO;
}

2、反反调试常规手段

我们知道了反调试的常用函数,我们可以使用 fishhook hook 这些函数,达到反反调试的效果。

MonkeyDev 已经为我们写好了相关实现,我们打开注释即可:

cf4d6c5947ba16867fd9321c3eeb63fa

3、进一步

打开上面的注释重新运行后,lldb 依然依附失败,说明抖音有更进一步的反调试手段。

通过网上查阅资料,有以下解题套路:

1)判断反调试代码的位置

Xcode 上添加 AppDelegate 的符号断点:

-[AppDelegate application:didFinishLaunchingWithOptions:]

f58d783f52007104ea94c8c2d913e0f9

运行后断点依然失败,证明反调试代码在 didFinishLaunchingWithOptions 之前。

2)利用 IDA 进行分析

2615ad09f676c83350c7956e316491cb

把二进制文件拖入 IDA 后进行分析,在 start 函数中的 0x0000000109640CF4~0x0000000109640D00 就是反调试代码,相当于调用了上面提到的 syscall(26,31,0,0),最终就是调用了 ptrace 函数。

3)绕过反调试代码

2b9359f745f52c2f10a05678678f2f2b

  1. lldb 中执行 image list -o -f 打印链接的库,其中第一个地址则是 ASLR 的偏移地址。

运行时基地址(0x000000010BEC8D00) = ASLR 偏移地址(0x0000000002888000) + 基地址(0x0000000109640D00)

  1. 添加地址断点,breakpoint set -a 0x000000010BEC8D00

  2. 断点到该地址后,执行 register write $x1 0,修改 x1 寄存器值。

继续运行后则发现已经成功依附到 lldb。

ASLR 偏移地址每次 App 运行都会被修改,所以每次运行都要进行上面的操作。

5ffff7707fb245d8158b11fa60fe4f37 6524af2a0852d90cf54e5c18233ab309