代码下载:https://github.com/ZhangJingHao/ZJHAppSafeGuard.git
很多时候,可执行文件中的字符串信息,对破解者来说,非常关键,是破解的捷径之一。为了加大破解、逆向难度,可以考虑对字符串进行加密。字符串的加密技术有很多种,可以根据自己的需要自行制定算法
这里举一个简单的例子,对每个字符进行异或处理,需要使用字符串时,对异或过的字符再进行一次异或,就可以获得原字符
直接在源代码里面编写字符串,使用hopper可直接找到该串

混淆后的代码已经看不到硬编码了。
参考链接:https://www.jianshu.com/p/49e98b8a05fd

对加密方法进行封装,提高代码的可读性
参考链接:https://github.com/CoderMJLee/MJCodeObfuscation

iOS程序可以通过class-dump、Hopper、IDA等获取类名、方法名、以及分析程序的执行逻辑。如果进行代码混淆,可以加大别人的分析难度
可通过宏定义的方式对程序进行混淆,例
#ifndef MJCodeObfuscation_h
#define MJCodeObfuscation_h
// 混淆类名
#define ZJHPerson CsPTLrkanhBbQAPL
// 混淆属性
#define zjh_name HrZLzcgSoPhwMBwW
// 混淆方法名
#define zjh_test _DU_TJLoLaiRpXAv
#define zjh_run KmJHtapxjqnqLtGp
// 可以使用正常的方法名替换原有方法名,减少特殊字符的使用
// 避免大量使用特殊字符,导致审核被拒,如
#define showAlertView dismissSheetView
#endif

需要对加密和解密使用相同密钥的加密算法。由于其速度快,对称性加密通常在消息发送方需要加密大量数据时使用。对称性加密也称为密钥加密。常用的非对称加密有DES和AES。
优点:对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。
缺点:在数据传送前,发送方和接收方必须商定好秘钥,然后使双方都能保存好秘钥。其次如果一方的秘钥被泄露,那么加密信息也就不安全了.
可以用混淆敏感字符串的方式对密钥进行存储,原理同本文第一条。
// DES + Base64 数据加密
- (void)DESRequestWithData:(NSData *)dictData {
NSString *dictStr = [[NSString alloc] initWithData:dictData
encoding:NSUTF8StringEncoding];
// 混淆 DES 的 Key
NSString *keyStr = [_WKCodeConfused getApiSecretKey];
// 3DES + Base64 数据加密处理
NSString *encryptStr = [DES3Util encryptUseDES:dictStr key:keyStr];
NSData *encryptData = [encryptStr dataUsingEncoding:NSUTF8StringEncoding];
[self requestWithData:encryptData];
}


Base64编码原理:https://blog.csdn.net/wo541075754/article/details/81734770
非对称加密算法需要两个密钥来进行加密和解密,这两个秘钥是公开密钥(public key,简称公钥)和私有密钥(private key,简称私钥)。
优点:与对称加密相比,其安全性更好:对称加密的通信双方使用相同的秘钥,如果一方的秘钥遭泄露,那么整个通信就会被破解。而非对称加密使用一对秘钥,一个用来加密,一个用来解密,而且公钥是公开的,秘钥是自己保存的,不需要像对称加密那样在通信之前要先同步秘钥。
缺点:非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
// RSA 加密
- (void)RSARequestWithData:(NSData *)data {
AMRSACryptor *rsaObj = [[AMRSACryptor alloc] init];
// 公钥文件路径
NSString *publicPath = [[NSBundle mainBundle] pathForResource:@"rsacert" ofType:@"der"];
[rsaObj loadPublicKey:publicPath];
// 使用公钥加密数据
NSData *encryptData = [rsaObj RSAEncryptData:data];
[self requestWithData:encryptData];
// 私钥文件路径
NSString *privatePath = [[NSBundle mainBundle] pathForResource:@"p" ofType:@"p12"];
[rsaObj loadPrivateKey:privatePath password:@"123456"];
// 使用私钥解密
NSData *decryptData = [rsaObj RSADecryptData:encryptData];
NSDictionary *dictionary =[NSJSONSerialization JSONObjectWithData:decryptData options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"dictionary : %@", dictionary);
}

在传输 Data 的时候可以对其加密,首先将data转换为byte, 然后对byte进行操作,比如将所有字符与 0xA8 异或。这样比人截取data,看到的是一推乱码
// 传输 Data 加密
- (void)transmitRequestWithData:(NSData *)data {
// 首先将data转换为byte, 然后对byte进行操作
NSUInteger lengthTemp = [data length];
char *bytesTemp = malloc(lengthTemp);
[data getBytes:bytesTemp length:lengthTemp];
// 比如将所有字符与 0xA8 异或
for (int i=0; i<lengthTemp; i++) {
bytesTemp[i]^=0xA8;
}
NSData *encryptData = [[NSData alloc] initWithBytes:bytesTemp length:lengthTemp];
[self requestWithData:encryptData];
}

iOS常用加密算法汇总:ZJHSecurityTool
SSL Pinning(又叫Certificate Pinning)可以理解为证书绑定。在一些应用场景中,客户端和服务器之间的通信是事先约定好的,既服务器地址和证书是预先知道的,这种情况常见于CS(Client-Server)架构的应用中。这样的话在客户端事先保存好一份服务器的证书(含公钥),每次请求服务器的时候,将服务器返回的证书与客户端保存的证书进行对比,如果证书不符,说明受到中间人攻击,马上可以中断请求。这样的话中间人就无法伪造证书进行攻击了。
在我们AFNetworking中,可以这样使用:
+ (AFHTTPSessionManager *)manager
{
static AFHTTPSessionManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:config];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey withPinnedCertificates:[AFSecurityPolicy certificatesInBundle:[NSBundle mainBundle]]];
manager.securityPolicy = securityPolicy;
});
return manager;
}
效果下入:

参考链接:https://www.jianshu.com/p/2c5c8bc55f54
对普通请求、返回数据,可使用DES对称加密的方式,密钥使用敏感字符串混淆的方式存储。
对于重要数据,使用RSA进行数字签名,起到防篡改作;对于比较敏感的数据,如用户信息(登陆、注册等),客户端发送使用RSA加密,服务器返回使用DES(AES)加密;
首先移动端给服务器传递通过RSA公钥加密后的数据,参数包括DES的密钥(密钥是随机生成的八位字符串) 和 相关参数信息,服务器通过私钥解密信息数据,里面包括DES密钥和 相关参数信息,服务器再通过此DES密钥加密返回数据给移动端,移动端通过此DES密钥进行解密获取数据。(注:DES的密钥每一次都要重新随机生成,也就是一个密钥只完成这一次的数据传递)

客服端发起获取插件信息请求,服务端返回插件url地址和对应zip的MD5值,客户端根据url下载zip文件,下载完成后,对zip文件进行md5取值,与服务器返回的md5值对比校验。


只要通过校验,就能确保脚本在传输的过程中没有被篡改,因为第三方若要篡改脚本文件,必须计算出新的脚本文件 MD5 并用私钥加密,客户端公钥才能解密出这个 MD5 值,而在服务端未泄露的情况下第三方是拿不到私钥的。
这种方案安全性跟 HTTPS 一致,但不像 HTTPS 一样部署麻烦,一套代码即可通用。对于它的缺点:数据内容泄露,其实在传输过程中不泄露,保存在本地同样会泄露,若对此在意,可以对脚本文件再加一层简单的对称加密。这个方案优点多缺点少,推荐使用,目前 JSPatch 平台就是使用这个方案。
参考链接:http://blog.cnbang.net/tech/2879/
代码实现:
// 判断zip文件是否合法
- (BOOL)checkIsValidatedPatchAtPath:(NSString *)zipPath {
if (!zipPath.length) {
return NO;
}
// 根据文件头,判断文件是否为 zip 类型
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:zipPath];
NSData *data = [fh readDataOfLength:4];
BOOL isZipFile = NO;
if ([data length] == 4) {
const char *bytes = [data bytes];
// ZIP Archive (zip),文件头:504B0304
isZipFile = (bytes[0] == 'P' && bytes[1] == 'K' && bytes[2] == 3 && bytes[3] == 4);
}
if (!isZipFile) {
return NO;
}
id res = nil;
NSDictionary *dict = nil;
NSString *md5 = nil;
OZZipFile *unzipFile = nil;
@try {
unzipFile=
[[OZZipFile alloc] initWithFileName:zipPath mode:OZZipFileModeUnzip];
// 取出 patch.key 中 md5值
if ([unzipFile locateFileInZip:@"app/patch.key"]) {
OZFileInZipInfo *info = [unzipFile getCurrentFileInZipInfo];
OZZipReadStream *readStream = [unzipFile readCurrentFileInZip];
NSUInteger infoLength = [[NSNumber numberWithUnsignedLongLong:info.length] unsignedIntegerValue];
NSMutableData *keyData = [[NSMutableData alloc] initWithLength:infoLength];
[readStream readDataWithBuffer:keyData];
if (keyData) {
// RSA公钥解析md5值
dict = [keyData wk_decodeKeyData];
}
}
// 取出 main.js 文件数据,生成文件的 md5值
if ([unzipFile locateFileInZip:@"app/main.js"]) {
OZFileInZipInfo *info = [unzipFile getCurrentFileInZipInfo];
OZZipReadStream *readStream = [unzipFile readCurrentFileInZip];
NSUInteger infoLength = [[NSNumber numberWithUnsignedLongLong:info.length] unsignedIntegerValue];
NSMutableData *data = [[NSMutableData alloc] initWithLength:infoLength];
[readStream readDataWithBuffer:data];
[readStream finishedReading];
if (data) {
NSString *tmpPath =
[NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp"];
if ([data writeToFile:tmpPath atomically:YES]) {
WKFileHash *fileHash =
[WKFileHash hashForFile:tmpPath types:WKFileHashTypeMD5];
md5 = fileHash.md5String;
}
}
}
[unzipFile close];
// 校验 md5 值是否相等
if ([dict isKindOfClass:[NSDictionary class]] &&
[md5 isKindOfClass:[NSString class]] && md5.length) {
if ([dict[ @"md5" ] isEqualToString:md5]) {
res = dict;
}
}
}
@catch (NSException *exception) {
[unzipFile close];
return res;
}
NSLog(@"patch.key : %@", res);
return res != nil;
}
为了方便应用软件的开发和调试,UNX的早期版本就提供了一种对运行中的进程进行跟踪和控制的手段,那就是系统调用 ptrace。通过 ptrace,可以对另一个进程实现调试跟踪。同时 ptrace 提供了一个非常有用的参数,那就是 PT_DENY_ATTACH,这个参数用于告诉系统阻止调试器依附。所以,最常用的反调试方案就是通过调用 ptrace 来实现反调试。
把ptrace.h导入工程。ptrace头文件不能直接导入app工程,可以新建命令行工程,然后#import <sys/ptrace.h>进入到ptrace.h,把内容全部复制到自己工程中新建的header文件MyPtrace.h中,那么自己的工程想调用ptrace就可以导入MyPtrace.h直接进行调用.
- (void)viewDidLoad {
[super viewDidLoad];
ptrace(PT_DENY_ATTACH, 0, 0, 0);
}
就是如果别人的的app进行了ptrace防护,那么你怎么让他的ptrace不起作用,进行调试他的app呢?
由于ptrace是系统函数,那么我们可以用fishhook来hook住ptrace函数,然后让他的app调用我们自己的ptrace函数
思路:别人hook ptrace的时候,自己的ptrace已经调用
想要自己函数调用在最之前:自己写一个framework库
在库中写入ptrace(PT_DENY_ATTACH, 0, 0, 0);
库加载顺序:
自己写的库>别人注入的库
自己的库加载顺序:按照 Link Binary Libraries的顺序加载
思路:别人hook ptrace的时候,自己的ptrace已经调用
想要自己函数调用在最之前:自己写一个framework库
在库中写入ptrace(PT_DENY_ATTACH, 0, 0, 0);
库加载顺序:
自己写的库>别人注入的库
自己的库加载顺序:按照 Link Binary Libraries的顺序加载
参考链接:https://www.jianshu.com/p/ebdfb0a25c85
反注入主要还是一种注入检测机制,即检测当前有没有其他模块注入。曾经出现过一种主
动防止注入的方法,但该方法现在已经没有用了。
当旧版的dyld检测到存在 __RESTRICT,__restrict 这样的 section 时, DYLD_INSERT_LIBRARIES
环境变量会被忽略,导致注入失败。因此,在 Xcode 的编译设置选项 “Other Linker flags” 中加
上 -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null 参数,就能达到反注入的效果。
在新版的 dyld 及 iOS10 的测试中发现,该方法已经没有用了,dyld 已经不检测这个 section
了,而且 opool 带 unrestrct 的功能。所以,这个方法现在已经没有实际的用处了。
从另一个角度来分析,可以通过注入检测的方式来判断有没有进行注入。和调试检测一样,
具体的应对措施由我们自己制定。注入检测可以判断加载模块中有没有一些不在正常加载列表
中的模块,使用 _dyld_get_image_name 获取模块名,然后进行对比,具体如下
int AMCheckInjector() {
int count = _dyld_image_count();
if(count>0) {
for (int i = 0; i < count; 1++) {
const char* dyld = _dyld_get_image_name(i);
// 或者发现其他不在白名单内的库是以load command方式注入的
if(strstr(dyld,"DynamicLibraries")) {
return 1;
}
}
}
return 0;
}
参考链接:
https://blog.csdn.net/sysprogram/article/details/76691496
https://www.jianshu.com/p/f664b1da8458
我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A
在Ruby中是否有Gem或安全删除文件的方法?我想避免系统上可能不存在的外部程序。“安全删除”指的是覆盖文件内容。 最佳答案 如果您使用的是*nix,一个很好的方法是使用exec/open3/open4调用shred:`shred-fxuz#{filename}`http://www.gnu.org/s/coreutils/manual/html_node/shred-invocation.html检查这个类似的帖子:Writingafileshredderinpythonorruby?
我正在使用ruby2.1.0我有一个json文件。例如:test.json{"item":[{"apple":1},{"banana":2}]}用YAML.load加载这个文件安全吗?YAML.load(File.read('test.json'))我正在尝试加载一个json或yaml格式的文件。 最佳答案 YAML可以加载JSONYAML.load('{"something":"test","other":4}')=>{"something"=>"test","other"=>4}JSON将无法加载YAML。JSON.load("
默认情况下:回形针gem将所有附件存储在公共(public)目录中。出于安全原因,我不想将附件存储在公共(public)目录中,所以我将它们保存在应用程序根目录的uploads目录中:classPost我没有指定url选项,因为我不希望每个图像附件都有一个url。如果指定了url:那么拥有该url的任何人都可以访问该图像。这是不安全的。在user#show页面中:我想实际显示图像。如果我使用所有回形针默认设置,那么我可以这样做,因为图像将在公共(public)目录中并且图像将具有一个url:Someimage:看来,如果我将图像附件保存在公共(public)目录之外并且不指定url(同
我在一个ruby文件中有一个函数可以像这样写入一个文件File.open("myfile",'a'){|f|f.puts("#{sometext}")}这个函数在不同的线程中被调用,使得像上面这样的文件写入不是线程安全的。有谁知道如何以最简单的方式使这个文件写入线程安全?更多信息:如果重要的话,我正在使用rspec框架。 最佳答案 您可以通过File#flock给锁File.open("myfile",'a'){|f|f.flock(File::LOCK_EX)f.puts("#{sometext}")}
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭8年前。Improvethisquestion我需要实现具有各种灵活需求的密码安全。这些要求基本上取自Sanspasswordpolicy:Strongpasswordshavethefollowingcharacteristics:Containatleastthreeofthe
安全产品安全网关类防火墙Firewall防火墙防火墙主要用于边界安全防护的权限控制和安全域的划分。防火墙•信息安全的防护系统,依照特定的规则,允许或是限制传输的数据通过。防火墙是一个由软件和硬件设备组合而成,在内外网之间、专网与公网之间的界面上构成的保护屏障。下一代防火墙•下一代防火墙,NextGenerationFirewall,简称NGFirewall,是一款可以全面应对应用层威胁的高性能防火墙,提供网络层应用层一体化安全防护。生产厂家•联想网御、CheckPoint、深信服、网康、天融信、华为、H3C等防火墙部署部署于内、外网编辑额,用于权限访问控制和安全域划分。UTM统一威胁管理(Un
我尝试使用Net::HTTP向Twitter发送GET请求(出于隐私原因替换了用户ID):url=URI.parse("http://api.twitter.com/1/friends/ids.json?user_id=12345")resp=Net::HTTP.get_response(url)这会在Net::HTTP中引发异常:NoMethodError:undefinedmethodempty?'for#from/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/net/http.rb:1
运行bundle安装时,我收到以下消息:Rubygems2.0.14isnotthreadsafe,soyourgemswillbeinstalledoneatatime.UpgradetoRubygems2.1.0orhighertoenableparallelgeminstallation.这很奇怪,因为在我的RubyGems环境中它说我的RubyGems版本是:2.4.5.1(见下文)~/w/Rafftopia❯❯❯gemenvRubyGemsEnvironment:-RUBYGEMSVERSION:2.4.5.1-RUBYVERSION:2.2.5(2016-04-26patc
假设你有这个结构:classHouse请注意,Tv的用户是故意不可访问的。所以你有一个三层嵌套的表单,允许你在一个页面上输入房子、房间和电视。这是Controller的创建方法:defcreate@house=House.new(params[:house])if@house.save#...standardstuffelse#...standardstuffendend问题:您究竟如何为每台电视填充user_id(它应该来自current_user.id)?什么是好的做法?这是我在其中看到的catch22。将user_ids直接填充到params散列中(它们嵌套得很深)保存将失败,因