iOS DeveloperiOS 逆向工程 app安全 网络安全iOS 开发每天分享优质文章

iOS逆向基础 —— Reveal越狱+非越狱使用

2021-05-27  本文已影响0人  tomatobin

本文将从越狱设备、非越狱设备两种方式进行介绍Reveal工具的使用。虽然是介绍Reveal,但会涉及到较多逆向相关的技术和工具;道高一尺,魔高一丈,逆向永无止境。

1、越狱设备上使用

越狱设备上使用Reveal查看App的界面,还是比较简单的,只有一个条件:你得有一台能越狱的设备~

1.1 环境准备

支持越狱的设备、OpenSSH、CydiaSubstrate

可直接使用爱思助手,进行一键越狱,按照助手步骤进行即可。需要注意,大部分系统重启后,越狱失效,需要重新越狱。

正常情况下,Cydia首页,有OpenSSH的访问教程,按照说明安装即可。

如果出现OpenSSH找不到的情况,可以先在软件源Tab中添加威锋源,再进入搜索Tab,搜索"OpenSSH"进行安装。

威锋源地址:

http://apt.91.com

正常情况下,越狱后默认安装。如果没有,搜索后安装即可。

1.2 安装Reveal

Cydia源中Reveal版本比较老,无法与Mac端新版本匹配,需要将Mac端Reveal中的iOS库拷贝到越狱设备指定的位置。

将App对应的bundleID写入,如米家com.xiaomi.mihome(可通过爱思助手查看)。

libRevealPlist.png

Mac端通过Help找到RevealServer.framework,将RevealServer.framework/RevealServer和libReveal.plist传至越狱设备,RevealServer在上传时需要将名称重新命名为libReveal.dylib。

reveal-show-in-finder.png

可参考如下scp命令,ssh连接设备后,一键傻瓜式操作(使用了usbmux,将22端口重新定向至2222,方便usb方式连接;中途可能需要输入设备密码,默认为alpine):

#Note:

## 0、Cydia中搜索Reveal Loader2并安装(Reveal Loader安装后会和MonkeyDev冲突)
## 1、Reveal.framework从Mac中安装的应用程序中,如:/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/RevealServer.framework
## 2、修改libReveal.plist中需要hook的App BundleId


#ssh root@127.0.0.1 -p 2222
#拷贝 plist至越狱机
scp -P 2222 libReveal.plist root@127.0.0.1:/Library/MobileSubstrate/DynamicLibraries/

#拷贝 Reveal至越狱机
scp -P 2222 RevealServer.framework/RevealServer  root@127.0.0.1:/Library/MobileSubstrate/DynamicLibraries/libReveal.dylib

1.3 使用Reveal

越狱设备打开App后,Mac上Reveal会有如下显示,点击图标进入即可。

如果没有出现,需要重启越狱设备。


Reveal-use.png

2、非越狱设备

在非越狱设备上使用Reveal,相对会麻烦一些,涉及到的技术(工具)包括:

2.1 IPA获取

有多种方式可获取,这里介绍简单的几种:

2.2 App脱壳

AppStore下载的App,会加一层壳,无法进行调试、重签名后无法正常使用(打开闪退)。可通过otool命令,查看二进制文件中对应的字段cryptid是否为0,来判断应用是否脱壳:

iblue@ibluedeMac-mini Resign % otool -l Payload/MiHome.app/MiHome | grep crypt 
     cryptoff 16384
    cryptsize 121339904
      cryptid 0

提一下几种常用的脱壳方法,具体可自行Google之:

https://github.com/AloneMonkey/dumpdecrypted
可以砸 framework,App启动后,查看控制台framework保存的位置,再拷出来

2.3 iOSOpenDev安装及动态库生成

以前AppStore版本的程序,禁止使用非系统的动态库,主要是为了安全和性能的考虑。但不意味着App不可以使用动态库,只要将动态库加入到程序的bundle中,并使用相同的证书对动态库、app进行签名,就可以正常使用。本文为方便展示逆向基础的知识,采用iOSOpenDev来创建动态库(再次说明: 使用MonkeyDev调试会简单很多)。

下载iOSOpenDev文件夹后,执行以下步骤完成安装:

重新打开Xcode,就可以看到动态库dylib创建界面了,继续新建CommonCrack工程。


iOSOpenDev.png

为了测试,这里Hook住登录界面类MPLoginViewController

1、添加打印 NSLog(@"🍎🍎🍎 %@ did appear...", NSStringFromClass([self class]));

2、填充登录界面用户名和密码

编译生成动态库libCommonCrack.dylib,会自动拷贝至上一层级的Resign目录下,供后续使用。

主要代码参考如下:

//Hook的class
//MPLoginViewController_Hook
CHDeclareClass(MPLoginViewController);

// Hook的函数
// - (void)viewDidAppear:(BOOL)animated
CHMethod(1, void, MPLoginViewController, viewDidAppear, BOOL, animated)
{
    NSLog(@"🍎🍎🍎 %@ did appear...", NSStringFromClass([self class]));
    CHSuper(1, MPLoginViewController, viewDidAppear, animated);

    UITextField *userField = [self valueForKey:@"_userField"];
    UITextField *phoneField = [self valueForKey:@"_phoneField"];
    UITextField *passwordField = [self valueForKey:@"_passwordField"];
    
    NSString *account = [[NSUserDefaults standardUserDefaults] objectForKey:@"Hook_Login_Account"];
    NSString *password = [[NSUserDefaults standardUserDefaults] objectForKey:@"Hook_Login_Password"];
    if (account.length) {
        userField.text = account;
        phoneField.text = account;
        passwordField.text = password;
    } else {
        userField.text = @"189****7580";
        phoneField.text = @"189***7580";
        passwordField.text = @"xxxxx";
    }
}


#pragma clang diagnostic pop
//
//  ReConfigManager.m
//  Exchange counterDylib
//+
//  Created by iblue on 2019/10/15.
//  Copyright © 2019 DH. All rights reserved.
//

#import "ReConfigManager.h"
#import "LCLogManager.h"
#import <dlfcn.h>

@implementation ReConfigManager

+ (void)load {
    NSLog(@"🍎🍎🍎 %@:: %@", NSStringFromClass([self class]), @"Loaded...");
    
    //app日志导出到文件
    [LCLogManager shareInstance].maxLogSize = 10;
    [LCLogManager shareInstance].isCycle = YES;
    [[LCLogManager shareInstance] startFileLog];
}

...

@end

2.4 动态注入

Github地址:https://github.com/KJCracks/yololib,下载后,编译可生成yololib即可。

通过命令行工具使用yololib,将libCommonCrack.dylib注入至App二进制文件中,App在启动后,就会加载libCommonCrack.dylib,执行我们想要的方法。

iblue@ibluedeMac-mini Resign % ./yololib Payload/MiHome.app/MiHome libCommonCrack.dylib

2021-05-26 14:41:51.220 yololib[94245:1688683] dylib path @executable_path/libCommonCrack.dylib
2021-05-26 14:41:51.221 yololib[94245:1688683] dylib path @executable_path/libCommonCrack.dylib
Reading binary: Payload/MiHome.app/MiHome

2021-05-26 14:41:51.221 yololib[94245:1688683] Thin 64bit binary!
2021-05-26 14:41:51.221 yololib[94245:1688683] dylib size wow 64
2021-05-26 14:41:51.221 yololib[94245:1688683] mach.ncmds 100
2021-05-26 14:41:51.221 yololib[94245:1688683] mach.ncmds 101
2021-05-26 14:41:51.221 yololib[94245:1688683] Patching mach_header..
2021-05-26 14:41:51.222 yololib[94245:1688683] Attaching dylib..

2021-05-26 14:41:51.222 yololib[94245:1688683] size 61
2021-05-26 14:41:51.222 yololib[94245:1688683] complete!

注入成功后,使用Mach-O查看MiHome二进制文件 ,可以看到libCommonCrack.dylib已在Load Commands中; 同样,对libReveal.dylib进行相同的操作:


mach-o.png

2.5 重签名

重签名脚本参考附录中的resign_app.sh,对主要的几个步骤进行说明:

1、进入Resign目录,将授权文件拷贝至此,并重命令为embeddedmobileprovision

2、拷贝脱壳后的App至此,如MiHome.app

3、修改resign_app.sh相关参数 :

2.6 验证及使用

签名完成后,将resign.ipa安装后手机中,打开App:

1、通过助手或iTunes打开沙盒目录,看Doucuments目录下是否创建了AppLog/Log/xxxx/log0.txt 沙盒.png

打开log0.txt,就可以看到添加的打印信息 🍎🍎🍎...


Log.png

2、将手机和Mac连同一个局域网,可以看到米家的App;手机如果通过USB连接到Mac,会多出一个USB连接的标识,建议使用USB。

Reveal-use.png reveal-mihome.png

3、案例归档

为方便初学者实操,对非越狱部分的内容进行了归档,https://pan.baidu.com/s/1Is0NT-VNxrpW4leKtRsA4A 密码: g4t3,目录内容包括:

附录

重签名脚本

#!/bin/sh
#说明:需要修改的参数
# 1. APP_NAME,如:APP_NAME=MiHome.app
# 2. KEYCHAIN_ID,即证书对应的SHA256, 如 KEYCHAIN_ID="B69D7658D231BD17F335B67E07BA333685C1F290"
# 3. BUNDLE_IDENTIFIER,授权文件对应的BundleID,如BUNDLE_IDENTIFIER="com.dahuatech.lecheng"
# 4. 授权文件,修改为embedded.mobileprovision后放入目录中,如PROVISION_IOS="${TEMP}/embedded.mobileprovision"


############################################################
#通用函数定义

#打印命令
function echoCommand()
{
    echo "$1"
    $1
}

#打印xcode、编译环境信息
function printXcodeInfo()
{
    xcode-select --version
    xcode-select --print-path
    security find-identity -v -p codesigning
}

# 打印电脑中安装的授权文件
function printProvisionFiles()
{
    ls -l ~/Library/MobileDevice/Provisioning\ Profiles/
}

# Generate entitlements
# 通过Profile文件生成签名用的entitlements.plist文件
#参数1:Profile文件,保存至ENTITLEMENTS_PLIST中
#返回值:plist文件路径
function generateEntitlementPlistFile()
{
    if [[ -z $1 ]]; then
        echo "Error: No profiles input..."
    fi

    provisionvalue=`cat "${1}"`
    parseEntitlement=${provisionvalue#*<key>Entitlements</key>}
    entitlementFromMPP=${parseEntitlement%%</dict>*}
    entitlementFromMPP="${entitlementFromMPP/<string>\*<\/string>/<array><string>applinks:funcshop.imoulife.com</string><string>applinks:dvl.lechange.cn</string><string>applinks:dx.lechange.cn</string><string>applinks:func.lechange.cn</string><string>applinks:u5c.cn</string></array>}"
    entitlementHeader1='<?xml version="1.0" encoding="UTF-8"?>'
    entitlementHeader2='<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
    entitlementHeader3='<plist version="1.0">'
    fullEntitlement=$entitlementHeader1$entitlementHeader2$entitlementHeader3"${entitlementFromMPP}</dict></plist>"
    echo "${fullEntitlement}" > "$(pwd)/entitlements.plist"

    #echo "------------ Entitlements file used --------------"
    #echo "${fullEntitlement}"
    #echo "--------------------------------------------------"

    echo "$(pwd)/entitlements.plist"
}

#对可执行文件进行签名
#参数1:授权文件路径
#参数2:证书KeychainId
#参数3:可执行文件路径
function resignFile()
{
    echo "Resign File: $1, $2, $3"
    entitlementsPlist=`generateEntitlementPlistFile $1`

    #去除旧的签名
    echo "Remove _CodeSignature..."
    rm -rf "$3/_CodeSignature"

    #拷贝描述文件
    echo "Copy provisioning file to ... $3/embedded.mobileprovision"
    cp -rf "$1" "$3/embedded.mobileprovision"

    #目录下有Frameworks文件夹,则需要对所有动态库进行重签名
    if [ -d "$3/Frameworks" ];then
        `codesign -v -f -s $2 $3/Frameworks/*`
    fi

    #对可执行文件进行签名
    `codesign -v -f -s $2 --entitlements ${entitlementsPlist} $3`
}

############################################################
# main loop
echo "[******************** *. List Xcode & codesign info... ********************]"
printXcodeInfo

#echo "[******************** *. List Provisionfiles ... ********************]"
#printProvisionFiles

echo "[******************** 0. Check build path ... ********************]"
#文件夹路径
TEMP=`pwd`
cd "$TEMP"

#将xx.app拷贝到Payload目录下,自动读取App名称
#APP_NAME=$(ls "$TEMP/Payload")

#将xxx.app拷贝到Resign目录下
APP_NAME=MiHome.app
APP_BINARY_NAME=${APP_NAME%.*}
echo "Check Path TEMP:${TEMP}"
echo "AppName: $APP_NAME"

#临时处理,只是保证每次动态注入的二进制是原始的
rm -rf ./Payload/*
cp -rf MiHome.app ./Payload/

#检测二进制文件是否脱壳
echo "[*** Check crypt: otool -l Payload/${APP_NAME}/${APP_BINARY_NAME} | grep crypt... ***] "
APP_CRYPT_INFO=`otool -l Payload/${APP_NAME}/${APP_BINARY_NAME} | grep crypt`
echo $APP_CRYPT_INFO
if [[ $APP_CRYPT_INFO =~ "cryptid 1" ]];then
    echo "[******************** Fatal error, binary is encrypted... ********************]"
    exit
else
    echo "[*** Check crypt succeed... ***] "
fi


echo "[******************** 1. Set resign parameters ... ********************]"

#证书签名变量【p12文件修改后需要更新】
KEYCHAIN_ID="B69D7658D231BD17F335B67E07BA333685C1F290"
BUNDLE_IDENTIFIER="com.dahuatech.lecheng"
PROVISION_IOS="${TEMP}/embedded.mobileprovision"

#libCommonCrack.dylib,注入的动态库,不能加上路径,否则App在启动时执行路径会变成 dylib path @executable_path//Users/
LIB_COMMON_CRACK="libCommonCrack.dylib"
LIB_REVEAL="libReveal.dylib"
#DISPLAY_NAME=""    #eg.xxx

echo "[******************** 2. Resigning for ${APP_NAME} ... ********************]"

#为方便签名,去除watch和插件文件夹
rm -rf $TEMP/Payload/$APP_NAME/Watch
rm -rf $TEMP/Payload/$APP_NAME/PlugIns

#修改BundleID
if [[ $BUNDLE_IDENTIFIER ]]; then
    echo "change bundle ID: ${BUNDLE_IDENTIFIER}"
    `/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier ${BUNDLE_IDENTIFIER}" "$TEMP/Payload/$APP_NAME/Info.plist"`
fi

#修改App名称
if [[ $DISPLAY_NAME ]]; then
    echo "change display name: ${DISPLAY_NAME}"
    `/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName ${DISPLAY_NAME}" "$TEMP/Payload/$APP_NAME/Info.plist"`
fi

#删除UISupportedDevices
`/usr/libexec/PlistBuddy -c "Delete :UISupportedDevices" "$TEMP/Payload/$APP_NAME/Info.plist"`

#设置为可以通过iTunes进行共享
`/usr/libexec/PlistBuddy -c "Delete :UIFileSharingEnabled" "$TEMP/Payload/$APP_NAME/Info.plist"`
`/usr/libexec/PlistBuddy -c "Add :UIFileSharingEnabled bool 1" "$TEMP/Payload/$APP_NAME/Info.plist"`

#注入动态库
echo "yololib dynamic framework/lib: $LIB_COMMON_CRACK"
./yololib "$TEMP/Payload/${APP_NAME}/${APP_BINARY_NAME}" $LIB_COMMON_CRACK
./yololib "$TEMP/Payload/${APP_NAME}/${APP_BINARY_NAME}" $LIB_REVEAL

#copy 动态库:将需要加载的动态库,拷贝到App主目录下
echo "copy dynamic framework/lib"
cp -rf ./$LIB_COMMON_CRACK "${TEMP}/Payload/${APP_NAME}"
cp -rf ./$LIB_REVEAL "${TEMP}/Payload/${APP_NAME}"

# Resign file
resignFile "${PROVISION_IOS}" "${KEYCHAIN_ID}" "$TEMP/Payload/${APP_NAME}/$LIB_COMMON_CRACK"
resignFile "${PROVISION_IOS}" "${KEYCHAIN_ID}" "$TEMP/Payload/${APP_NAME}/$LIB_REVEAL"
resignFile "${PROVISION_IOS}" "${KEYCHAIN_ID}" "$TEMP/Payload/${APP_NAME}"

echo "==============================================="
echo "Resign result"
codesign -dvvv $TEMP/Payload/${APP_NAME}

#清理临时文件
rm -rf entitlements.plist

# Zip file generate new ipa file
echo "zip file generate new ipa file"
rm -rf resign.ipa
echoCommand "zip -qr resign.ipa Payload "


echo "[******************** End resigning ... ********************]"
上一篇下一篇

猜你喜欢

热点阅读