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

陈恒捷 · 2016年04月05日 · 最后由 陈恒捷 回复于 2017年05月03日 · 13722 次阅读

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

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

前言

之前通过对 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 的原因!

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

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

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

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

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

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

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

15楼 已删除

#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 怎么抓取到的 啊

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

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

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

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

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

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

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

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

零飞飞 回复

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

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

hanyinbo 回复

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

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

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册