移动安全测试 [已成功] 给微信加 hook 尝试记录

陈恒捷 · April 05, 2016 · Last by 陈恒捷 replied at May 03, 2017 · 25693 hits

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

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

前言

之前通过对 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 3GS
  • iPod touch(第三代)
  • iPad
  • iPhone 4
  • iPod touch(第四代)
  • iPad 2
  • iPhone 4S
  • new iPad (第三代)
  • iPod touch (第五代)
  • iPad mini

手上虽然有部 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

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 22 条回复 时间 点赞

微信应该是有自身 hash 值检验 你得 hook 掉才行

—— 来自 TesterHome 官方 安卓客户端

#1 楼 @seveniruby 应该不是。我直接把越狱市场的微信直接重签名也不行。不重签的话是可以正常安装打开的。所以我觉得有可能是我用的是免费个人开发者证书,没有推送这些授权引起的。

#1 楼 @seveniruby 解决了。。。和证书无关,是我偷懒没生成 ipa 后再装,导致少了一些文件。

这种闪退应该和第三方应用市场上装的应用闪退比较类似。

#3 楼 @chenhengjie123 请问 bundle id 怎么换呢?我用了你的签名工具还是安装不了,然后自己手动签名,倒是能安装,但还是闪退,我感觉是不是没换 bundleid 的原因!

陈恒捷 #5 · May 03, 2016 Author

#4 楼 @Tairy bundle id 在 Entitlement.plist 里面换。

你安装的时候报什么错?签名校验失败?

没换 bundle id 你是获取不到 embedded.mobileprovision 这个文件的。 Xcode 会提示你这个 bundle id 不可用。

#5 楼 @chenhengjie123 是的 我是签名校验失败, 我的 embedded.mobileprovision 文件我是直接在苹果开发者中心下载的,难道我这里弄错了么!

#5 楼 @chenhengjie123 我把微信原来的包没做任何改动重新签名,然后安装之后闪退。

陈恒捷 #8 · May 03, 2016 Author

#7 楼 @Tairy 你用 idevicesyslog 看看闪退时有什么日志?

你也可以试试用一些修复闪退工具看能不能修复。如果是已经砸壳的应用应该不会闪退。

9Floor has deleted

#8 楼 @chenhengjie123

May  3 17:54:36 XXXX SpringBoard[6515] <Error>:  SecTrustEvaluate  [leaf IssuerCommonName SubjectCommonName]
May  3 17:54:36 XXXX SpringBoard[6515] <Error>:  SecTrustEvaluate  [leaf IssuerCommonName SubjectCommonName]
May  3 17:54:36 XXXX securityd[93] <Error>:  secTaskDiagnoseEntitlements MISSING keychain entitlements: no stored taskRef found
May  3 17:54:36 XXXX securityd[93] <Error>:  secTaskDiagnoseEntitlements MISSING keychain entitlements: no stored taskRef found
May  3 17:54:36 XXXX amfid[8943] <Error>:  SecTrustEvaluate  [leaf IssuerCommonName SubjectCommonName]
May  3 17:54:36 XXXX kernel[0] <Notice>: AppleFairplayTextCrypterSession::fairplayOpen() failed, error -42022
May  3 17:54:36 XXXX com.apple.xpc.launchd[1] (UIKitApplication:com.tencent.xin[0x3e7c][9297]) <Notice>: Service exited due to signal: Killed: 9
May  3 17:54:36 XXXX assertiond[67] <Warning>: Unable to obtain a task name port right for pid 9297: (os/kern) failure (5)
May  3 17:54:36 XXXX SpringBoard[6515] <Warning>: Unable to register for exec notifications: No such process
May  3 17:54:36 XXXX SpringBoard[6515] <Warning>: Unable to obtain a task name port right for pid 9297: (os/kern) failure (5)
May  3 17:54:36 XXXX SpringBoard[6515] <Warning>: Unable to obtain a task name port right for <FBApplicationProcess: 0x13408b5a0; com.tencent.xin; pid: 9297>
May  3 17:54:36 XXXX SpringBoard[6515] <Warning>: Application 'UIKitApplication:com.tencent.xin[0x3e7c]' exited abnormally via signal.
May  3 17:54:37 XXXX SpringBoard[6515] <Warning>: Application '(null)' exited for an unknown reason.

打出来日志大概是上面这个样子

<Error>:  SecTrustEvaluate  [leaf IssuerCommonName SubjectCommonName]

#8 楼 @chenhengjie123 嗯 搞定了 我现在把直接砸壳的微信打包之后可以安装了,之前闪退可能是我注入的代码有问题。

http://bbs.iosre.com/t/app-log/4161 安装闪退的 log 打印是啥工具抓取到的?谢谢

#10 楼 @Tairy 这种`log 怎么抓取到的 啊

陈恒捷 #14 · July 05, 2016 Author

#13 楼 @wg689 看 8 楼。。。这是 iOS 系统 log ,你可以理解为等价于 android 的 logcat

#14 楼 @chenhengjie123 系统日志是用 xcode 看还是 LLDB 还是啥第三方工具啊 google 了下没发现合适的答案

#13 楼 @wg689 我的微信重签名崩溃,拉钩 app 重签名就 OK 的,估计我只签了一个地方,我也很想看到你们这种日志,看看微信为啥崩溃了

陈恒捷 #17 · July 05, 2016 Author

#15 楼 @wg689 xcode 可以,一些第三方应用市场也可以。

idevicesyslog 是 libimobiledevice 里面的一个组件,可以通过命令行工具直接调起

你好 “解决方案很简单:最后不要直接安装 .app ,而是使用下面的命令生成 ipa 后安装 ipa 文件:” 最后你是怎么安装 ipa 的 我用 PP 助手,itunes, mobiledevice 都安不上。想请教一下

#18 楼 @ziacke 装不上报啥错?

请问下 WeChatWatchNative.app 是怎么砸壳的?按照 dumpdecrypted 的原理应该是要连接 apple watch 打开微信的相关功能才能砸壳的?

陈恒捷 #21 · March 20, 2017 Author
对 零飞飞 回复

可以直接去越狱市场下载试试。我只是砸壳了微信的手机版 app ,apple watch 的没改。

hi,读了你的这篇文章,收获不小。现在我想重签名一个 ipa,先用自己随意建的工程的 embedded.mobileprovision 替换了 embedded.mobileprovision, 然后构建了 Entitlement.plist 把里面的开发者 id 都替换成我的并重签名,然后重新打包。可是安装的时候出来 entitlements fonud that are not permitted by provisioning file. 请问这个问题你有遇到过吗?还是我哪里理解的不对😥

陈恒捷 #23 · May 03, 2017 Author
对 hanyinbo 回复

你的 embedded.mobileprovision 对应工程的 bundle id 有和你想重签名的 bundle id 一致不?

mobileprovision 中绑定了开发者证书 id 、应用 id 以及 Entitlement 等信息,主要用来检测这几者是否对的上号。如果对不上号,安装时系统会自动检测到。

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up。