文档章节

iOS使用shell脚本注入混淆内容

aron1992
 aron1992
发布于 2018/02/14 13:35
字数 2461
阅读 4388
收藏 16

背景

公司需要做一系列的壳版本,壳版本如果内容雷同提交到App Store会有被拒绝的风险,其中有一种解决方案是在壳版本中注入混淆的代码,防止被苹果检测到内容太过雷同而导致审核被拒绝,本文是针对这个场景,使用shell脚本进行半自动批量添加和删除混淆代码。

shell实战的系列文章
iOS使用shell脚本注入混淆内容
iOS使用Shell脚本批量修改类名称
iOS使用shell脚本批量修改属性

结果

使用方法

  • 打开测试工程

测试工程位于项目目录下面的DevPods/InjectedContentKit/Example/目录下,打开InjectedContentKit.xcworkspace即可

  • 执行命令

在命令行中进入到项目目录下面的DevPods/InjectedContentKit/Example/injectContentShell子目录,在我的电脑对应的目录为/Users/aron/git-repo/YTTInjectedContentKit/DevPods/InjectedContentKit/Example/injectContentShell,然后执行./injectedContentShell.sh脚本文件批量添加混淆内容,脚本开头的to_process_file_dir变量配置的是需要修改的类所在目录,脚本会自动检测该配置是否正确,正确会显示菜单项页面,否则会提示用户输入需要修改的类所在目录,输入正确之后才会显示菜单项页面。菜单项有三个选项,第一个为删除注入内容,第二个为添加注入内容,第三为退出脚本执行,输入对应的编号即可执行相应的功能

➜  injectContentShell git:(master) ./injectedContentShell.sh
/usr/local/bin/sed
testresult = 0
lrwxr-xr-x 1 aron admin 29 2 13 10:28 /usr/local/bin/sed -> ../Cellar/gnu-sed/4.4/bin/sed
testresult = 82
检测到使用gun sed
目录存在 /Users/aron/git-repo/YTTInjectedContentKit/DevPods/InjectedContentKit/Example/injectContentShell/../injectedContentKit/Business
检测到配置文件存在 /Users/aron/git-repo/YTTInjectedContentKit/DevPods/InjectedContentKit/Example/injectContentShell/injectedContentConfig.cfg
开始读取配置文件...

/Users/aron/git-repo/YTTInjectedContentKit/DevPods/InjectedContentKit/Example/injectContentShell/../injectedContentKit/Business
read_implement_file_recursively
// ......省略


			选项菜单

	1. 删除注入内容
	2. 添加注入内容
	0. Exit menu


		Enter option: 2

// ......
>>>>>>>
 内容添加完成 

	Hit any key to continue


			选项菜单

	1. 删除注入内容
	2. 添加注入内容
	0. Exit menu


		Enter option: 1/Users/aron/git-repo/YTTInjectedContentKit/DevPods/InjectedContentKit/Example/injectContentShell/../injectedContentKit/Business/ICKXibTestViewController.m
>>>>>>>[[GameDetailDataComposer new] loadDataWithBlock:nil];
// ......省略
pattern_str = \[\[WriterDataComposer new\] loadWithType:MMLoadTypeMore completionBlock:nil\];
 内容删除完成 

处理结果

添加注入内容到源码中的结果

添加注入内容到源码中

把注入内容从源码中删除的结果

把注入内容从源码中删除

本文的Demo代码YTTInjectedContentKit
Fork me on Gitee

分析

在开始做之前,对步骤流程做了一些构思如下:

初始步骤流程

  • 步骤一:手动处理
    混淆注入类列表
    混淆注入类对应的方法列表

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

  • 步骤三:自动化
    扫描对应的类和类对应的方法列表,形成对应的配置文件
    从配置文件中读取配置注入到对应的目标类中

  • 步骤四:自动目标文件的处理
    目标文件的查找规则:哪些是需要注入的目标文件
    目标文件的注入规则:目标文件中需要在什么位置进行注入

  • 步骤五:注入内容自身配置
    注入内容需要在不同环境下变换不同的形态,包括类名,方法名等

后面在实现的过程中发现步骤三和步骤五不好实现,所有简化了这部分的流程,最终只保留了以下几个步骤:

优化的步骤流程

  • 步骤一:手动处理
    混淆注入类列表
    混淆注入类对应的方法列表

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

  • 步骤三:自动目标文件的处理
    目标文件的查找规则:哪些是需要注入的目标文件
    目标文件的注入规则:目标文件中需要在什么位置进行注入

以及在实现过程中遇到了一些细节需要处理,这些细节部分作为自步骤如下:

  • 子步骤:
    步骤三-1:检查时候安装gun-sed,mac下的sed和gun sed 会有差别,所有统一使用gun sed
    步骤三-2:用户指定目标位置,需要用户输入
    步骤三-3:删除所有的注入内容

实现

步骤一:手动处理

整理一份注入的内容这部分工作是需要手动处理的,这部分的内容应该是具备自完备性的,可以被多个项目做为依赖导入,所以把这些内容作为一个pod库比较合适,也很方便在pod库的测试项目中测试该库的自完备性。库里面的内容可以是任意的,在我实践的过程中,我是把一个旧的项目的网络接口模块作为了这部分内容,因为这部分内容相对的比较独立和容易抽取。

下图是我从旧的项目中提取的一些类作为混淆类

目录结构

以及我在测试工程中测试混淆的接口调用,在测试工程中测试混淆代码的调用以确保编译链接无误以及确保库的自完备性。

#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

步骤二:配置化

配置文件其实就是把测试工程总的接口调用代码拷贝一份到单独的配置文件中,配置文件如下

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

步骤三:自动目标文件的处理

这个是最核心的部分,主要包含了以下内容:

  • 配置文件路径配置和需要注入的源码文件夹的配置
  • 读取配置文件的注入内容
  • 读取源码文件夹下的源码实现文件(XXX.m)
  • 把注入内容添加到源码中指定的位置
  • 从源码从把注入的内容删除

完整的脚本如下,里面有比较完整的注释,阅读起来应该不会有太大难度:

文本的插入和删除部分使用的是shell中的sed(stream editor)工具,特别滴在mac中sed命令和标准的sed命令有差别,脚本中也会有做这部分的检测,如果机器上安装的不是标准的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
	# 设置局部环境变量
	echo "set PATH...."
	source ./set-gun-sed-path.sh
	echo "set PATH done"

	#mark: echo 颜色选项 http://www.jb51.net/article/43968.htm
	echo "请手动执行命令,然后重新执行"
	command="PATH=\"/usr/local/Cellar/gnu-sed/4.4/bin:\$PATH\""
	printHighlightMessage $command
	echo ""
	exit 1
fi


# 循环检测输入的文件夹
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 "请确认需处理源码目录是否存在 $to_process_file_dir"
	checkInputDestDir
fi

# mark: p261
# 配置文件检查
if [[ -f $cfg_file ]]; then
	echo "检测到配置文件存在 $cfg_file"
else
	echo "请确认配置文件是否存在 $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 ""

# 读取需要处理目标文件
declare -a implement_source_file_array
implement_source_file_count=0

# mark: p384
# 递归函数读取目录下的所有.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 "处理目录结束====="
			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}


# 处理目标文件,添加配置文件中注入的内容
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="内容添加完成"
	printHighlightMessage $message
}


# 处理目标文件,删除配置文件中注入的内容
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="内容删除完成"
	printHighlightMessage $message
}


function genMunu {
	clear
	echo
	echo -e "\t\t\t选项菜单\n"
	echo -e "\t1. 删除注入内容"
	echo -e "\t2. 添加注入内容"
	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 )
		# 删除配置文件中注入的内容
		removeInjectedContent
	;;
	2 )
		# 添加配置文件中注入的内容
		addInjectedContent
	;;
	h )
		genMunu
	;;
	* )
		echo "Wrong!!"
	;;
	esac

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

done

总结

以上就是基于shell脚本,从混淆内容注入和把混淆内容删除两方面做了一个半自动化的实现步骤,如果不妥之处,还请不吝赐教。

© 著作权归作者所有

aron1992

aron1992

粉丝 65
博文 90
码字总数 164172
作品 0
厦门
程序员
私信 提问
加载中

评论(24)

aron1992
aron1992 博主

引用来自“YYFast”的评论

大神,请问下RenameFunction.sh混淆方法脚本里面怎么没有东西,你写完了没?

引用来自“aron1992”的评论

这个TODO,暂时没空处理,这个处理比较麻烦

引用来自“YYFast”的评论

还有一个就是我用RenameClass.sh批量更改类名实现了,但是这个能批量改文件夹的名字不?
不行的
YYFast
YYFast

引用来自“YYFast”的评论

大神,请问下RenameFunction.sh混淆方法脚本里面怎么没有东西,你写完了没?

引用来自“aron1992”的评论

这个TODO,暂时没空处理,这个处理比较麻烦
还有一个就是我用RenameClass.sh批量更改类名实现了,但是这个能批量改文件夹的名字不?
aron1992
aron1992 博主

引用来自“YYFast”的评论

大神,请问下RenameFunction.sh混淆方法脚本里面怎么没有东西,你写完了没?
这个TODO,暂时没空处理,这个处理比较麻烦
YYFast
YYFast
大神,请问下RenameFunction.sh混淆方法脚本里面怎么没有东西,你写完了没?
摩西d
大神,你好,这个地方报错了,怎么弄
./injectedContentShell.sh: line 36: ./set-gun-sed-path.sh: No such file or directory
set PATH done
请手动执行命令,然后重新执行
PATH="/usr/local/Cellar/gnu-sed/4.5/bin:$PATH"
h
hezikun

引用来自“hezikun”的评论

老哥 能否加个QQ咨询下问题?

引用来自“aron1992”的评论

862709539
老哥 加你了 通过一下哈
aron1992
aron1992 博主

引用来自“hezikun”的评论

老哥 能否加个QQ咨询下问题?
862709539
h
hezikun
老哥 能否加个QQ咨询下问题?
565566
565566
你的联系方式多少,我们想找你弄APP,我的QQ2109610983
565566
565566
s
Dhar/YTTInjectedContentKit

YTTInjectedContentKit iOS壳版本场景下的批量修改类名、属性名、插入混淆代码、修改项目名称的shell脚本 具体的实现和使用方法请参考我的博客文章: iOS使用shell脚本注入混淆内容 iOS使用S...

Dhar
2018/05/04
0
0
iOS安全攻防-代码混淆、反调试

一、静态分析 静态分析是指用工具对程序结构,代码逻辑的分析。很大程度上取决关键字,通过关键字找到敏感代码,进行破解。所以静态分析的防护主要是代码混淆。 代码混淆 念大婶在博客中介绍...

萌面道人
2018/11/05
0
0
IOS工程自动打包并发布脚本实现

作者:webfrogs 转载请注明出处。 前言 IOS的开发过程中,当需要给测试人员发布测试包的时候,直接使用xcode来做的效率是非常低下的。尤其是当有一点小改动需要重新出包时,那简直是个折磨的...

孙哲
2015/03/26
0
6
iOS音视频—FFmepg:iOS平台下集成和应用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wtdask/article/details/83901924 1.在iOS平台下集成和应用FFmpeg Mac配置FFmpeg环境 1、安装homebrew 2、安装...

十二指环
2018/11/12
0
0
PhoneGap使用PushPlugin插件实现消息推送

概括 Android设备通过GCM服务来接收推送消息,而iOS设备这从苹果的APN服务获取。虽然按照负载来说这是两种不同的服务,但是证书都在从第三方接收消息的时候是需要作为一个存储和转发类型的服...

Mr-iu
2014/04/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

uniapp + bootstrapvue 移动/PC 一套搞定 (一)配置bootstrapvue

1.准备文件 自己到DCloud官网: http://dcloud.io/ 去下载官方的IDE Hbuilder,新建一个空的uniapp项目即可。 uniapp框架自带优化的vue,我们仅仅需要准备以下三个文件: bootstrap.min.css ...

panyunxing
今天
5
0
Android Camera原理之camera service类与接口关系

camera service主要是指 frameworks/av/services/camera/下面的代码,最近在看这一块的代码,为了更好地理清这一块的代码,也为了后续学习camera方便一些,我觉得很有必要理一下这一块的整体...

天王盖地虎626
今天
2
0
Golang学习笔记

[TOC] Golang学习笔记 这个学习笔记是最早在1.初,版本左右的时候写的,和当前最新的版本可能会有较大的差异. 因为成文比较早,文章里面又有很多自己的见解,有些东西当时理解的不太透彻可能写错...

我爱吃炒鸡
今天
8
0
科技赋能成效显著!金融壹账通两大赋能项目荣获IDC大奖

7月19日,2019IDC中国未来金融论坛曁颁奖典礼于北京举办。由金融壹账通赋能的长春农商银行多人视频面审智能风控系统、包头农商银行互联网银行SaaS服务两大项目因在项目的创新性、技术领先性、...

IFTNews
昨天
1
0
HTTP协议

HTTP简介 HTTP协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准。 HTTP是一个基于TCP/IP通信协议来传递数据...

Eappo_Geng
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部