当前位置:网站首页>网站建设>网站制作

长垣小程序制作【长垣企业邮箱】长垣网站外包、长垣微信商城开发、长垣网店美工、长垣淘宝设计

发表日期: 2021-04-30 10:49:12 浏览次数:5

长垣小程序制作【长垣企业邮箱】长垣网站外包、长垣微信商城开发、长垣网店美工、长垣淘宝设计


长垣市,河南省辖县级市,新乡市代管, [1]  位于豫东北地区,介于东经114°29'—114°59'、北纬34°59'—35°23'之间,总面积1051平方千米。 [2]  截至2020年6月,长垣市下辖5个街道、11镇、2乡, [3]  总人口88.18万人(2019年)。 [4]  市政府驻蒲西街道人民路368号。 [1] 

前221年,秦改首邑为长垣县。2019年8月,河南省撤销长垣县,设立县级长垣市。 [1]  属暖温带大陆性季风型气候。 [2]  长济高速、大庆—广州高速公路、济东高速公路、新菏铁路、省道308线、213线穿境而过。

长垣市是全国文明城市 [5]  、国家园林县城 [6]  、国家新型城镇化综合试点地区 [7]  、全国科普示范区 [8]  、节水型社会建设达标县 [9]  、国家农产品质量安全县 [10]  、全国农村创新创业典型县 [11]  、革命文物保护利用片区分县。 [12] 


Jailbreak

本文主要讨论的是应用安全,因此关于越狱实现的部分不做深入介绍。关于XNU内核漏洞的分析和利用网上有很多相关的文章或书籍,比如:

看一些公开的漏洞利用代码对加深印象也很有帮助,比如oob_timestamp的利用。对于本文而言只需要站在巨人的肩膀上使用这些封装好的利用即可。iOS越狱和Android root的的一个很大不同是前者系统封闭性高,碎片化较低,因此提权的方法也相对单一,不支持刷机,大部分都是通过漏洞去获取更高的权限(tfp0)。既然是专注应用层安全,就抓大放小,使用现有的越狱工具,站在前人肩膀上即可。

砸壳

越狱之后总算可以通过 ssh 进入到系统中,也就是相当于安卓世界的 adb shell 而已。既然是逆向分析,下一步就是获取应用的安装包,这在安卓中是一条adb pull命令,但苹果里要复杂一些。

 

iOS 中大部分应用都从 Apple Store 即应用商店下载,而从应用商店下载的 app 是通过苹果签名和加密保护的,这是苹果 FairPlay DRM 数字版权保护的重要部分。为了能使用逆向工具进行分析,需要先对其进行解密,即俗称的砸壳。网上关于砸壳的资料和工具都很多,比如:

这里面有些工具已经年代久远年久失修了,比如我就注意到 dumpdecrypted 在文件沙盒的处理上有点问题导致无法保存文件,需要经过简单 patch 。

 

上面的这些工具大部分都是基于内存 dump,也就是需要通过 exec 执行目标程序才实现砸壳,这可能会让一些 App 通过在初始化函数中自我检测来对抗砸壳。如果出现这种情况,可以通过基于 mremap_encrypted 的方式进行解密,直接调用内核接口,无需启动目标程序实现静态砸壳。

抓手

狱也越了,壳也砸了,接下来呢?直接丢到 IDA 里分析?不是不可以,但 iOS 的应用主体是一个巨大的 mach-o 文件,直接分析是很难的。还是以微信为例,主程序解密后单 arm64 架构的可执行文件就有 218 MB,即便在逆向工具里分析也会让人无从下手。这时候就需要一个入手点,用互联网的黑话来说,就是需要一个抓手。

 

一个最常见的入手点就是 UI 界面。在安卓应用分析中一般通过 Android Studio 提供的uiautomatorviewer可以进行目标应用的 UI 分析,并通过 UI 的 ID 在反编译的代码中查找引用。不过这个方法我一般不用,而是直接获取顶层 Activity 然后找对应的类去分析。

 

在 iOS 中,UI 分析却是一个有效的入手点,因为 iOS 应用都是基于 MVC 结构,View 中触发的事件由对应的 Controller 去实现。不管是基于 Springboard 的拖拖拽拽还是通过代码布局,MVC 的基调是不变的。在 Xcode 中有视图层级调试功能(Debug View Hierarchy),但需要目标开启调试。除此之外,更为常用的是一些视图调试框架,比如 Reveal 或者开源的 FLEX。

 

用 FLEX 直接注入到进程中可实现 UI 的分析:

 

flex.gif

 

这里用到了 cycript,后文再细说。或者可以用 Reveal 在电脑端分析,当然还是需要将 RevealServer.dylib 注入到目标进程中并调用[IBARevealLoader startServer],参考官方的 lldb 脚本,启动后 PC 端界面如下:

 

Reveal

 

点击发送骰子的区域是一个 UIImageView,其本身是没有响应点击事件的,因此要找对应的 ViewController 或者 Gesture Recognizer。

动态分析

根据 UI 信息能知道的是当前界面的 Controller 类是 BaseMsgContentViewController,所以我们可以通过脚本去跟踪发送骰子时该类所有的函数调用,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[-13:04:24.188 Hooked: -[BaseMsgContentViewController didRotateFromInterfaceOrientation:]
[-13:04:24.188 Hooked: -[BaseMsgContentViewController previewingContext:viewControllerForLocation:]
[-13:04:24.188 Hooked: -[BaseMsgContentViewController previewingContext:commitViewController:]
[-13:04:24.188 Hooked: -[BaseMsgContentViewController previewActionItems]
[-13:04:24.189 Hooked: -[BaseMsgContentViewController m_delegate]
[!] 13:04:24.291 Failed to hook: -[BaseMsgContentViewController willShow]
[-13:04:24.291 Hooked: -[BaseMsgContentViewController canPasteImage]
[-13:04:24.292 Hooked: -[BaseMsgContentViewController setTableFooterView:]
[-13:04:24.292 Hooked: -[BaseMsgContentViewController documentInteractionControllerViewControllerForPreview:]
[-13:04:24.292 Hooked: -[BaseMsgContentViewController willAppear]
[-13:04:24.292 Hooked: -[BaseMsgContentViewController showLoadingView]
[-13:04:24.293 Hooked: -[BaseMsgContentViewController initTableView]
[-13:04:24.293 Hooked: 614 methods for class BaseMsgContentViewController
 
# 点击骰子图标发送表情
[-13:04:35.075 ENTER -[BaseMsgContentViewController useTransparentNavibar]
[-13:04:35.076 ENTER -[BaseMsgContentViewController useTransparentNavibar]
[-13:04:35.078 ENTER -[BaseMsgContentViewController shouldInteractivePop]
[-13:04:35.078 ENTER -[BaseMsgContentViewController toolView]
[-13:04:35.132 ENTER -[BaseMsgContentViewController SendEmoticonMesssageToolView:]
[-13:04:35.148 ENTER -[BaseMsgContentViewController findNodeDataByLocalId:]
[-13:04:35.148 ENTER -[BaseMsgContentViewController addMessageNode:layout:addMoreMsg:]
[-13:04:35.148 ENTER -[BaseMsgContentViewController findNodeDataByLocalId:]
[-13:04:35.148 ENTER -[BaseMsgContentViewController getCurContentSizeHeight]
...

frida

其中SendEmoticonMesssageToolView是个值得关注的函数,可以直接用 frida 打印函数堆栈:

1
2
3
4
5
6
7
8
9
10
import { kObjC } from "../../agent/objc";
import { kNative } from "../../agent/native";
 
// kObjC.traceClass("BaseMsgContentViewController");
kObjC.traceMethod("BaseMsgContentViewController""- SendEmoticonMesssageToolView:", {
  onEnter: function(args) {
    log.i("SendEmoticonMesssageToolView", args[0], args[1]);
    kNative.printStackTrace(this.context);
  }
});

输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[-13:10:32.079 Hooked: -[BaseMsgContentViewController SendEmoticonMesssageToolView:]
[+13:10:37.212 SendEmoticonMesssageToolView 0x11f8a1800 0x10bf6e8ba
[-13:10:37.636 backtrace:
0x104e06de4 WeChat!0x2676de4
0x10294cde8 WeChat!0x1bcde8
0x1041bb930 WeChat!0x1a2b930
0x1b8331e34 UIKitCore!-[UICollectionView _selectItemAtIndexPath:animated:scrollPosition:notifyDelegate:deselectPrevious:]
0x1b83464b0 UIKitCore!-[UICollectionView _cellForItemAtIndexPath:includePrefetchedCells:]
0x1b8359e10 UIKitCore!-[UICollectionView touchesEnded:withEvent:]
0x1b46ef730 libsystem_malloc.dylib!nanov2_calloc
0x1b46f4100 libsystem_malloc.dylib!calloc
0x1b48ddae4 CoreFoundation!-[__NSSetM addObject:]
0x1b473926c libobjc.A.dylib!objc_autoreleasePoolPop
0x1b4a14320 CoreFoundation!_CFAutoreleasePoolPop
0x1b48dd594 CoreFoundation!-[__NSSetM enumerateObjectsWithOptions:usingBlock:]
0x1b498bc7c CoreFoundation!__NSSetM_new
0x1bb64e208 QuartzCore!CA::Layer::retain_parent(CA::Transaction*) const
0x1b8b5d9e8 UIKitCore!forwardTouchMethod
0x1b8b5dae4 UIKitCore!-[UIResponder touchesEnded:withEvent:]

在 WeChat 部分的回溯没有符号,因为已经去掉了。

lldb

在 lldb 中也可以看到类似的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(lldbinit) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000105334f04 WeChat`___lldb_unnamed_symbol201841$$WeChat
    frame #1: 0x0000000104b32f80 WeChat`___lldb_unnamed_symbol170264$$WeChat + 160
    frame #2: 0x0000000104e06de4 WeChat`___lldb_unnamed_symbol182196$$WeChat + 2600
    frame #3: 0x000000010294cde8 WeChat`___lldb_unnamed_symbol8796$$WeChat + 248
    frame #4: 0x00000001041bb930 WeChat`___lldb_unnamed_symbol129751$$WeChat + 624
    frame #5: 0x00000001b8331e34 UIKitCore`-[UICollectionView _selectItemAtIndexPath:animated:scrollPosition:notifyDelegate:deselectPrevious:] + 952
    frame #6: 0x00000001b8359e10 UIKitCore`-[UICollectionView touchesEnded:withEvent:] + 572
    frame #7: 0x00000001b8b5d9e8 UIKitCore`forwardTouchMethod + 332
    frame #8: 0x00000001b8b5dae4 UIKitCore`-[UIResponder touchesEnded:withEvent:] + 64
    frame #9: 0x00000001b8b5d9e8 UIKitCore`forwardTouchMethod + 332
    frame #10: 0x00000001b8b5dae4 UIKitCore`-[UIResponder touchesEnded:withEvent:] + 64
    frame #11: 0x00000001b8b5d9e8 UIKitCore`forwardTouchMethod + 332
    frame #12: 0x00000001b8b5dae4 UIKitCore`-[UIResponder touchesEnded:withEvent:] + 64
    frame #13: 0x00000001b86e6478 UIKitCore`_UIGestureEnvironmentUpdate + 6992
    frame #14: 0x00000001b86e48dc UIKitCore`-[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 380
    frame #15: 0x00000001b86e4698 UIKitCore`-[UIGestureEnvironment _updateForEvent:window:] + 248
    frame #16: 0x00000001b8b6d654 UIKitCore`-[UIWindow sendEvent:] + 3512
    frame #17: 0x00000001b8b48840 UIKitCore`-[UIApplication sendEvent:] + 348
    frame #18: 0x0000000103de4920 WeChat`___lldb_unnamed_symbol111457$$WeChat + 404
    frame #19: 0x00000001ebaaa87c UIKit`-[UIApplicationAccessibility sendEvent:] + 100

有符号更好,没有也无所谓,我们还是可以通过地址在逆向工具中查看代码。不过考虑到 ASLR 的存在,需要确认一下镜像的加载地址:

1
2
3
4
(lldbinit) vmmap /WeChat
000102790000 - 00010d6d4000  af44000 R-/private/var/containers/Bundle/Application/2A5D623F-5F8E-4A99-96C8-8CBD00D8B6BE/WeChat.app/WeChat        0 __TEXT
00010d6d4000 - 00010fc40000  256c000 RW- /private/var/containers/Bundle/Application/2A5D623F-5F8E-4A99-96C8-8CBD00D8B6BE/WeChat.app/WeChat  af44000 __DATA
00010fc40000 - 000110ac4000   e84000 R-- /private/var/containers/Bundle/Application/2A5D623F-5F8E-4A99-96C8-8CBD00D8B6BE/WeChat.app/WeChat  cb68000 __LINKEDIT

vmmap 是我自己定义的一个 lldb 命令,参考了 gef 和 pwndbg 在 gdb 中的实现

 

这里加载地址是 0x000102790000,而在 IDA 中 text 段的加载地址是 0x100004000,可以在后者进行 rebase,但是因为函数不多,所以先手动查找。另外用 lldb 也可以直接查看对应函数地址的偏移:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(lldbinit) image lookup -0x0000000105334f04
      Address: WeChat[0x0000000102ba4f04] (WeChat.__TEXT.__text + 45747972)
      Summary: WeChat`___lldb_unnamed_symbol201841$$WeChat
(lldbinit) image lookup -0x0000000104b32f80
      Address: WeChat[0x00000001023a2f80] (WeChat.__TEXT.__text + 37351296)
      Summary: WeChat`___lldb_unnamed_symbol170264$$WeChat + 160
(lldbinit) image lookup -0x0000000104e06de4
      Address: WeChat[0x0000000102676de4] (WeChat.__TEXT.__text + 40316388)
      Summary: WeChat`___lldb_unnamed_symbol182196$$WeChat + 2600
(lldbinit) image lookup -0x000000010294cde8
      Address: WeChat[0x00000001001bcde8] (WeChat.__TEXT.__text + 1805800)
      Summary: WeChat`___lldb_unnamed_symbol8796$$WeChat + 248
(lldbinit) image lookup -0x00000001041bb930
      Address: WeChat[0x0000000101a2b930] (WeChat.__TEXT.__text + 27425072)
      Summary: WeChat`___lldb_unnamed_symbol129751$$WeChat + 624

符号恢复

根据 lldb 中每个函数地址在 TEXT 段中的偏移,可以在 IDA 中直接跳转到对应函数(返回值),整理对应的调用函数分别是:

既然 IDA 可以识别出对应 OC 函数的符号,那么理论上这些符号也是可以还原的,如果经常需要进行动态分析,那么可以通过一些方法自动化恢复对应的符号,可以参考 iOS符号表恢复 以及 restore-symbol 等项目。

静态分析

通过动态分析找到了着手点,以及通过回溯调用栈找到了一些相关函数,其中并没有一目了然的设置骰子点数的函数 setDiceValue,所以要想改点数还需要对上面的函数进行进一步分析。到这里,逆向工程是不可避免了,一般而言我是能躺着绝不坐着,能坐着绝不站着,不到万不得已是不去逆向的。

 

花开两朵,各表一枝,在前面砸完壳的第一时间我就预感到需要逆向,所以早早地把 200MB 的 MachO 丢进了 IDA,在文章写到这里时已经过去了四个小时,期间看了两集动漫,但是 IDA 还是没有全部分析完。所以在等待的时间里,先来简单介绍些 ObjectiveC 的底层实现。

ObjectiveC 101

在学习 OC 的时候,总会看到说 OC 语言实现面向对象是通过发送消息,具体是怎么发送呢?因此在实际逆向之前,我们先来自己写一个程序来进行分析,以便了解 OC 的调用原理。

 

以下面简单的 helloworld 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import <Foundation/Foundation.h>
 
@interface MyClass: NSObject
 - (void)insMethod:(const char *)a1 arg2:(int)a2;
 + (void)clsMethod:(const char *)a1 arg2:(int)a2;
@end
 
@implementation MyClass
 - (void)insMethod:(const char *)a1 arg2:(int)a2 {
     NSLog(@"insMethod called, self=%p, a1=%s"self, a1);
 }
 + (void)clsMethod:(const char *)a1 arg2:(int)a2 {
     NSLog(@"clsMethod called, self=%p, a1=%s"self, a1);
 }
@end
 
int main() {
    MyClass *= [[MyClass alloc] init];
    [MyClass clsMethod:"hello" arg2:1];
    [c insMethod:"world" arg2:2];
    return 0;
}

主要调用了两个方法,一个类方法和一个成员方法,编译后查看其汇编代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(lldbinit) disassemble
main @ test_hello:
    0x100003e90: push   rbp
    0x100003e91: mov    rbp, rsp
    0x100003e94: sub    rsp, 0x10
    0x100003e98: mov    dword ptr [rbp - 0x4], 0x0
->  0x100003e9f: mov    rax, qword ptr [rip + 0x424a] ; (void *)0x0000000100008120: MyClass
    0x100003ea6: mov    rdi, rax
    0x100003ea9: call   0x100003f04               ; symbol stub for: objc_alloc_init
    0x100003eae: mov    qword ptr [rbp - 0x10], rax
    0x100003eb2: mov    rax, qword ptr [rip + 0x4237] ; (void *)0x0000000100008120: MyClass
    0x100003eb9: mov    rsi, qword ptr [rip + 0x4220] ; "clsMethod:arg2:"
    0x100003ec0: mov    rdi, rax
    0x100003ec3: lea    rdx, [rip + 0xa8]         ; "hello"
    0x100003eca: mov    ecx, 0x1
    0x100003ecf: call   qword ptr [rip + 0x12b]   ; (void *)0x00007fff20439d00: objc_msgSend
    0x100003ed5: mov    rax, qword ptr [rbp - 0x10]
    0x100003ed9: mov    rsi, qword ptr [rip + 0x4208] ; "insMethod:arg2:"
    0x100003ee0: mov    rdi, rax
    0x100003ee3: lea    rdx, [rip + 0x8e]         ; "world"
    0x100003eea: mov    ecx, 0x2
    0x100003eef: call   qword ptr [rip + 0x10b]   ; (void *)0x00007fff20439d00: objc_msgSend
    0x100003ef5: xor    eax, eax
    0x100003ef7: add    rsp, 0x10
    0x100003efb: pop    rbp
    0x100003efc: ret

且不管初始化部分,后面两个函数调用最终都进入了 objc_msgSend 函数,在苹果官网可以看到其函数原型:

1
id objc_msgSend(self, op, ...);

其中各个参数为:

  1. self: 消息的接收方,对于类方法为指向类的指针,对于对象方法而言为对象指针;

  2. op: 为消息的标识符,也称为 selector,实际上是一个表示对应方法名称的字符串;

  3. ... 其他方法参数;

回到上面的例子,第一次类方法调用的参数为:

1
2
3
4
5
6
7
8
(lldbinit) p/x $rdi
(unsigned long) $29 = 0x0000000100008120
(lldbinit) x/1s $rsi
0x100003f86"clsMethod:arg2:"
(lldbinit) x/1s $rdx
0x100003f72"hello"
(lldbinit) p/x $rcx
(unsigned long) $32 = 0x0000000000000001

第二次成员方法的调用参数为:

1
2
3
4
5
6
7
8
(lldbinit) p/x $rdi
(unsigned long) $54 = 0x0000000100208340
(lldbinit) x/1s $rsi
0x100003f96"insMethod:arg2:"
(lldbinit) x/1s $rdx
0x100003f78"world"
(lldbinit) p/x $rcx
(unsigned long) $57 = 0x0000000000000002

程序的输出如下:

1
2
2021-04-10 22:54:19.487148+0800 test_hello[99332:5120374] clsMethod called, self=0x100008120, a1=hello
2021-04-10 22:54:54.565229+0800 test_hello[99332:5120374] insMethod called, self=0x100208340, a1=world

所以,ObjectiveC 在实现上还是挺接近 C 语言的,这对于我们逆向而言方便很多。

头铁逆向

了解了简单的 OC 逆向之后,IDA 也跑的差不多了。根据栈回溯对应 OC 函数的名称,逐级往上看。首先是 didSelectorSelfDefinedEmotcion这个函数,表示选中了某个自定义表情,里面只是一些发送操作,所以并不是我们想要的;

 

然后是 -[EmoticonBoardView onTapEmoticonWrap:atIndex:maxCountPerLine:fromSection:] ,该函数中主要是判断所选择的表情是否为自拍表情或者自定义表情,如果是的话就进行异步上传。其调用didSelectorSelfDefinedEmotcion 的参数为 a3,即一个对象指针,虽然还不知道是哪个对象,但知道其包含这些属性(方法):

在 class-dump 导出的头文件中搜索,可以发现是CEmoticonWrap 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21