iOSはshellスクリプトで一括的にプロパティを変更します。
背景
会社は一連のシェルバージョンを作る必要があります。もしシェルバージョンの内容が同じなら、App Storeに提出すると拒否されるリスクがあります。前の記事で述べたシェルバージョンに混同コードを注入して、アップルから内容があまりにも似ていることが検出され、審査が拒否されることを防止します。もう一つの可能な方法は、ソースファイルのクラス名、属性、方法名などを大量に修正して、バイナリファイルにマークの情報を残して、アップルのマシンを回って審査することです。
この記事では、スクリプトを使って属性名を一括的に変更する方法を紹介しています。その後、スクリプトを使ってクラス名や方法名などの情報を大量に修正する文章もあります。
結果
以下はスクリプトを実行して属性を置換した結果図です。スクリプトはすべての代替属性をabc拡張子に追加しました。もちろん、通常のコンパイルで実行できます。
ソースコード:https://gitee.com/dhar/YTTInjectedContentKit
分析
原理分析
objcコードのクラス名、属性、方法、ソースファイルのパスなどの情報は最終的にバイナリファイルにパッケージされ、バイナリファイルに保存されます。sym記号のセグメントでは、Objdump-tコマンドを使用してバイナリ記号情報を確認します。以下のコマンドは、Objdump-tの結果をファイルInjectContht Kitに書き込みます。Example_Symborsに行きます。
また、アップルはコードの流れによっては判断されないはずです。バイナリのコントロールプロセスはマシンコードになっています。逆コンパイルというのはアセンブリコードです。少しでもバイナリを変えると大きく変わります。この点から判断するのは難しいです。
ステップ分析
主に以下のステップがあります。は、代替が必要なソースファイルのすべての属性を見つけ、処理後、プロファイルに保存する 。ユーザがブラックリスト設定ファイルをカスタマイズする ある部分から隔離が必要なコードの中の属性は、ブラックリスト構成ファイル を生成する。は、代替が必要なソースファイルのすべてのマッチング属性を一括的に置換する。
ここではなぜ第一歩はプロファイルに保存する必要があるのかを説明します。第三段階の操作は部分的には第一歩と同じです。この部分は単独で一つのモジュールで共有して、一つのフォルダを入力して、最終的に指定されたファイルに保存します。後のコードにこの部分が見られます。
実現する
シングルステップ実現
1、代替が必要なソースファイルのすべての属性を探して、処理後に設定ファイルに保存します。
このステップの機能は、クライアントが処理するソースフォルダを入力し、ソースフォルダを経由してすべてのソースファイル(.h.mファイル)を取得します。正規のマッチングを使って属性名を見つけて、しばらく配列に保存して、最後にブラックリストのフィルタリング、フィルタリング、その他のフィルタリング条件を経て、最終的に処理対象の属性をクライアント入力の出力ファイルに保存します。
いくつかのステップに分解できます。再帰的にフォルダを巡回してソースファイルを取得します。 正則はソースファイルの属性にマッチしています。 フィルタ属性(オプション) ファイルに属性を保存します。
この部分の機能のソースコードは以下の通りです。
ファイル名:GetAndStreProperties.sh
スクリプトは複数の場所で有用なので、別のモジュールとしていくつかのパラメータを定義して、異なるアプリケーションシーンに適応します。下に台本を使っているところが見えます。
実際のプロセスでは、属性を置き換える記号は、システムクラスの属性を置き換える場合があります。 AppDelegateのwindow属性を交換してコンパイルリンクを招いたのは間違いないですが、インタフェースが出られなくなりました。最初のwindowオブジェクトが見つからないからです。 。 UButtonのtitleLabel属性を置換しました。直接コンパイルエラーを引き起こしました。
このような問題に対しては、ブラックリストにデフォルトのフィルタ属性を配置する必要があります。ブラックリストのこれらの属性は処理しなくてもいいです。私のビジネスシーンでは、ブラックリストファイルの構成は以下の通りです。
ファイル名:Default BlackListPropertiesConfig.cfg
この部分の機能は、GetAndStreProperties.shというスクリプトを呼び出し、最終的にファイル出力のファイルをユーザーがカスタマイズしたブラックリスト属性ファイルに追加的に書き込みます。
ファイル:Custoom BlackListPropertiesConfig.cfg
このステップは前の3つの要素に基づいて、ソースディレクトリのPrpertiesConfigs.cfgプロファイルに現れた属性と属性の参照を検索し、代わりにsedコマンドを使用してgrepコマンドを使用して検索します。スクリプトコードは以下の通りです
以上はshellスクリプトに基づいて、シェルバージョンをシーンとして、属性のロットを交替して半自動化の実現ステップを作りました。
会社は一連のシェルバージョンを作る必要があります。もしシェルバージョンの内容が同じなら、App Storeに提出すると拒否されるリスクがあります。前の記事で述べたシェルバージョンに混同コードを注入して、アップルから内容があまりにも似ていることが検出され、審査が拒否されることを防止します。もう一つの可能な方法は、ソースファイルのクラス名、属性、方法名などを大量に修正して、バイナリファイルにマークの情報を残して、アップルのマシンを回って審査することです。
この記事では、スクリプトを使って属性名を一括的に変更する方法を紹介しています。その後、スクリプトを使ってクラス名や方法名などの情報を大量に修正する文章もあります。
結果
以下はスクリプトを実行して属性を置換した結果図です。スクリプトはすべての代替属性をabc拡張子に追加しました。もちろん、通常のコンパイルで実行できます。
ソースコード:https://gitee.com/dhar/YTTInjectedContentKit
分析
原理分析
objcコードのクラス名、属性、方法、ソースファイルのパスなどの情報は最終的にバイナリファイルにパッケージされ、バイナリファイルに保存されます。sym記号のセグメントでは、Objdump-tコマンドを使用してバイナリ記号情報を確認します。以下のコマンドは、Objdump-tの結果をファイルInjectContht Kitに書き込みます。Example_Symborsに行きます。
objdump -t InjectedContentKit_Example > InjectedContentKit_Example_Symbols
ファイルの内容が大きいので、いくつかの代表的な内容説明を選択しました。
0000000100026350 l d __TEXT,__text __text
#
0000000000000000 l d *UND* /Users/aron/PuTaoWorkSpace/project/sscatch/DevPods/InjectedContentKit/InjectedContentKit/Classes/Composer/PubSearchDataComposer.h
# var
0000000000000000 l d *UND* _OBJC_IVAR_$_TextCardItem._title
0000000000000000 l d *UND* _OBJC_IVAR_$_TextCardItem._showReact
0000000000000000 l d *UND* _OBJC_IVAR_$_TextCardItem._topChart
0000000000000000 l d *UND* _OBJC_IVAR_$_TextCardItem._reaction
# getter
00000001000264a0 l F __TEXT,__text -[TextCardItem title]
00000001000264c0 l F __TEXT,__text -[TextCardItem showReact]
00000001000264f0 l F __TEXT,__text -[TextCardItem topChart]
0000000100026510 l F __TEXT,__text -[TextCardItem setTopChart:]
# setter
00000001000028a0 l F __TEXT,__text -[SSCatchInviteScheduler setOrganizer:]
00000001000028e0 l F __TEXT,__text -[SSCatchInviteScheduler setInputCardBack:]
0000000100002920 l F __TEXT,__text -[SSCatchInviteScheduler setInputTextBack:]
#
0000000000000000 l d *UND* PubSearchDataComposer.m
000000005a937587 l d __TEXT,__stub_helper __stub_helper
00000001000251c0 l d __TEXT,__text __text
上記から分かるように、バイナリには多くの情報が保存されています。ソースコードとは大きく関係しています。簡単な推測をします。アップルのバックグラウンドマシンはバイナリを審査する時に、バイナリの中の記号を通して対比します。もし二つのバイナリ(メインバージョン、シェルバージョン)コードの中の符号の重なりがある閾値を超えたら、これはシェルバージョンをリリースする行為であると判断します。これはアップルが許可しないと言っていますので、実行可能な方法はソースファイルの中のこれらの情報を修正してアップルの審査メカニズムを回避することです。また、アップルはコードの流れによっては判断されないはずです。バイナリのコントロールプロセスはマシンコードになっています。逆コンパイルというのはアセンブリコードです。少しでもバイナリを変えると大きく変わります。この点から判断するのは難しいです。
ステップ分析
主に以下のステップがあります。
実現する
シングルステップ実現
1、代替が必要なソースファイルのすべての属性を探して、処理後に設定ファイルに保存します。
このステップの機能は、クライアントが処理するソースフォルダを入力し、ソースフォルダを経由してすべてのソースファイル(.h.mファイル)を取得します。正規のマッチングを使って属性名を見つけて、しばらく配列に保存して、最後にブラックリストのフィルタリング、フィルタリング、その他のフィルタリング条件を経て、最終的に処理対象の属性をクライアント入力の出力ファイルに保存します。
いくつかのステップに分解できます。
ファイル名:GetAndStreProperties.sh
スクリプトは複数の場所で有用なので、別のモジュールとしていくつかのパラメータを定義して、異なるアプリケーションシーンに適応します。下に台本を使っているところが見えます。
#!/bin/bash
########################
# :
# -i
# -o
# -f
# -c
########################
#######
param_input_dir=""
param_output_file=""
param_custom_filter_file=""
param_should_use_filter=0
#######
while getopts :i:o:c:f opt
do
case "$opt" in
i) param_input_dir=$OPTARG
echo "Found the -i option, with parameter value $OPTARG"
;;
o) param_output_file=$OPTARG
echo "Found the -o option, with parameter value $OPTARG"
;;
c) param_custom_filter_file=$OPTARG
echo "Found the -c option, with parameter value $OPTARG"
;;
f) echo "Found the -f option"
param_should_use_filter=1
;;
*) echo "Unknown option: $opt";;
esac
done
#######
#
blacklist_cfg_file="$(pwd)/DefaultBlackListPropertiesConfig.cfg"
#######
#
declare -a implement_source_file_array
implement_source_file_count=0
#
declare -a tmp_props_array
props_count=0
# mark: p384
# .m
function read_source_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_source_file_recursively $itemPath
echo " ====="
else
#
echo " ${itemPath}"
if [[ $(expr "$item" : '.*\.m') -gt 0 ]] || [[ $(expr "$item" : '.*\.h') -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
}
# ,
# :
function get_properties_from_source_file {
local class_file=$1;
echo "class_file=${class_file}"
properties=$(grep "@property.*" ${class_file})
IFS_OLD=$IFS
IFS=$'
'
for prop_line in $properties; do
echo ">>>>>${prop_line}"
asterisk_seperator_pattern="\*"
if [[ ${prop_line} =~ ${asterisk_seperator_pattern} ]]; then
# string
prop_name=${prop_line##*${asterisk_seperator_pattern}}
# string
seal_pattern=";*"
seal_pattern_replacement=""
prop_name=${prop_name//${seal_pattern}/${seal_pattern_replacement}}
subsring_pattern="[ |;]"
replacement=""
prop_name=${prop_name//${subsring_pattern}/${replacement}}
if [[ ${param_should_use_filter} -gt 0 ]]; then
grep_result=$(grep ${prop_name} ${blacklist_cfg_file})
echo "grep_result = >>${grep_result}<<"
custom_grep_result=""
if [[ -n ${param_custom_filter_file} ]]; then
custom_grep_result=$(grep ${prop_name} ${param_custom_filter_file})
fi
if [[ -n ${grep_result} ]] || [[ -n ${custom_grep_result} ]]; then
echo "--${prop_name}-- "
else
echo "--${prop_name}--XXX "
tmp_props_array[$props_count]=$prop_name
props_count=$[ props_count + 1 ]
echo ">>>>>>>result_prop_name=${prop_name}"
fi
else
tmp_props_array[$props_count]=$prop_name
props_count=$[ props_count + 1 ]
fi
fi
done
IFS=$IFS_OLD
}
# ,
function get_properties_from_source_dir {
local l_classed_folder=$1
echo " ... ${l_classed_folder}"
#
read_source_file_recursively ${l_classed_folder}
echo " ..."
for(( i=0;i<${#implement_source_file_array[@]};i++))
do
class_file=${implement_source_file_array[i]};
echo " :${class_file}"
get_properties_from_source_file ${class_file}
done;
}
#
# 、 、
# , (DefaultBlackListPropertiesConfig.cfg )
function post_get_properties_handle {
local prop_config_file=$1
#
echo "# Properties Configs" > ${prop_config_file}
for key in $(echo ${!tmp_props_array[*]})
do
# echo "$key : ${tmp_props_array[$key]}"
echo ${tmp_props_array[$key]} >> ${prop_config_file}
done
#
cfg_back_file="${prop_config_file}.bak"
mv ${prop_config_file} ${cfg_back_file}
sort ${cfg_back_file} | uniq > ${prop_config_file}
#
if [[ ${param_should_use_filter} -gt 0 ]]; then
mv ${prop_config_file} ${cfg_back_file}
echo "# Properties Configs Filtered" > ${prop_config_file}
IFS_OLD=$IFS
IFS=$'
'
#
lastLine="";
for line in $(cat ${cfg_back_file} | sed 's/^[ \t]*//g')
do
if [[ ${#line} -le 6 ]] || [[ $(expr "$line" : '^#.*') -gt 0 ]]; then
# 6
echo "less then 6 char line or comment line"
else
if [[ -n ${lastLine} ]]; then
#
# ,
if [[ ${line} =~ ${lastLine} ]]; then
echo "${line} ${lastLine} "
else
echo ${lastLine} >> ${prop_config_file}
fi
fi
#
lastLine=${line}
fi
done
IFS=${IFS_OLD}
fi
#
rm -f ${cfg_back_file}
}
get_properties_from_source_dir ${param_input_dir}
post_get_properties_handle ${param_output_file}
以上のスクリプトを使って生成されたプロファイルPrpertiesConfigs.cfgの部分は以下の通りです。
# Properties Configs Filtered
UserRestrictionLabel
aboutusButton
activitySamplers
addAddressPress
addressSamplers
addressTextBox
appealPress
appliedGroupedSamplers
appliedSamplers
applyPress
asyncArray
asyncListSampler
audioPlayer
2.ユーザーがブラックリストの設定ファイルをカスタマイズする実際のプロセスでは、属性を置き換える記号は、システムクラスの属性を置き換える場合があります。
ファイル名:Default BlackListPropertiesConfig.cfg
# BlackListPropertiesConfig.cfg
# ,
window
name
title
titleLabel
layout
appealSamplers
Get AndStreProperties.shスクリプトで使用されているコードの断片は以下の通りです。実はgrepコマンドを使って検索しています。判定時に見つけられました。もしあるなら処理しません。具体的には上から提供された完全なGetAndStreProperties.shスクリプトコードを見ることができます。
if [[ ${param_should_use_filter} -gt 0 ]]; then
grep_result=$(grep ${prop_name} ${blacklist_cfg_file})
echo "grep_result = >>${grep_result}<<"
custom_grep_result=""
if [[ -n ${param_custom_filter_file} ]]; then
custom_grep_result=$(grep ${prop_name} ${param_custom_filter_file})
fi
if [[ -n ${grep_result} ]] || [[ -n ${custom_grep_result} ]]; then
echo "--${prop_name}-- "
else
echo "--${prop_name}--XXX "
tmp_props_array[$props_count]=$prop_name
props_count=$[ props_count + 1 ]
echo ">>>>>>>result_prop_name=${prop_name}"
fi
else
tmp_props_array[$props_count]=$prop_name
props_count=$[ props_count + 1 ]
fi
3.ある部分から隔離が必要なコードの中の属性はブラックリストの設定ファイルを生成する。この部分の機能は、GetAndStreProperties.shというスクリプトを呼び出し、最終的にファイル出力のファイルをユーザーがカスタマイズしたブラックリスト属性ファイルに追加的に書き込みます。
#...
#
declare -a custom_blacklist_search_dirs
custom_blacklist_search_dirs=("/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/SSCatchAPI"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Categories"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Components"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/External"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/HandyTools"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Macros" )
# ...
#
custom_blacklist_cfg_file="$(pwd)/CustomBlackListPropertiesConfig.cfg"
# ...
#
echo "" > ${custom_blacklist_cfg_file}
for (( i = 0; i < ${#custom_blacklist_search_dirs[@]}; i++ )); do
custom_blacklist_search_dir=${custom_blacklist_search_dirs[${i}]}
./GetAndStoreProperties.sh \
-i ${custom_blacklist_search_dir}\
-o ${custom_blacklist_cfg_tmp_file}
cat ${custom_blacklist_cfg_tmp_file} >> ${custom_blacklist_cfg_file}
done
#...
最終的に生成されたユーザー定義のブラックリストファイルの一部は以下の通りです。ファイル:Custoom BlackListPropertiesConfig.cfg
# Properties Configs
DBFilePath
ValidityString
accessQueue
age
attributedNameString
avatarURLString
avatarUrlString
backColorString
bodyScheduler
bodyView
catchDateString
cellHeight
channelKey
cityName
conditionString
# ....
4.交換が必要なソースファイルのすべてのマッチした属性を一括で置換します。このステップは前の3つの要素に基づいて、ソースディレクトリのPrpertiesConfigs.cfgプロファイルに現れた属性と属性の参照を検索し、代わりにsedコマンドを使用してgrepコマンドを使用して検索します。スクリプトコードは以下の通りです
#!/bin/bash
#
#######
# classes
classes_dir="$(pwd)/../InjectedContentKitx"
#
declare -a custom_blacklist_search_dirs
custom_blacklist_search_dirs=("/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/SSCatchAPI"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Categories"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Components"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/External"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/HandyTools"
"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Macros" )
#
cfg_file="$(pwd)/PropertiesConfigs.cfg"
#
blacklist_cfg_file="$(pwd)/DefaultBlackListPropertiesConfig.cfg"
#
custom_blacklist_cfg_file="$(pwd)/CustomBlackListPropertiesConfig.cfg"
custom_blacklist_cfg_tmp_file="$(pwd)/TmpCustomBlackListPropertiesConfig.cfg"
# ,
class_prefix=""
#
class_suffix="abc"
# ,
checkOrCreateFile() {
file=$1
if [[ -f $file ]]; then
echo " $file"
else
echo " $file"
touch $file
fi
}
#
checkOrCreateFile $cfg_file
#
function checkInputDestDir {
echo -n " : "
read path
if [[ -d $path ]]; then
classes_dir=$path
else
echo -n " ,"
checkInputDestDir
fi
}
#
if [[ -d $classes_dir ]]; then
echo " $classes_dir"
else
echo " $classes_dir"
checkInputDestDir
fi
#######
#
declare -a rename_properties_config_content_array
cfg_line_count=0
#
function read_rename_properties_configs {
IFS_OLD=$IFS
IFS=$'
'
# https://www.jb51.net/article/57972.htm
for line in $(cat $cfg_file | sed 's/^[ \t]*//g')
do
is_comment=$(expr "$line" : '^#.*')
echo "line=${line} is_common=${is_comment}"
if [[ ${#line} -eq 0 ]] || [[ $(expr "$line" : '^#.*') -gt 0 ]]; then
echo "blank line or comment line"
else
rename_properties_config_content_array[$cfg_line_count]=$line
cfg_line_count=$[ $cfg_line_count + 1 ]
# echo "line>>>>${line}"
fi
done
IFS=${IFS_OLD}
}
function print_array {
#
local newarray
newarray=($(echo "$@"))
for (( i = 0; i < ${#newarray[@]}; i++ )); do
item=${newarray[$i]}
echo "array item >>> ${item}"
done
}
#
function rename_properties {
#
read_rename_properties_configs
# print_array ${rename_properties_config_content_array[*]}
#
for (( i = 0; i < ${#rename_properties_config_content_array[@]}; i++ )); do
original_prop_name=${rename_properties_config_content_array[i]};
result_prop_name="${class_prefix}${original_prop_name}${class_suffix}"
sed -i '{
s/'"${original_prop_name}"'/'"${result_prop_name}"'/g
}' `grep ${original_prop_name} -rl ${classes_dir}`
echo " ${original_prop_name}....."
done
}
checkOrCreateFile ${custom_blacklist_cfg_tmp_file}
#
echo "" > ${custom_blacklist_cfg_file}
for (( i = 0; i < ${#custom_blacklist_search_dirs[@]}; i++ )); do
custom_blacklist_search_dir=${custom_blacklist_search_dirs[${i}]}
./GetAndStoreProperties.sh \
-i ${custom_blacklist_search_dir}\
-o ${custom_blacklist_cfg_tmp_file}
cat ${custom_blacklist_cfg_tmp_file} >> ${custom_blacklist_cfg_file}
done
#
./GetAndStoreProperties.sh \
-i ${classes_dir}\
-o ${cfg_file}\
-f \
-c ${custom_blacklist_cfg_file}
#
rename_properties
echo "done."
締め括りをつける以上はshellスクリプトに基づいて、シェルバージョンをシーンとして、属性のロットを交替して半自動化の実現ステップを作りました。