iOS使用shell腳本注入混淆內(nèi)容

背景

公司需要做一系列的殼版本,殼版本如果內(nèi)容雷同提交到App Store會有被拒絕的風(fēng)險,其中有一種解決方案是在殼版本中注入混淆的代碼,防止被蘋果檢測到內(nèi)容太過雷同而導(dǎo)致審核被拒絕,本文是針對這個場景,使用shell腳本進行半自動批量添加和刪除混淆代碼。

結(jié)果

下面以兩張圖作為添加注入內(nèi)容和刪除注入內(nèi)容的演示:

添加注入內(nèi)容到源碼中

添加注入內(nèi)容到源碼中

把注入內(nèi)容從源碼中刪除

把注入內(nèi)容從源碼中刪除

本文的Demo代碼YTTInjectedContentKit

分析

在開始做之前,對步驟流程做了一些構(gòu)思如下:

初始步驟流程

  • 步驟一:手動處理
    混淆注入類列表
    混淆注入類對應(yīng)的方法列表

  • 步驟二:配置化
    步驟一的形成配置化

  • 步驟三:自動化
    掃描對應(yīng)的類和類對應(yīng)的方法列表,形成對應(yīng)的配置文件
    從配置文件中讀取配置注入到對應(yīng)的目標(biāo)類中

  • 步驟四:自動目標(biāo)文件的處理
    目標(biāo)文件的查找規(guī)則:哪些是需要注入的目標(biāo)文件
    目標(biāo)文件的注入規(guī)則:目標(biāo)文件中需要在什么位置進行注入

  • 步驟五:注入內(nèi)容自身配置
    注入內(nèi)容需要在不同環(huán)境下變換不同的形態(tài),包括類名,方法名等

后面在實現(xiàn)的過程中發(fā)現(xiàn)步驟三和步驟五不好實現(xiàn),所有簡化了這部分的流程,最終只保留了以下幾個步驟:

優(yōu)化的步驟流程

  • 步驟一:手動處理
    混淆注入類列表
    混淆注入類對應(yīng)的方法列表

  • 步驟二:配置化
    步驟一的形成配置化

  • 步驟三:自動目標(biāo)文件的處理
    目標(biāo)文件的查找規(guī)則:哪些是需要注入的目標(biāo)文件
    目標(biāo)文件的注入規(guī)則:目標(biāo)文件中需要在什么位置進行注入

以及在實現(xiàn)過程中遇到了一些細節(jié)需要處理,這些細節(jié)部分作為自步驟如下:

  • 子步驟:
    步驟三-1:檢查時候安裝gun-sed,mac下的sed和gun sed 會有差別,所有統(tǒng)一使用gun sed
    步驟三-2:用戶指定目標(biāo)位置,需要用戶輸入
    步驟三-3:刪除所有的注入內(nèi)容

實現(xiàn)

步驟一:手動處理

整理一份注入的內(nèi)容這部分工作是需要手動處理的,這部分的內(nèi)容應(yīng)該是具備自完備性的,可以被多個項目做為依賴導(dǎo)入,所以把這些內(nèi)容作為一個pod庫比較合適,也很方便在pod庫的測試項目中測試該庫的自完備性。庫里面的內(nèi)容可以是任意的,在我實踐的過程中,我是把一個舊的項目的網(wǎng)絡(luò)接口模塊作為了這部分內(nèi)容,因為這部分內(nèi)容相對的比較獨立和容易抽取。

下圖是我從舊的項目中提取的一些類作為混淆類

目錄結(jié)構(gòu)

以及我在測試工程中測試混淆的接口調(diào)用,在測試工程中測試混淆代碼的調(diào)用以確保編譯鏈接無誤以及確保庫的自完備性。

#import "ICKViewController.h"
#import <InjectedContentKit.h>

@implementation ICKViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    

    [[GameDetailDataComposer new] loadDataWithBlock:nil];
    
    [[PubSearchDataComposer new] loadSuggestionWithCompletionBlock:nil];
    
    [[WriterDataComposer new] loadWithType:MMLoadTypeMore completionBlock:nil];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

步驟二:配置化

配置文件其實就是把測試工程總的接口調(diào)用代碼拷貝一份到單獨的配置文件中,配置文件如下

    [[GameDetailDataComposer new] loadDataWithBlock:nil];
    
    [[PubSearchDataComposer new] loadSuggestionWithCompletionBlock:nil];
    
    [[WriterDataComposer new] loadWithType:MMLoadTypeMore completionBlock:nil];

步驟三:自動目標(biāo)文件的處理

這個是最核心的部分,主要包含了以下內(nèi)容:

  • 配置文件路徑配置和需要注入的源碼文件夾的配置
  • 讀取配置文件的注入內(nèi)容
  • 讀取源碼文件夾下的源碼實現(xiàn)文件(XXX.m)
  • 把注入內(nèi)容添加到源碼中指定的位置
  • 從源碼從把注入的內(nèi)容刪除

完整的腳本如下,里面有比較完整的注釋,閱讀起來應(yīng)該不會有太大難度:

文本的插入和刪除部分使用的是shell中的sed(stream editor)工具,特別滴在mac中sed命令和標(biāo)準(zhǔn)的sed命令有差別,腳本中也會有做這部分的檢測,如果機器上安裝的不是標(biāo)準(zhǔn)的gun sed程序會自動通過brew安裝gun sed

#!/bin/bash

############## 配置

# 需處理文件目錄
# mark: TODO
to_process_file_dir="$(pwd)/../injectedContentKit/Business000"
# 配置文件
cfg_file="$(pwd)/injectedContentConfig.cfg"

############## 工具類方法
function printHighlightMessage {
    echo -e "\033[31m $1 \033[0m"
}


# 檢查是否安裝gunsed
# mac安裝gunSed  http://blog.csdn.net/sun_wangdong/article/details/71078083
which_sed=`which sed`
echo $which_sed
echo "testresult = $(expr $which_sed : '.*/gnu-sed/')"
if [[ $(expr $which_sed : '.*/gnu-sed/') -gt 0 ]]; then
    echo "檢測到使用gun sed"
else
    if [ ! `which brew` ]
    then
        echo 'Homebrew not found. Trying to install...'
                    ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" \
            || exit 1
    fi
    echo 'Trying to install gun sed...'
    brew install gnu-sed --with-default-names || exit 1
    # 設(shè)置局部環(huán)境變量
    echo "set PATH...."
    source ./set-gun-sed-path.sh
    echo "set PATH done"

    #mark: echo 顏色選項 http://www.jb51.net/article/43968.htm
    echo "請手動執(zhí)行命令,然后重新執(zhí)行"
    command="PATH=\"/usr/local/Cellar/gnu-sed/4.4/bin:\$PATH\""
    printHighlightMessage $command
    echo ""
    exit 1
fi


# 循環(huán)檢測輸入的文件夾
function checkInputDestDir {
    echo -n "請輸入需處理源碼目錄: "
    read path
    if [[ -d $path ]]; then
        to_process_file_dir=$path
    else
        echo -n "輸入的目錄無效,"
        checkInputDestDir
    fi
}

# 需處理源碼目錄檢查
if [[ -d $to_process_file_dir ]]; then
    echo "需處理源碼目錄存在 $to_process_file_dir"
else
    echo "請確認(rèn)需處理源碼目錄是否存在 $to_process_file_dir"
    checkInputDestDir
fi

# mark: p261
# 配置文件檢查
if [[ -f $cfg_file ]]; then
    echo "檢測到配置文件存在 $cfg_file"
else
    echo "請確認(rèn)配置文件是否存在 $cfg_file"
    exit 1
fi

# 讀取配置文件
echo "開始讀取配置文件..."

declare -a config_content_array
cfg_line_count=0
# mark: p291
IFS_OLD=$IFS
IFS=$'\n'
# 刪除文件行首的空白字符 http://www.jb51.net/article/57972.htm
for line in $(cat $cfg_file | sed 's/^[ \t]*//g')
do
    if [[ ${#line} -eq 0 ]]; then
        echo "blank line"
    else
        config_content_array[$cfg_line_count]=$line
    fi
    cfg_line_count=$[ $cfg_line_count + 1 ]
done
IFS=${IFS_OLD}


echo ""

# 讀取需要處理目標(biāo)文件
declare -a implement_source_file_array
implement_source_file_count=0

# mark: p384
# 遞歸函數(shù)讀取目錄下的所有.m文件
function read_implement_file_recursively {
    echo "read_implement_file_recursively"
    if [[ -d $1 ]]; then
        for item in $(ls $1); do
            itemPath="$1/${item}"
            if [[ -d $itemPath ]]; then
                # 目錄
                echo "處理目錄 ${itemPath}"
                read_implement_file_recursively $itemPath
                echo "處理目錄結(jié)束====="
            else 
                # 文件
                echo "處理文件 ${itemPath}"
                if [[ $(expr $item : '.*\.m') -gt 0 ]]; then
                    echo ">>>>>>>>>>>>mmmmmmm"
                    implement_source_file_array[$implement_source_file_count]=${itemPath}
                    implement_source_file_count=$[ implement_source_file_count + 1 ];
                fi
                echo ""
            fi
        done
    else
        echo "err:不是一個目錄"
    fi
}


echo ${to_process_file_dir}
read_implement_file_recursively ${to_process_file_dir}


# 處理目標(biāo)文件,添加配置文件中注入的內(nèi)容
function addInjectedContent {
    # implement_source_file_array
    # ${#config_content_array[@]}
    injected_content_index=0
    for(( i=0;i<${#implement_source_file_array[@]};i++)) 
    do 
        file=${implement_source_file_array[i]}; 
        echo ${file}
        injected_content=${config_content_array[$injected_content_index]};
        injected_content_index=$[ $injected_content_index + 1 ]

        echo ">>>>>>>${injected_content}"
        # mark: sed 命令中使用變量 http://blog.csdn.net/lepton126/article/details/36374933
        sed -i '/^- \(.*\)/{
            a\ '"$injected_content"'
        }' ${file}

    done;
    message="內(nèi)容添加完成"
    printHighlightMessage $message
}


# 處理目標(biāo)文件,刪除配置文件中注入的內(nèi)容
function removeInjectedContent {
    for(( i=0;i<${#implement_source_file_array[@]};i++)) 
    do 
        file=${implement_source_file_array[i]}; 
        echo ${file}

        for(( j=0;j<${#config_content_array[@]};j++)) 
        do 
            pattern_str=${config_content_array[$j]};
            echo ">>>>>>>${pattern_str}"

            # mark: sed 命令中使用變量 http://blog.csdn.net/lepton126/article/details/36374933
            substring="["
            replacement="\["
            pattern_str=${pattern_str//$substring/$replacement}
            substring="]"
            replacement="\]"
            pattern_str=${pattern_str//$substring/$replacement}
            echo "pattern_str = $pattern_str"
            #pattern_str="[CardDataComposer new]"

            sed -i '/'"$pattern_str"'/ {
                d
            }' ${file}
        done
    done;
    message="內(nèi)容刪除完成"
    printHighlightMessage $message
}


function genMunu {
    clear
    echo
    echo -e "\t\t\t選項菜單\n"
    echo -e "\t1. 刪除注入內(nèi)容"
    echo -e "\t2. 添加注入內(nèi)容"
    echo -e "\t0. Exit menu\n\n"
    echo -en "\t\tEnter option: "
    read -n 1 option
}


while [[ 1 ]]; do
    genMunu
    case $option in
    0 )
        echo ""
        echo "Bye"
        exit 0
    ;;
    1 )
        # 刪除配置文件中注入的內(nèi)容
        removeInjectedContent
    ;;
    2 )
        # 添加配置文件中注入的內(nèi)容
        addInjectedContent
    ;;
    h )
        genMunu
    ;;
    * )
        echo "Wrong!!"
    ;;
    esac

    echo
    echo -en "\n\n\tHit any key to continue"
    read -n 1 line

done

總結(jié)

以上就是基于shell腳本,從混淆內(nèi)容注入和把混淆內(nèi)容刪除兩方面做了一個半自動化的實現(xiàn)步驟,如果不妥之處,還請不吝賜教。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,063評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,275評論 6 342
  • 若真的有,這個人注定是幸福的一生。 這是我最近才想出來的主題,原先想要寫的是假設(shè)人生只有50%的電池,后來轉(zhuǎn)念一想...
    海豚的世界_0820閱讀 348評論 0 0
  • 朝陽和夕陽似的,天空的色彩比較多,是烏云遮不住呢還是陰霾來襲?我選擇前者。 晚上的月亮又大又圓。 惦記起某人的心情...
    lonely_tree閱讀 182評論 0 0

友情鏈接更多精彩內(nèi)容