2

新博客

@[toc]

前言

  • Hopper

是一种适用于 OS X 和 Linux 的逆向工程工具,可以用于反汇编、反编译和调试 32位/64位英特尔处理器的 Mac、Linux、Windows 和 iOS 可执行程序!

hopper常用的操作:直接修改汇编代码 (在菜单Modify - Assemble Instruction 进行汇编代码的修改)
  • LLDB是Low Level Debugger的简称,

在iOS开发的调试中LLDB是经常使用的,LLDB是Xcode内置的动态调试工具。使用LLDB可以动态的调试你的应用程序,如果你不做其他的额外处理,因为debugserver缺少task_for_pid权限,所以你只能使用LLDB来调试你自己的App。
那么本篇博客中就要使用LLDB来调试从AppStore下载安装的App,并且结合着Hopper来分析第三方App内部的结构。
LLDB与Hopper的结合,会让你看到不一样的东西.

在上篇博客《iOS逆向工程之脱壳》 我们已经给微信进行可脱壳处理,所以使用Hopper进行处理是没有问题的。

此部分我们就要将Hopper与LLDB结合在一起发挥其双剑合璧的作用。该部分也算是本篇博客中实战的一部分。

I、查看WeChat线程的模块偏移后基地址

1.1 基础概念

  • 模块基地址
`模块在内存中的起始地址----模块基地址
  • ASLR偏移
ASLR偏移 ---- 虚拟内存起始地址与模块基地址的偏移量
  • lldb命令的格式如下 :
<noun> <verb> [-options [option-value]] [argument [argument..]]

1.2 查看WeChat线程的模块偏移后基地址


LLDB连接上debugserver后,我们首先使用下方的命令来查看当前进程中的所有模块。从这些输出信息中我们能找到“WeChat”这个进程在虚拟内存相对于模块基地址的偏移量。

(lldb) image list -o -f
[  0] 0x00082000 /private/var/mobile/Containers/Bundle/Application/01ECB9D1-858D-4BC6-90CE-922942460859/WeChat.app/WeChat(0x0000000000086000)

第一个就是“WeChat”程序的相关信息
左边0x00082000就是ASLR偏移量(随机偏移量);ASLR偏移量其实就是虚拟内存的启始地址,相对于模块基地址的偏移量。
右边0x0000000000086000的地址就是偏移后的地址。

从输出结果我们可以知道:ASLR偏移量 = 0x00082000, 模块偏移后基地址 = 0x0000000000086000

下方是使用Hopper打开的解密后的微信的安装包,其起始地址从下图中我们可以看出是0x4000, 这个地址就是模块偏移前的地址,也就是模块在虚拟内存中的起始地址。从Hopper中我们可以知道:模块偏移前的基地址=0x4000
这里写图片描述

从上面两组数据我们可以得出:

  模块偏移后的基地址(0x0000000000086000)= ASLR偏移量(0x00082000)+ 模块偏移前基地址(0x4000)

上面这个公式是尤为重要的,因为Hopper中显示的都是“ 模块偏移前基地址”,而LLDB要操作的都是“模块偏移后的基地址”。
所以从Hopper到LLDB,我们要做一个地址偏移量的转换。这个会多次用到。
当然,有一点需要注意的是Hopper与LLDB所选择的AMR架构的位数得一致,要么是32位,要么都是64位,如果位数不匹配的话,那么计算出来的内存地址肯定是不对的。

II、使用LLDB给微信登录添加断点的例子


将断点添加在登录页面的初始化方法中

分析:
首先得知道手机号“登录”视图控制器的内存地址,然后才可以使用LLDB添加断点。
使用dump出来的头文件,或者使用hopper
WCAccountPhoneLoginControlLogic

- (id)initWithData:(id)arg1;

下方截图中的地址就是“initWithData:”方法偏移前的基地址。根据上面的公式我们很容易就可以计算出该方法“偏移后的基地址”也就是真正的内存地址。算法如下所示:
initWithData内存地址 = 0x01ba1c7e + 0x00082000(ALSR偏移) = 0x01c23c7e

这里写图片描述

III、常用命令

//0x000000015bd0ff80等是某个虚拟地址
po 0x000000015bd0ff80
mem read 0x000000015bd0ff80 ;用来取内存查看内存
watchpoint set expression -w read_write -- 0x00000002812cdb80 ;用来设置watchpoint,来观察某个内存的读写情况;
watchpoint set expression -w write -- 0x0000000281281e00
dis -a 0x000000015bd0ff80; //反汇编某个地址,如果该地址是个函数符号,则可以看到对应汇编代码;
x 0x000000015bd0ff80 ; //这个命令x/n比较有意思,查看对应地址往后n字节的内存
breakpoint set -a 0x00000001ca9abc0c ;命令行的方式设置断点地址,特别有利于设置非符号断点,可直接根据crash地址计算得到当前的运行地址然后直接设置断点.
im li ;这个命令把当前所有加载的binary images的地址给出来,可用来结合进行动态分析;
thread return 0;直接当前函数调用return或return返回值0
image lookup -a 0x202952620; 符号查找

3.1、添加断点


使用下述命令,给上述地址添加断点。断点添加后,点击登录按钮就会跳转到“手机号登录”页面就会执行该断点,下方截图的红框中就是“断点”执行后的效果。从下方截图中我们可以看出该断点的编号是1,Breakpoint后方就是断点编号,该编号会在操作断点是会用到,下方会给出实例。

br s -a 0x01C23C7E

如果发现错误,立即重现查看下

(lldb) image list -o -f|grep 'WeChat'
[  0] 0x00001000 /private/var/mobile/Containers/Bundle/Application/5CC4B194-DDC5-442F-A117-2D135C3FCEA9/WeChat.app/WeChat(0x0000000000005000)

此时的ALSR偏移 = 0x00001000;
因此

initWithData内存地址 = 0x01ba1c7e + 0x00001000(ALSR偏移) = 0x01ba2c7e

br s -a 0x01ba2c7e
(lldb) br s -a 0x01ba2c7e
Breakpoint 1: where = WeChat`ClearDataItem::compareTime(std::__1::shared_ptr<ClearDataItem> const&, std::__1::shared_ptr<ClearDataItem> const&) + 4897562, address = 0x01ba2c7e

重新生成WeChat.decrypted 6.5.18
这里写图片描述

(lldb) image list -o -f|grep 'WeChat'
[  0] 0x00017000 /private/var/mobile/Containers/Bundle/Application/5CC4B194-DDC5-442F-A117-2D135C3FCEA9/WeChat.app/WeChat(0x000000000001b000)

initWithData内存地址 = 0x016dbf3a + 0x00017000(ALSR偏移) = 0x016f2f3a

br s -a 0x016f2f3a
(lldb) br s -a 0x000c3000+0x016dbf3a
Breakpoint 17: where = WeChat`__cxa_throw + 16426614, address = 0x0179ef3a

还是没有成功,待续

3.2、断点的单步执行(ni, si)


你可以通过nexti (简写:ni)和stepi (简写:si)来进行单步的调试。ni遇到跳转不会进入到跳转中去,而si则会跳转到相应的分支中去。

(lldb) ni
Process 609 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
    frame #0: 0x327e6478 libsystem_kernel.dylib`mach_msg_trap + 24
libsystem_kernel.dylib`mach_msg_trap:
->  0x327e6478 <+24>: bx     lr

libsystem_kernel.dylib`mach_msg_overwrite_trap:
    0x327e647c <+0>:  mov    r12, sp
    0x327e6480 <+4>:  push   {r4, r5, r6, r8}
    0x327e6484 <+8>:  ldm    r12, {r4, r5, r6, r8}
(lldb) si
Process 609 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x327e626c libsystem_kernel.dylib`mach_msg + 40
libsystem_kernel.dylib`mach_msg:
->  0x327e626c <+40>: cmp    r0, #0x0
    0x327e626e <+42>: beq    0x327e62be                ; <+122>
    0x327e6270 <+44>: str.w  r10, [sp, #0x10]
    0x327e6274 <+48>: tst.w  r10, #0x40

3.3 放开执行该断点(c)


命令c可以执行该断点, 上面这种情况如果执行c命令,因为只有一个断点,该断点执行后,就会跳转到“手机号登陆页面”。

(lldb) c
Process 609 resuming

3.4断点的禁用和开启


上面也有提到,上述创建的断点的编号是1,我们要对该断点进行禁用和开启操作,具体命令如下所示:

br dis 1 -- 禁用(disable)编号为1的断点

br en 1 -- 启用(enable)编号为1的断点

br dis  -- 禁用所有断点

br en  -- 启用所有断点
(lldb) c
Process 609 resuming
(lldb) br dis 1
1 breakpoints disabled.
(lldb) br en 1 
1 breakpoints enabled.

3.5、删除breakpoints


(lldb) br dis
All breakpoints disabled. (5 breakpoints)
(lldb) br del
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (5 breakpoints)
br del 1 -- 删除(delete)编号为1的断点

3.6、 退出lldb

(lldb) br del
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (1 breakpoint)
(lldb) exit
Quitting LLDB will kill one or more processes. Do you really want to proceed: [Y/n] y

IV、lldb 操作寄存器的值

4.1.输出寄存器的值(p, po)


我们使用$来访问某个寄存器中的值,并且使用p命令进行打印。下方就是通过p命令将r1寄存器中所存的内容进行打印,在打印之前将$r1进行类型转换,po命令则输出了Objective-C的对象,而p输出的是C语言类型的数据。如下所示:

(lldb) po $r0
(lldb) p (char *)$r1
(char *) $1 = 0x07000806 "\xffffffec\xffffff99"

我们还可以将一个地址所存放的值进行打印,下方这个命令就是输出了$sp指针所指的地址处所存放的值:

(lldb) p/x $sp
(unsigned int) $2 = 0x039a3c74

4.2.修改寄存器中的值


我们不仅可以查看某些寄存器中的值,而且可修改寄存器中的中,通过下述命令我们就可以修改指的寄存器中的值。

register write 寄存器 值

接过在Hopper中对登录模块的分析,发现“WCAccountManualAuthControlLogic”这个类中的“handleAuthResponse:”方法就是用来处理“登录认证响应”的方法。
也就是说“handleAuthResponse:”负责处理登录业务逻辑的网络响应,并且在这个函数的前边有一个比较(cmp r0, r1), 根据r0和r1的比较结果来进行跳转。

这里写图片描述

接下来我们要做的事情就是,在比较寄存器r0和r1中的值时我们要改变r1寄存器中的值,然后观察App的运行效果。下方这个截图是随便输入手机号和密码时所提示的内容。也就是正常的流程会弹出下方的框。

这里写图片描述

(lldb) image list -o -f|grep 'WeChat'
[  0] 0x00012000 /private/var/mobile/Containers/Bundle/Application/5CC4B194-DDC5-442F-A117-2D135C3FCEA9/WeChat.app/WeChat(0x0000000000016000)

接下来我们要做的就是给
`(lldb) br s -a 0x00012000+0x13f087a
`(cmp)这个内存地址添加断点,然后去修改寄存器r1的值。

接下来我们先将r0和r1中的值进行打印$r0 ,$r1中的值改成$r0

V、总结

5.1 lldb 常用命令

lldb命令的格式如下 :

<noun> <verb> [-options [option-value]] [argument [argument..]]
  • 连接server
process connect connect://127.0.0.1:12345
  • 查看进程在虚拟内存相对于模块基地址的偏移量。
image list -o -f
  • 只查看微信的进程
(lldb) image list -o -f|grep 'WeChat'

5.1.0 断点管理

  • 简单地通过文件和行号进行断点 :
breakpoint set --file foo.c --line 12
breakpoint set -f foo.c -l 12
  • 通过函数的名称来设置断点 :
breakpoint set --name foo
breakpoint set -n foo
  • 可以同时使用 -n来设置多个函数的断点:

`breakpoint set -n foo -n bar

  • `要为C++函数设置断点,使用method :
breakpoint set --method foo
breakpoint set -M foo
  • 设置 OC里面selector的断点 :
breakpoint set --seletor alignLeftEdges:
breakpoint set -S alignLeftEdges:
  • 也可以针对可执行的image设置断点 :
breakpoint set --shlib foo.dylib --name foo
breakpoint set -s foo.dylib -n foo

可以重复使用 --shlib来标记多个公共库。

  • breakpoint简写为br :
breakpoint set -n "-[SKTGraphicView alignLeftEdges:]"
br s -n "-[SKTGraphicView alignLeftEdges:]"
(lldb) br list
No breakpoints currently set.
(lldb) br li 
No breakpoints currently set.

使用 breakpoint delete id 删除断点,简写 br del, 使用breakpoint enable id 和 breakpoint disable id 来启用或禁用断点,

5.1.1 自定义alias

我们可以设置alias :

command alias bfl breakpoint set -f %1 -l %2

bfl foo.c 12

用户可以修改~/.lldbinit文件,以存储自己的快捷键。

5.1.2 流程控制

  • process continue 取消暂停,继续执行函数 ,别名是 continue,简写是 c.
  • thread step-over : 执行当前函数中的一行代码,但是不跳进函数。简写是 next ,n 。
  • thread step-in : 跳进函数中, 简写 step,s 。
  • thread step-out : 跳出函数,简写是 finish 。
  • thread list : 列出线程列表
  • thread backtrace : 列出线程堆栈。可以通过thread select 2切换线程。 thread backtrace all 输出所有线程堆栈
  • thread return <RETURN EXPRESSION> : 直接返回当前函数, 可以设置返回值。

5.1.2.1 thread return

5.1.3 查看当前的断点的函数信息。

(lldb) frame info 
frame #0: 0x0000000114ab4e76 libsystem_kernel.dylib`mach_msg_trap + 10
  • 打印出view的所有层次信息
po [[UIApp keyWindow] recursiveDescription]

5.1.4 添加Action以在断点时,执行自定义事件

(lldb) breakpoint set -n isEven
Breakpoint 1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00
(lldb) breakpoint modify -c 'i == 99' 1 
(lldb) breakpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> p i
> DONE
  • 添加action的触发条件

(lldb) breakpoint modify -c 'i == 99' 1


iOS逆向
44 声望15 粉丝

引用和评论

0 条评论