iOS应用加固

完整性校验

通过检测SignerIdentity判断是Mach-O文件否被篡改

原理是:SignerIdentity的值在info.plist中是不存在的,开发者不会加上去,苹果也不会,只是当ipa包被反编译后篡改文件再次打包,需要伪造SignerIdentity。

    NSBundle *bundle = [NSBundle mainBundle];
    NSDictionary *info = [bundle infoDictionary];
    if ([info objectForKey:@"SignerIdentity"] != nil)
    {
        return YES;
    }
    return NO;

参考:https://www.jianshu.com/p/91aa49c45677

越狱检测

参照念茜大神的方法:iOS安全攻防(二十):越狱检测的攻与防,很详细的讲述了检测的办法,不过经过测试,发现有的方法在未越狱的设备上也会检测成越狱,可以在使用的时候过滤掉这些方法。

双击home键后app缩略视图模糊处理

有时候双击home键,app展示启动app记录,会有可能暴露敏感信息,可以在app退至后台时做一个模糊处理,然后进入前台后移除模糊效果。
实现如下:

@interface AppDelegate ()
@property(nonatomic,strong)UIVisualEffectView *effectView;
@end

@implementation AppDelegate

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self.window addSubview:self.effectView];
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    [self.effectView removeFromSuperview];
}

-(UIVisualEffectView *)effectView
{
    if (!_effectView)
    {
        UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
        _effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
        _effectView.frame = self.window.bounds;
    }
    return _effectView;
}
...
@end

方法名混淆

也是参照念茜大神的方法:iOS安全攻防(二十三):Objective-C代码混淆.

明文字符串混淆

参考这篇文章:iOS字符串硬编码混淆.

可能作者的步骤描述的不是很清楚,我在这里简单描述一下:
1.在项目的.pch文件中,添加如下代码

#include "GolobalCFile.h"


//字符串混淆加密 和 解密的宏开关
//#define ggh_confusion
#ifdef ggh_confusion
#define confusion_NSSTRING(string) [NSString stringWithUTF8String:decryptConstString(string)]
#define confusion_CSTRING(string) decryptConstString(string)
#else
#define confusion_NSSTRING(string) @string
#define confusion_CSTRING(string) string
#endif

其中decryptConstString定义在c文件GolobalCFile中

/*
 *  字符串混淆解密函数,将char[] 形式字符数组和 aa异或运算揭秘
 *  如果没有经过混淆,请关闭宏开关
 */
extern char* decryptConstString(char* string)
{
    char* origin_string = string;
    while(*string) {
        *string ^= 0xAA;
        string++;
    }
    return origin_string;
}

2.打开终端Terminal,cd到你的工程根目录;
3.执行如下混淆脚本(执行之前将脚本里面的工程名字改为你的工程名字):

python .../confusion.py

将如下脚本写在一个文件里confusion.py。在终端执行即可。

#!/usr/bin/env python
# encoding=utf8
# -*- coding: utf-8 -*-
# 本脚本用于对源代码中的字符串进行加密
# 替换所有字符串常量为加密的char数组,形式((char[]){1, 2, 3, 0})

import importlib
import os
import re
import sys


# 替换字符串为((char[]){1, 2, 3, 0})的形式,同时让每个字节与0xAA异或进行加密
def replace(match):
    string = match.group(2) + '\x00'
    replaced_string = '((char []) {' + ', '.join(["%i" % ((ord© ^ 0xAA) if c != '\0' else 0) for c in list(string)]) + '})'
    return match.group(1) + replaced_string + match.group(3)


# 修改源代码,加入字符串加密的函数
def obfuscate(file):
    with open(file, 'r') as f:
        code = f.read()
        f.close()
        code = re.sub(r'(confusion_NSSTRING\(|confusion_CSTRING\()"(.*?)"(\))', replace, code)
        code = re.sub(r'//#define ggh_confusion', '#define ggh_confusion', code)
        with open(file, 'w') as f:
            f.write(code)
            f.close()


#读取源码路径下的所有.h和.m 文件
def openSrcFile(path):
    print("开始处理路径: "+ path +"  下的所有.h和.m文件")
    # this folder is custom
    for parent,dirnames,filenames in os.walk(path):
        #case 1:
        #        for dirname in dirnames:
        #            print((" parent folder is:" + parent).encode('utf-8'))
        #            print((" dirname is:" + dirname).encode('utf-8'))
        #case 2
        for filename in filenames:
            extendedName = os.path.splitext(os.path.join(parent,filename))
            if (extendedName[1] == '.h' or extendedName[1] == '.m'):
                print("处理源代码文件: "+ os.path.join(parent,filename))
                obfuscate(os.path.join(parent,filename))


#源码路径
srcPath = '../StringDecodeDemo'

if __name__ == '__main__':
    print("本脚本用于对源代码中被标记的字符串进行加密")
    
    if len(srcPath) > 0:
        openSrcFile(srcPath)
    else:
        print("请输入正确的源代码路径")
        sys.exit()

执行完成后查看你的代码,会发现用confusion_NSSTRING和confusion_CSTRING 写的明文字符串都被编码。这样就达到了混淆的效果。

4.由于代码被混淆不利于以后项目迭代,所以需要解码。方法同编码,在终端执行
如下脚本:

#!/usr/bin/env python
# encoding=utf8
# -*- coding: utf-8 -*-
# 本脚本用于对源代码中的字符串进行解密
# 替换所有加密的char数组为字符串常量,""

import importlib
import os
import re
import sys


# 替换((char[]){1, 2, 3, 0})的形式为字符串,同时让每个数组值与0xAA异或进行解密
def replace(match):
    string = match.group(2)
    decodeConfusion_string = ""
    for numberStr in list(string.split(',')):
        if int(numberStr) != 0:
            decodeConfusion_string = decodeConfusion_string + "%c" % (int(numberStr) ^ 0xAA)

# replaced_string = '\"' + "".join(["%c" % ((int© ^ 0xAA) if int© != 0 else '\0') for c in string.split(',')]) + '\"'
    replaced_string = '\"' + decodeConfusion_string + '\"'
    print("replaced_string = " + replaced_string)
    
    return match.group(1) + replaced_string + match.group(3)


# 修改源代码,加入字符串加密的函数
def obfuscate(file):
    with open(file, 'r') as f:
        code = f.read()
        f.close()
        code = re.sub(r'(confusion_NSSTRING\(|confusion_CSTRING\()\(\(char \[\]\) \{(.*?)\}\)(\))', replace, code)
        code = re.sub(r'[/]*#define ggh_confusion', '//#define ggh_confusion', code)
        with open(file, 'w') as f:
            f.write(code)
            f.close()


#读取源码路径下的所有.h和.m 文件
def openSrcFile(path):
    print("开始处理路径: "+ path +"  下的所有.h和.m文件")
    # this folder is custom
    for parent,dirnames,filenames in os.walk(path):
        #case 1:
        #        for dirname in dirnames:
        #            print((" parent folder is:" + parent).encode('utf-8'))
        #            print((" dirname is:" + dirname).encode('utf-8'))
        #case 2
        for filename in filenames:
            extendedName = os.path.splitext(os.path.join(parent,filename))
            #读取所有.h和.m 的源文件
            if (extendedName[1] == '.h' or extendedName[1] == '.m'):
                print("处理代码文件:"+ os.path.join(parent,filename))
                obfuscate(os.path.join(parent,filename))


#源码路径
srcPath = '../StringDecodeDemo'
if __name__ == '__main__':
    print("字符串解混淆脚本,将被标记过的char数组转为字符串,并和0xAA异或。还原代码")
    if len(srcPath) > 0:
        openSrcFile(srcPath)
    else:
        print("请输入正确的源代码路径!")
        sys.exit()

即可解码明文。

为了不那么麻烦,可以打包前将工程拷贝一份,这样就可以只需要编码,不用解码。

以上脚本和代码均在文末Demo中。

反编译

build生成的app

在混淆了函数名以后,当然要检验一下成果了,这里就需要反编译我们的app了,有个很方便的工具Class-dump,这里有篇文章详细的描述了安装和使用方法:Objective-C Class-dump 安装和使用方法(原创).

另外还有一个反编译、反汇编和调试神器:Hopper。可以查看源码。

疑问

1.执行脚本文件时报如下错误

confusion.py: Permission denied

解决方法:
1、打开终端。
2、cd到目标文件夹。
3、输入 chmod 755 你的文件名.sh。

Demo

本文demo:iOS_CodeEncrypt

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 144,481评论 1 305
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,908评论 1 258
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 95,710评论 0 214
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,372评论 0 183
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 49,216评论 1 262
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,949评论 1 178
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,558评论 2 275
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,308评论 0 168
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,183评论 7 237
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,675评论 0 214
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,416评论 2 217
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,757评论 1 232
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,314评论 1 33
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,215评论 2 213
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,682评论 3 214
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,665评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,091评论 0 170
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,687评论 2 233
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,830评论 2 237

推荐阅读更多精彩内容

  • 前提 众所周知,iOS系统安全性非常高,很少出现漏洞,几乎不会中毒。大家认为苹果系统的封闭性会使iOS APP安全...
    小枫123阅读 1,160评论 0 10
  • 前提 众所周知,iOS系统安全性非常高,很少出现漏洞,几乎不会中毒。大家认为苹果系统的封闭性会使iOS APP安全...
    在_宥阅读 23,360评论 12 56
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,598评论 0 10
  • 再道欺凌 1 中关村二小的校园欺凌,被炒的沸沸扬扬,各个公众号各抒己见,可谓百家争鸣。 胡子宏先生说:“他打你,你...
    lovingyourself阅读 226评论 0 1