1. 程式人生 > >(轉載) Objective-C程式碼混淆(轉載自念茜)

(轉載) Objective-C程式碼混淆(轉載自念茜)

class-dump可以很方便的匯出程式標頭檔案,不僅讓攻擊者瞭解了程式結構方便逆向,還讓著急趕進度時寫出的欠完善的程式給同行留下笑柄。

所以,我們迫切的希望混淆自己的程式碼。

混淆的常規思路

混淆分許多思路,比如:

1)花程式碼花指令,即隨意往程式中加入迷惑人的程式碼指令

2)易讀字元替換

等等

防止class-dump出可讀資訊的有效辦法是易讀字元替換。

Objective-C的方法名混淆

混淆的時機

我們希望在開發時一直保留清晰可讀的程式程式碼,方便自己。

同時,希望編譯出來的二進位制包含亂七八糟的混淆後的程式程式碼,噁心他人。

因此,我們可以在Build Phrase 中設定在編譯之前進行方法名的字串替換。

混淆的方法

方法名混淆其實就是字串替換,有2個方法可以,一個是#define,一個是利用tops。
利用#define的方法有一個好處,就是可以把混淆結果合併在一個.h中,在工程Prefix.pch的最前面#import這個.h。不匯入也可以編譯、匯入則實現混淆。
單段的selector,如func: ,可以通過#define func 來實現字串替換。
多段的selector,如a:b:c: ,可以通過分別#define a 、b、c 來實現字串替換。

我的混淆工具

我寫了個簡易的混淆指令碼,主要思路是把敏感方法名集中寫在一個名叫func.list的檔案中,逐一#define成隨機字元,追加寫入.h。

指令碼如下:

#!/usr/bin/env bash
TABLENAME=symbols
SYMBOL_DB_FILE="symbols"
STRING_SYMBOL_FILE="func.list"
HEAD_FILE="$PROJECT_DIR/$PROJECT_NAME/codeObfuscation.h"
export LC_CTYPE=C
 
#維護資料庫方便日後作排重
createTable()
{
    echo "create table $TABLENAME(src text, des text);" | sqlite3 $SYMBOL_DB_FILE
}
 
insertValue()
{
    echo "insert into $TABLENAME values('$1' ,'$2');" | sqlite3 $SYMBOL_DB_FILE
}
 
query()
{
    echo "select * from $TABLENAME where src='$1';" | sqlite3 $SYMBOL_DB_FILE
}
 
ramdomString()
{
    openssl rand -base64 64 | tr -cd 'a-zA-Z' |head -c 16
}
 
rm -f $SYMBOL_DB_FILE
rm -f $HEAD_FILE
createTable
 
touch $HEAD_FILE
echo '#ifndef Demo_codeObfuscation_h
#define Demo_codeObfuscation_h' >> $HEAD_FILE
echo "//confuse string at `date`" >> $HEAD_FILE
cat "$STRING_SYMBOL_FILE" | while read -ra line; do
    if [[ ! -z "$line" ]]; then
        ramdom=`ramdomString`
        echo $line $ramdom
        insertValue $line $ramdom
        echo "#define $line $ramdom" >> $HEAD_FILE
    fi
done
echo "#endif" >> $HEAD_FILE
 
 
sqlite3 $SYMBOL_DB_FILE .dump

操作步驟
1.將混淆指令碼confuse.sh放到工程目錄下 
mv confuse.sh your_proj_path/
2.修改Prefix.pch
開啟Xcode,修改XXX-Prefix.ch ,新增混淆標頭檔案:

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    //新增混淆作用的標頭檔案(這個檔名是指令碼confuse.sh中定義的)
    #import "codeObfuscation.h"
#endif
3.配置Build Phase
在工程Build Phase中新增執行指令碼操作,執行confuse.sh指令碼,如圖:

4.建立函式名列表func.list,寫入待混淆的函式名,如:
-(void)sample;
-(void)seg1:(NSString *)string seg2:(NSUInteger)num;
就這樣寫:

sample
seg1
seg2


並將檔案放置於與confuse.sh指令碼同級
mv func.list your_proj_path/

5.編譯檢視結果
直接build,混淆指令碼會在編譯前執行,進行字元隨機替換,並且每次build的隨機字元不同,如圖:

實際驗證了下  可用。但是要注意兩點

#取以.m或.h結尾的檔案以+號或-號開頭的行 |去掉所有+號或-號|用空格代替符號|n個空格跟著<號 替換成 <號|開頭不能是IBAction|用空格split字串取第二部分|排序|去重複|刪除空行|刪掉以init開頭的行>寫進func.list
grep -h -r -I  "^[-+]" $CONFUSE_FILE  --include '*.[mh]' |sed "s/[+-]//g"|sed "s/[();,: *\^\/\{]/ /g"|sed "s/[ ]*</</"| sed "/^[ ]*IBAction/d"|awk '{split($0,b," "); print b[2]; }'| sort|uniq |sed "/^$/d"|sed -n "/^hsk_/p" >$STRING_SYMBOL_FILE

最終我的confuse.sh是這樣

#!/usr/bin/env bash
TABLENAME=symbols
SYMBOL_DB_FILE="symbols"
STRING_SYMBOL_FILE="func.list"
TARGET_PATH="$PROJECT_DIR/ios"

HEAD_FILE="$TARGET_PATH/codeObfuscation.h"

echo $HEAD_FILE
export LC_CTYPE=C

#維護資料庫方便日後作排重
createTable()
{
    echo "create table $TABLENAME(src text, des text);" | sqlite3 $SYMBOL_DB_FILE
}

insertValue()
{
    echo "insert into $TABLENAME values('$1' ,'$2');" | sqlite3 $SYMBOL_DB_FILE
}

query()
{
    echo "select * from $TABLENAME where src='$1';" | sqlite3 $SYMBOL_DB_FILE
}

ramdomString()
{
    openssl rand -base64 64 | tr -cd 'a-zA-Z' |head -c 16
}

writeToFile()
{
    echo $1 >> $HEAD_FILE;
}

rm -f $SYMBOL_DB_FILE
rm -f $HEAD_FILE
createTable

touch $HEAD_FILE
touch $CONFIG_FILE
writeToFile '#ifndef Demo_codeObfuscation_h'
writeToFile '#define Demo_codeObfuscation_h'

writeToFile "//confuse string at `date`"
#echo "//confuse string at `date`" >> $CONFIG_FILE

writeToFile "#define CONFUSE_FUNC_MAP_START  "
writeToFile "#define CONFUSE_FUNC_MAP_END  "

writeToFile "CONFUSE_FUNC_MAP_START  "

cat "$TARGET_PATH/$STRING_SYMBOL_FILE" | while read -ra line; do
if [[ ! -z "$line" ]]; then
ramdom=`ramdomString`
echo $line $ramdom
insertValue $line $ramdom
writeToFile "#define $line $ramdom"
fi
done

writeToFile "CONFUSE_FUNC_MAP_END  "
writeToFile "#endif"

sqlite3 $SYMBOL_DB_FILE .dump

跟原始的區別在於:     需要注意如果你配置在xcode裡編譯報錯了,他是不能繼續執行shell的

因為每一次執行func.list 生成.h檔案,再進行編譯連結生成的話,生成太慢了,所以我只是直接手動生成func.list一次,執行confuse.sh,生成了.h檔案新增進去就ok了,並不配置到xcode的build phase裡面,因為那樣每一次都會重新生成,慢的hei skr。

還有兩個注意的。

一個是連結的外部庫檔案,不能將庫檔案的.h匯出。比如百度sdk 或者微信sdk的.h檔案裡面的函式,你抽取出來混淆了,就會導致執行時crash,除非你能拿到庫檔案原始碼把混淆的codeObfucation.h編譯進去. 不然庫檔案裡函式呼叫還是用的原來的函式,就會崩啊.

還有一個,如果用到了lua呼叫,要進行函式名替換. 我是直接改的 LuaObjcBridge, 初始化的時候直接讀取codeObfucation.h檔案 維護一個map, 在LuaObjcBridge 中NSSelectorFromString 前進行替換.