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

  • 本文涉及的资源下载地址: https://pan.baidu.com/s/1Is0NT-VNxrpW4leKtRsA4A 密码: g4t3
  • 快速体验:下载后,进入Resign目录,修改resign_app.sh中相关参数,替换授权文件,即可体验丝滑的逆向过程(以米家App为例)

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

1、越狱设备上使用

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

1.1 环境准备

支持越狱的设备、OpenSSH、CydiaSubstrate

  • 越狱

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

  • 安装OpenSSH

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

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

威锋源地址:

http://apt.91.com

  • CydiaSubstrate

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

1.2 安装Reveal

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

  • 新建libReveal.plist

将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 [email protected] -p 2222
#拷贝 plist至越狱机
scp -P 2222 libReveal.plist [email protected]:/Library/MobileSubstrate/DynamicLibraries/

#拷贝 Reveal至越狱机
scp -P 2222 RevealServer.framework/RevealServer  [email protected]:/Library/MobileSubstrate/DynamicLibraries/libReveal.dylib

1.3 使用Reveal

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

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


Reveal-use.png

2、非越狱设备

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

  • IPA获取

  • App脱壳

  • iOSOpenDev创建动态库(或MonkeyDev直接调试,会简单很多)

  • 动态库注入

  • 应用重签名

2.1 IPA获取

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

  • 可通过Apple Configuration 2获取

  • 爱思助手中,直接搜索下载即可

  • PP论坛可下载脱壳后的App,已不在维护了,有兴趣的可以看看(自从阿里收购了PP就没有然后了~):https://www.jianshu.com/p/c789f52b1cdf

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之:

  • 大杀器:网上搜索脱壳后的App,直接下载即可
  • 越狱设备,使用工具clutch,导出ipa
  • 越狱设备,使用工具dumpdecrypted.dylib,导出二进制文件、动态库
https://github.com/AloneMonkey/dumpdecrypted
可以砸 framework,App启动后,查看控制台framework保存的位置,再拷出来
  • 越狱设备,使用frida,导出ipa

2.3 iOSOpenDev安装及动态库生成

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

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

  • 运行iOSOpenDev-1.6-2_0.pkg
  • 执行install.sh脚本(脚本内注释,说明了干了什么)

重新打开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 

@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相关参数 :

  • APP_NAME,如:APP_NAME=MiHome.app
  • KEYCHAIN_ID,即对应的SHA-1, 如 KEYCHAIN_ID="B69D7658D231BD17F335B67E07BA333685C1F290"
  • BUNDLE_IDENTIFIER,授权文件对应的BundleID

2.6 验证及使用

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

  • 如果看到手机号已填充,说明代码生效;
  • 如果App闪退,或者手机号为空,往往是动态注入或签名有错(闪退一般是由于使用了未脱壳的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,目录内容包括:

  • CommonCrack:生成libCommonCrack动态库工程,生成后,自动将libCommonCrack.dylib拷贝至Resign目录下

  • MachOExplorer:Mach-O文件查看器

  • Resign:重签名、打包相关,直接运行resign_app.sh即可(需要替换成自己的授权文件 、KeychainId、BundleId)

  • Reverse_Tools:逆向相关的工具包


    example.png

附录

重签名脚本

#!/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#*Entitlements}
    entitlementFromMPP=${parseEntitlement%%*}
    entitlementFromMPP="${entitlementFromMPP/\*<\/string>/applinks:funcshop.imoulife.comapplinks:dvl.lechange.cnapplinks:dx.lechange.cnapplinks:func.lechange.cnapplinks:u5c.cn}"
    entitlementHeader1=''
    entitlementHeader2=''
    entitlementHeader3=''
    fullEntitlement=$entitlementHeader1$entitlementHeader2$entitlementHeader3"${entitlementFromMPP}"
    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 ... ********************]"

你可能感兴趣的:(iOS逆向基础 —— Reveal越狱+非越狱使用)