破解Paste: 基于LLDB堆栈跟踪与Frida调试

查看 100|回复 10
作者:QiuChenly   
  没有注入补丁。因为Paste只有一个主程序文件,不方便注入。
本来这个帖子是有剧情的,但是情感失利,妹子常规好感度完美开局48小时内打成残血副本:
油腻男的聊天风格直接触发妹子的无视微信消息大招,绷不住了,绝望下编写此帖,实在没心情创作了
现在是半夜00:44还在emo
0x01 寻找入口点与分析


16811420538989.jpg (1.16 MB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

  放错图了。这里应该是未激活的状态,差不多意思意思吧这样子。


16811421036157.jpg (110.68 KB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

  因为楼主买不起苹果电脑手机平板,所以只能幻想自己左苹果右华为胸口纹嘉然脑门贴nanami的雄姿。
  老样子搜索字符串,这次用Sublime Text。
  根据上图的内容,我们尝试搜索“立即开始免费试用”:


16812037573708.jpg (211.76 KB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

这里"licensing.paywall.title"我们到ida搜索一下:


16812043285737.jpg (317.12 KB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

按下x看一下xref引用看看函数细节:


16812043519751.jpg (118.94 KB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

这里函数又被两个地方调用,我们直接用lldb看一下函数调用堆栈.我们记下这个函数地址方便后面下断点.


16812040802761.jpg (239.6 KB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

加载后我们先输入c让他继续运行。
然后我们给内存地址:0x1002253E0下断点: br s -a 1002253E0
随后输入r重启app,可以看到直接被断下了:


16812044276264.jpg (356.53 KB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

那么我们往上查堆栈看是谁触发这个Call:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
    frame #0: 0x00000001002253e0 Paste`___lldb_unnamed_symbol16296
Paste`___lldb_unnamed_symbol16296:
->  0x1002253e0 :  push   rbp
    0x1002253e1 :  mov    rbp, rsp
    0x1002253e4 :  lea    rax, [rip + 0x167715]     ; "licensing.paywall.title"
    0x1002253eb : movabs rsi, -0x8000000000000000
Target 0: (Paste) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
  * frame #0: 0x00000001002253e0 Paste`___lldb_unnamed_symbol16296
    frame #1: 0x00007ff817b19033 libdispatch.dylib`_dispatch_client_callout + 8
    frame #2: 0x00007ff817b1a267 libdispatch.dylib`_dispatch_once_callout + 20
    frame #3: 0x000000010022544a Paste`___lldb_unnamed_symbol16297 + 42
    frame #4: 0x00000001001f8045 Paste`___lldb_unnamed_symbol14918 + 293
    frame #5: 0x00000001001f8905 Paste`___lldb_unnamed_symbol14923 + 37
    frame #138: 0x00000001002489e6 Paste`___lldb_unnamed_symbol17865 + 86
    frame #139: 0x0000000100248800 Paste`___lldb_unnamed_symbol17861 + 16
    frame #140: 0x0000000100247a8e Paste`___lldb_unnamed_symbol17812 + 302
    frame #141: 0x000000010006127a Paste`___lldb_unnamed_symbol5177 + 778
    frame #142: 0x0000000100066423 Paste`___lldb_unnamed_symbol5291 + 195
    frame #143: 0x0000000100068970 Paste`___lldb_unnamed_symbol5356
    frame #144: 0x0000000100067870 Paste`___lldb_unnamed_symbol5313
    frame #145: 0x0000000100068930 Paste`___lldb_unnamed_symbol5354
那么我们重点关注栈3-5-138-145,因为下面都是系统库,所以我们直接看#3看看有没有发现:
void *sub_100225420()
{
  if ( qword_100466E50 != -1 )
    swift_once(&qword_100466E50, sub_1002253E0);
  return &unk_10046CF50;
}
qword_100466E50必然不是-1,所以这里没有可检查的必要,我们继续回溯到#142: 0x0000000100066423:
__int64 __fastcall sub_100066360()
{
  __int64 v0; // r13
  __int64 *v1; // r14
  __int64 v2; // r15
  __int64 v3; // r12
  __int64 v4; // rax
  __int64 v5; // r14
  __int64 v6; // rbx
  void *v7; // r14
  __int64 v8; // rdi
  __int64 v9; // r13
  __int64 v10; // rbx
  __int64 v11; // r15
  v2 = *v1;
  v3 = *v1;
  swift_task_dealloc(*(_QWORD *)(*v1 + 48));
  if ( v0 )
    swift_errorRelease(v0);
  v4 = *(_QWORD *)(v2 + 24);
  v5 = *(_QWORD *)(v4 + 80);
  v6 = *(_QWORD *)(v4 + 88);
  sub_10000B980(v4 + 56, v5);
  if ( ((*(__int64 (__fastcall **)(__int64, __int64))(v6 + 8))(v5, v6) & 1) != 0 )
  {
    if ( *(_BYTE *)(v2 + 56) == 1 )
    {
      (*(void (**)(void))(v2 + 32))();
    }
    else if ( (sub_100068390() & 1) != 0 )
    {
      sub_100066460();
    }
  }
  else
  {
    v7 = *(void **)(v2 + 40);
    v8 = *(_QWORD *)(v2 + 24);
    v9 = *(_QWORD *)(v2 + 32);
    v10 = *(_QWORD *)(v8 + 280);
    v11 = *(_QWORD *)sub_10000B980(v8 + 144, *(_QWORD *)(v8 + 168));
    swift_retain(v10);
    sub_100060F70(v11, v9, v7, v10);
    swift_release(v10);
  }
  return (*(__int64 (**)(void))(v3 + 8))();
}
可以看到这里(v6 + 8)这个指针所在的地址函数如果等于0 就执行到了我们的#142堆栈。
  else
  {
    v7 = *(void **)(v2 + 40);
    v8 = *(_QWORD *)(v2 + 24);
    v9 = *(_QWORD *)(v2 + 32);
    v10 = *(_QWORD *)(v8 + 280);
    v11 = *(_QWORD *)sub_10000B980(v8 + 144, *(_QWORD *)(v8 + 168));
    swift_retain(v10);
    sub_100060F70(v11, v9, v7, v10);
    swift_release(v10);
  }
所以我们的工作重点放在这里这个判断上,在lldb中给这个地址下断点:
__text:00000001000663A8 loc_1000663A8:                          ; CODE XREF: sub_100066360+3E↑j
__text:00000001000663A8                 mov     rax, [r15+18h]
__text:00000001000663AC                 lea     rdi, [rax+38h]
__text:00000001000663B0                 mov     r14, [rax+50h]
__text:00000001000663B4                 mov     rbx, [rax+58h]
__text:00000001000663B8                 mov     rsi, r14
__text:00000001000663BB                 call    sub_10000B980
__text:00000001000663C0                 mov     r13, rax
__text:00000001000663C3                 mov     rdi, r14
__text:00000001000663C6                 mov     rsi, rbx
__text:00000001000663C9                 call    qword ptr [rbx+8]  
lldb中输入br s -a 0x1000663c9,然后输入r重启,可以看到自动断下:
Process 28613 resuming
Process 28613 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 6.1
    frame #0: 0x00000001000663c9 Paste`___lldb_unnamed_symbol5291 + 105
Paste`___lldb_unnamed_symbol5291:
->  0x1000663c9 : call   qword ptr [rbx + 0x8]
    0x1000663cc : test   al, 0x1
    0x1000663ce : je     0x1000663e1               ;
    0x1000663d0 : cmp    byte ptr [r15 + 0x38], 0x1
Target 0: (Paste) stopped.
(lldb)
我们来StepInto 这个Call:


16812062048060.jpg (732.56 KB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

我们IDA看一下这个地址所在的函数是干嘛的:
__int64 __fastcall sub_1001FBF10(__int64 a1, __int64 a2)
{
  __int64 v2; // rax
  __int64 v3; // r13
  void *v4; // rsp
  void *v5; // rsp
  __int64 v6; // rdi
  __int64 v7; // rax
  unsigned int v8; // ebx
  _QWORD v10[6]; // [rsp-8h] [rbp-30h] BYREF
  v10[0] = v2;
  sub_10000B6A0(&unk_100454320);
  v4 = alloca(sub_10000C800(&unk_100454320, a2));
  v5 = alloca(sub_10000C800(&unk_100454320, a2));
  v6 = *(_QWORD *)(*(_QWORD *)v3 + 16LL);
  swift_retain(v6);
  _s7Combine19CurrentValueSubjectC5valuexvg();
  swift_release(v6);
  sub_1001FC3D0(v10, v10, &unk_100454320);
  v7 = sub_1001F95A0(0LL);
  v8 = (*(__int64 (__fastcall **)(_QWORD *, __int64, __int64))(*(_QWORD *)(v7 - 8) + 48LL))(v10, 1LL, v7);
  if ( v8 != 1 )
    sub_1001FC400(v10, &unk_100454320);
  LOBYTE(v8) = v8 != 1;
  sub_1001FC400(v10, &unk_100454320);
  return v8;
}
可以看出v8返回值为0或者1,因为他有一个v8!=1的判断在这里,那么我们如果Hook 0x1001FBF10这个函数的返回值是否就ok了呢?
编写Frida代码进行内存注入:
import { HookApp, log } from "./Utils.js";
HookApp("Paste", (hook, point, method, baseAddr, tools) => {
  // setTimeout(() => {
  //   hook(point(0x1f9f10), (ths, r) => {
  //     // r.replace(ptr(2));
  //   });
  // }, 3000);
  hook(point(0x1fbf10), (ths, r) => {
    r.replace(ptr(1));
  });
});
狠狠的按下F5启动,我们看看是否注入成功:


16812068323518.jpg (652.54 KB, 下载次数: 0)
下载附件
2023-4-11 19:21 上传

这里没有去修改有效订阅的那个函数,因为修改了这个信息之后会因为无法读取订阅数据而导致App崩溃,而且Paste只检查了0x1fbf10这个函数,所以我们直接返回1即可完成。
0x02 总结
文中使用的Frida js全文如下:
var appBaseAddr = Module.findBaseAddress("Paste");
var func = appBaseAddr.add(0x1fbf10)
Interceptor.attach(func, {
    onEnter(this1, args) {
    },
    onLeave(retval) {
        retval.replace(0x1)
    },
})
安装python和Frida后,frida -p pid -l main.js启动注入。pid是进程列表中的进程id。

下载次数, 函数

jack_king   

mac m1 使用 rosetta 打开 paste。执行 frida -p  pid -l main.js 的时候报错 Failed to attach: module not found at "/usr/lib/libSystem.B.dylib"
楼主这种问题有没有遇到? frida 和 rosetta都升级最新版了,还是没解决,网上也没找到类似的问题
QiuChenly
OP
  


hoochanlon 发表于 2023-4-11 20:27
那么“0x01 寻找入口点与分析”标题里的最后一段话,“返回1”,意思就是保持订阅试用期内咯。

也并不是  因为他的判断逻辑是如果订阅有效那么这个地址函数一定会返回1 不区分是试用期还是有效授权期 因为这个函数就是一个判断用户是否为vip的功能
lnleen   

牛啊,这个paste正是我需要的
hoochanlon   

那么“0x01 寻找入口点与分析”标题里的最后一段话,“返回1”,意思就是保持订阅试用期内咯。
Jooing   

跪着看完大神贴
pjy612   

仙侠风没有了。。。
不过还是好厉害
csdoc   

高产的高手!👍
m0fx   

膜拜大佬,涨姿势了
deTrident   

学习了,原来可以这样注入,查找关键点费了很多功夫吧
您需要登录后才可以回帖 登录 | 立即注册

返回顶部