声明:此文仅仅用于学习研究,也请大家不要用于商业或其他非法途径上~~

说明:尝试最终卡在了重签名上,安装后的应用持续闪退。目前还没找到解决方法。。。大家如果有什么好方法,欢迎回复~

前言

之前通过对 iOS 冰与火之歌番外篇 - 在非越狱手机上进行 App Hook 走了一遍,尝试到了一点甜头。看到文中抢红包的动画是在太诱惑了,所以把微信作为目标,准备一步步自己实现同样的目标。

目标要一步一步来。与之前相比,这次真的没有源码,也没有 debug 证书打的包。因此先从给微信加 hook 开始。

步骤:

  1. 下载 ipa 文件。可通过在 itunes 下载应用然后右键->open in finder 找到 ipa 文件。当然,抓包也可以。
  2. 在头添加 hook1.dylib 的加载命令
  3. 重签名
  4. 运行

关于微信的获取

一开始打算直接用 app store 上的微信,结果发现在成功添加 hook ,成功重签并装上设备后,打开 app 还是会闪退。系统错误提示如下:

Mar 23 22:43:46 hengjie-chens-iPad SpringBoard[644] <Error>:  SecTrustEvaluate  [leaf IssuerCommonName SubjectCommonName]
Mar 23 22:43:47 hengjie-chens-iPad SpringBoard[644] <Error>:  SecTrustEvaluate  [leaf IssuerCommonName SubjectCommonName]
Mar 23 22:43:47 hengjie-chens-iPad kernel[0] <Notice>: AppleFairplayTextCrypterSession::fairplayOpen() failed, error -42022
Mar 23 22:43:47 hengjie-chens-iPad com.apple.xpc.launchd[1] (UIKitApplication:com.tencent.xin[0x7c18][1086]) <Notice>: Service exited due to signal: Killed: 9
Mar 23 22:43:47 hengjie-chens-iPad assertiond[653] <Warning>: Unable to obtain a task name port right for pid 1086: (os/kern) failure (5)
Mar 23 22:43:47 hengjie-chens-iPad SpringBoard[644] <Warning>: Unable to register for exec notifications: No such process
Mar 23 22:43:47 hengjie-chens-iPad SpringBoard[644] <Warning>: Unable to obtain a task name port right for pid 1086: (os/kern) failure (5)
Mar 23 22:43:47 hengjie-chens-iPad SpringBoard[644] <Warning>: Unable to obtain a task name port right for <FBApplicationProcess: 0x150211ac0; com.tencent.xin; pid: 1086>
Mar 23 22:43:47 hengjie-chens-iPad SpringBoard[644] <Warning>: Application 'UIKitApplication:com.tencent.xin[0x7c18]' exited abnormally via signal.
Mar 23 22:43:47 hengjie-chens-iPad SpringBoard[644] <Warning>: Application '(null)' exited for an unknown reason.

参考 iOS 冰与火之歌番外篇 - App Hook 答疑以及 iOS 9 砸壳,app store 上的应用都是加密的。若需要进行 hook 及重打包,必须获取到解密后的 app 才行。否则即使 Hook 成功,签名成功,安装成功,app 还是会闪退。

纸上说来终觉浅,我们先来验证一下 app store 上的微信是否真的加了密。

确认 app 是否加密

第一步,获取 ipa 文件。

方法很多,我使用的是用 itunes 下载微信,然后通过在 finder 显示文件获取到 ipa 。

第二步,解压获得二进制文件,并查看包含的架构

$ unzip WeChat_6.3.13.ipa -d wechat_6.3.13
...
$ cd wechat_6.3.13/Payload/WeChat.app/
$ file Wechat
Wechat: Mach-O universal binary with 2 architectures
Wechat (for architecture armv7):    Mach-O executable arm
Wechat (for architecture arm64):    Mach-O 64-bit executable

第三步,通过 otool -l 输出 app load commands ,然后查看 cryptid 标志位的值是否为 1(已加密)

$ otool -l Wechat | grep crypt
     cryptoff 16384
    cryptsize 38748160
      cryptid 1
     cryptoff 16384
    cryptsize 41467904
      cryptid 1

很不幸,看来两个架构都被加密了。

砸壳

按照文中指示,对 armv7 架构进行砸壳是通用性最强的。通过 iossupportmatrix 找到了采用 armv7 架构的设备清单:

手上虽然有部 iPhone ,但考虑到越狱相对麻烦,这个留待下次进行。

虽然没有亲自砸壳,但还是有办法拿到砸壳后的 app 的。那就是越狱市场。

在 pp 助手上找到了越狱版微信(6.3.15)。虽然和原来的版本略有出入,但能用就好。

下载后的文件重命名为 Wechat_6.3.15.16_jailbreak_pp.ipa 。再次检查加密相关字段:

$ otool -l WeChat | grep crypt
     cryptoff 16384
    cryptsize 39305216
      cryptid 0
     cryptoff 16384
    cryptsize 42057728
      cryptid 0

ok,顺利完成

制作并加入 hook1.dylib

参照 iOS 冰与火之歌番外篇 - 在非越狱手机上进行 App Hook 。由于目前只是试验,里面先不对具体方法进行 hook ,只是在启动时加入一句 Log :

#import <Foundation/Foundation.h>
#import "CaptainHook/CaptainHook.h"

__attribute__((constructor)) static void entry()
{
    NSLog(@"Chj hook begins");
}

然后 build (记得选择用真机,不要用模拟器)。build 完后的结果会放在 Xcode 项目缓存目录 ~/Library/Developer/Xcode/DerivedData,打开这个文件夹后找到前缀和项目名一致的文件夹,找到里面的 Build/Products/Debug-iphoneos ,把 hook4wechat.dylib hook4wechat.dylib.dSYM 两个文件先拷贝出来待用。

在应用头部添加 hook 的加载命令

这里参考 iOS 冰与火之歌番外篇 - 在非越狱手机上进行 App Hook 中的方法,用 yololib 来插入。

➜  WeChat.app  yololib WeChat hook4wechat.dylib
2016-03-06 22:13:07.861 yololib[77851:9213874] dylib path @executable_path/hook1.dylib
2016-03-06 22:13:07.864 yololib[77851:9213874] dylib path @executable_path/hook1.dylib
Reading binary: WeChat

2016-03-06 22:13:07.864 yololib[77851:9213874] FAT binary!
2016-03-06 22:13:07.865 yololib[77851:9213874] Injecting to arch 9
2016-03-06 22:13:07.866 yololib[77851:9213874] Patching mach_header..
2016-03-06 22:13:07.866 yololib[77851:9213874] Attaching dylib..

2016-03-06 22:13:07.867 yololib[77851:9213874] Injecting to arch 0
2016-03-06 22:13:07.867 yololib[77851:9213874] 64bit arch wow
2016-03-06 22:13:07.867 yololib[77851:9213874] dylib size wow 56
2016-03-06 22:13:07.867 yololib[77851:9213874] mach.ncmds 73
2016-03-06 22:13:07.868 yololib[77851:9213874] mach.ncmds 74
2016-03-06 22:13:07.868 yololib[77851:9213874] Patching mach_header..
2016-03-06 22:13:07.868 yololib[77851:9213874] Attaching dylib..

2016-03-06 22:13:07.868 yololib[77851:9213874] size 52
2016-03-06 22:13:07.868 yololib[77851:9213874] complete!

插入后用 MachOView 打开确认

Screen Shot 2016-03-28 at 10.26.25 PM.png-83.9kB

重签名

主要需要三个步骤

  1. 获取 embedded.mobileprovision
  2. 提供 entitlement 信息
  3. 对子组件也一并进行重签名

获取 embedded.mobileprovision

因为穷到没有苹果开发者账号,所以没办法在 developer center 里配置。但也有其它办法。

首先,通过 Info.plist 找到微信的 bundle id com.tencent.xin

然后自己创建一个使用同名 bundle id 的项目,进行编译就能找到 embedded.mobileprovision 了。

你觉得就是这么简单吗?你自己动手试试这个 bundle id 和微信一样的项目能不能正常签上名?

是的。bundle id 作为唯一标识,不能和其它应用重名,否则生成 mobileprovision 时会报错:An App ID with Identifier 'com.tencent.xin' is not available. Please enter a different string.。所以这里直接用同名 bundle id 是不可能获取到 mobileprovision 的。

因此,这里换个 bundle id :com.tencent.xin4hook 。然后在生成的 .app 文件中获取到 embedded.mobileprovision。

生成 Entitlement.plist

可以按照文中的方法,通过 iOSOpenDev 提供的 ldid 进行解析(如果这个 ldid 用不了,可以试试这个)。此外,参考 代码签名探析 ,还有一种更方便的获取方法

$ codesign -d --entitlements - WeChat.app
...
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>com.apple.developer.team-identifier</key>
        <string>88L2Q4487U</string>

        <key>com.apple.developer.healthkit</key>
        <true/>

        <key>application-identifier</key>
        <string>532LCLCWL8.com.tencent.xin</string>

        <key>com.apple.external-accessory.wireless-configuration</key>
        <true/>

        <key>com.apple.developer.networking.HotspotHelper</key>
        <true/>

        <key>com.apple.developer.networking.networkextension</key>
        <array>
            <string>packet-tunnel-provider</string>
            <string>app-proxy-provider</string>
            <string>content-filter-provider</string>
        </array>

        <key>aps-environment</key>
        <string>production</string>

        <key>com.apple.security.application-groups</key>
        <array>
            <string>group.com.tencent.xin</string>
        </array>
...

我一开始不知道撞了什么邪,ldid 老是报 minimal/mapping.h(54): _assert(false); errno=2 这个错误。 Google 和 Stackoverflow 都说是我的 xcode command line 没装,但我装了啊,instruments,xcodebuild 等命令都好好的。结果等到第二天再试竟然自己好了。。。

回想一下,也许是因为我重启了几次 xcode ?

好了,先跳过把。

原文说理论上要签上原 app 对应的所有 entitlement 。因此先尝试这种方式。直接把输出值放到 Entitlement.plist 文件中。

注:关于 Entitlements.plist 的 Key 和 value 含义可以参考苹果的 About Entitlements

重签名应用

参考蒸米的文章:

codesign -f -s "iPhone Developer: zhengmin1989@gmail.com (**********)" WeChat.app/Watch/WeChatWatchNative.app/PlugIns/WeChatWatchNativeExtension.appex
codesign -f -s "iPhone Developer: zhengmin1989@gmail.com (**********)" WeChat.app/Watch/WeChatWatchNative.app
codesign -f -s "iPhone Developer: zhengmin1989@gmail.com (**********)" WeChat.app/PlugIns/WeChatShareExtensionNew.appex
codesign -f -s "iPhone Developer: zhengmin1989@gmail.com (**********)" WeChat.app/hook2.dylib
codesign -f -s "iPhone Developer: zhengmin1989@gmail.com (**********)" --entitlements Entitlements.plist WeChat.app

由于穷,所以我用的是 XCode7 施舍的免费个人开发者证书。。。

结果在最后一步出错:

codesign -f -s "iPhone Developer: 704495442@qq.com (**********)" --entitlements Entitlements.plist WeChat.app
Entitlements.plist: cannot read entitlement data

经过搜索发现,原来这个 plist 不是单纯的文本文件,而是二进制文件,文件开头有一些特殊的二进制字符。因此用 xcode 打开然后保存一次即可。

安装

最后一步了。操作很简单,但所有问题都会暴露在这里

$ ideviceinstaller -i WeChat.app
Uploading WeChat.app package contents... DONE.
Installing '(null)'
 - CreatingStagingDirectory (5%)
 - ExtractingPackage (15%)
 - InspectingPackage (20%)
 - TakingInstallLock (20%)
 - PreflightingApplication (30%)
 - InstallingEmbeddedProfile (30%)
 - VerifyingApplication (40%)
 - CreatingContainer (50%)
 - InstallingApplication (60%)
 - PostflightingApplication (70%)
 - SandboxingApplication (80%)
 - GeneratingApplicationMap (90%)
 - Complete

嗯。安装成功了。但打开闪退。。。肯定哪里姿势不对。

重新参照了整篇文章,最大可能出问题的就是砸壳。看来还是得亲自动手啊。

砸壳

主要参考 一步一步实现 iOS 微信自动抢红包 (非越狱)

幸好手上有部越狱机,iPhone4 + iOS 7。先装好 OpenSSH、Cycript 。

首先,获取需要砸壳的 app 的二进制文件地址。

# ps ax
...
  536   ??  Ss     0:03.63 /var/mobile/Applications/694EDC84-5D40-458E-956E-1041530DC7E6/WeChat.app/WeChat

然后,获取 app 的 Document 目录

# cycript -p Wechat
cy# NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]
@ /var/mobile/Applications/694EDC84-5D40-458E-956E-1041530DC7E6/Documents

编译及拷贝 dumpdecrypted.dylib 文件到应用的 Document 目录:

# scp ./dumpdecrypted.dylib root@192.168.1.133:/var/mobile/Applications/694EDC84-5D40-458E-956E-1041530DC7E6/Documents/
root@192.168.1.133's password:
dumpdecrypted.dylib                                          100%  193KB 192.9KB/s   00:00

开始砸壳

# DYLD_INSERT_LIBRARIES=/var/mobile/Applications/694EDC84-5D40-458E-956E-1041530DC7E6/Documents/dumpdecrypted.dylib /var/mobile/Applications/694EDC84-5D40-458E-956E-1041530DC7E6/WeChat.app/WeChat
mach-o decryption dumper

DISCLAIMER: This tool is only meant for security research purposes, not for application crackers.

[+] detected 32bit ARM binary in memory.
[+] offset to cryptid found: @0x13a4c(from 0x13000) = a4c
[+] Found encrypted data at address 00004000 of length 39305216 bytes - type 1.
[+] Opening /private/var/mobile/Applications/694EDC84-5D40-458E-956E-1041530DC7E6/WeChat.app/WeChat for reading.
[+] Reading header
[+] Detecting header type
[+] Executable is a FAT image - searching for right architecture
[+] Correct arch is at offset 16384 in the file
[+] Opening WeChat.decrypted for writing.
[+] Copying the not encrypted start of the file
[+] Dumping the decrypted data into the file
[+] Copying the not encrypted remainder of the file
[+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset 4a4c
[+] Closing original file
[+] Closing dump file

把砸壳后的文件拷贝出来

scp root@192.168.1.133:/var/mobile/Applications/694EDC84-5D40-458E-956E-1041530DC7E6/Documents/WeChat.decrypted .
root@192.168.1.133's password:
WeChat.decrypted

检查是否已经被解密

otool -l WeChat.decrypted | grep crypt
WeChat.decrypted (architecture armv7):
     cryptoff 16384
    cryptsize 39305216
      cryptid 0
WeChat.decrypted (architecture arm64):
     cryptoff 16384
    cryptsize 42057728
      cryptid 1

可以看到,只有 armv7 被解密了。不过足够了。

再次尝试

然后把这个 WeChat.decrypted 重命名为 WeChat ,覆盖 Wechat.app 下同名文件,再重新加 dylib 头、加 embedded.mobileprovision,重签名。

好吧,安装后还是闪退。看来不是破壳问题。还得继续查。。。目前已购买收费个人开发者证书,准备把授权加上再尝试。

大家如果有什么好方法,欢迎回复

updated in 4.10

终于解决这个问题了。我之所以一直闪退和砸壳、换 bundle id 和换 entitlements 无关,应该是和平时使用第三方助手时会出现的闪退一个道理。具体什么道理,我也不清楚,待探究。。。

解决方案很简单:最后不要直接安装 .app ,而是使用下面的命令生成 ipa 后安装 ipa 文件:

$ xcrun -sdk iphoneos PackageApplication -v Wechat.app  -o `pwd`/Wechat_resign.ipa

启动后就出现添加进去的 Hook 信息了:

总结

  1. 不要随便耍小聪明偷懒。。。就因为偷了转成 ipa 的懒结果多花了几天时间
  2. 因为每次重签 .app 里面的各种 framework,dylib 和 app 略累,所以参考网上的脚本写了一个重签的脚本。放在了 github 上。有兴趣的同学可以上去看看。

参考文章:

重签名:
http://dev.mlsdigital.net/posts/how-to-resign-an-ios-app-from-external-developers/
http://gowithfloat.com/2011/11/re-signing-an-ios-app-without-xcode/
http://www.enterpriseios.com/forum/topic/Resigning_3rd_party_apps
https://segmentfault.com/a/1190000004144556

plist
http://stackoverflow.com/questions/15231592/error-when-trying-to-sandbox-with-codesign-command

全过程:
http://www.jianshu.com/p/189afbe3b429


↙↙↙阅读原文可查看相关链接,并与作者交流