共计 6864 个字符,预计需要花费 18 分钟才能阅读完成。
目前加固对于 App 开发人员来说,不论是 App Store 的审核 4.3 问题,还是为了避免逆向工程、篡改、反编译等问题,加固都算是一个必备的抉择了。
然而加固技术在一步步降级的同时,其固有的平安缺点和兼容性问题却始终未能失去解决。
目前,加固技术曾经倒退到第五代 – 虚机源码爱护,应用代码类型更宽泛,App 爱护级别更高,兼容性更强。那么,这一波技术升级,能解决下面的平安和兼容性问题吗?咱们先挨个看看各代技术的劣势和缺点吧。
第一代加固技术:动静加载
第一代加固技术中的动静加载性能通常被用于爱护应用程序中的外围代码,以及避免歹意攻击者对代码进行反编译、逆向工程等操作。动静加载性能通常包含以下几个步骤:
1. 加密: 将应用程序中的外围代码进行加密解决,以避免歹意攻击者对代码进行解密操作。
2. 动静加载: 在应用程序启动时,动静加载器会将加密后的代码加载到内存中,并对其进行解密操作。
3. 代码完整性检查: 在加载代码后,动静加载器会对代码进行完整性检查,以确保代码没有被篡改或批改。
4. 运行时爱护: 在利用程序运行期间,动静加载器会对代码进行爱护,以避免攻击者利用破绽对代码进行攻打或批改。
外围代码
用 Java 代码简略写了一个动静加载的外围代码:
import java.io.File;
import java.io.FileInputStream;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class DynamicLoader {
private static final String KEY = "mysecretkey"; // 加密密钥
private static final String FILE_PATH = "/path/to/encrypted/code"; // 加密后的代码文件门路
private static final String MD5_CHECKSUM = "1234567890abcdef1234567890abcdef"; // 加密前的代码的 MD5 校验和
public static void main(String[] args) throws Exception {byte[] encryptedCode = loadEncryptedCode(); // 加载加密后的代码
byte[] decryptedCode = decrypt(encryptedCode); // 解密代码
checkCodeIntegrity(decryptedCode); // 查看代码完整性
runCode(decryptedCode); // 运行代码
}
// 加载加密后的代码
private static byte[] loadEncryptedCode() throws Exception {File file = new File(FILE_PATH);
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[(int) file.length()];
fis.read(buffer);
fis.close();
return buffer;
}
// 解密代码
private static byte[] decrypt(byte[] encryptedData) throws Exception {Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
return cipher.doFinal(encryptedData);
}
// 查看代码完整性
private static void checkCodeIntegrity(byte[] code) throws Exception {MessageDigest md = MessageDigest.getInstance("MD5");
byte[] checksum = md.digest(code);
String hex = bytesToHex(checksum);
if (!hex.equals(MD5_CHECKSUM)) {throw new Exception("Code has been modified");
}
}
// 运行代码
private static void runCode(byte[] code) throws Exception {
// 通过反射执行代码
Class<?> clazz = new MyClassLoader().defineClass(null, code, 0, code.length);
clazz.getDeclaredMethod("run").invoke(null);
}
// 将字节数组转换为十六进制字符串
private static String bytesToHex(byte[] bytes) {StringBuilder sb = new StringBuilder();
for (byte b : bytes) {sb.append(String.format("%02x", b));
}
return sb.toString();}
// 自定义 ClassLoader,用于加载解密后的代码
private static class MyClassLoader extends ClassLoader {public Class<?> defineClass(String name, byte[] code, int off, int len) {return super.defineClass(name, code, off, len);
}
}
}
PS0:因为动静加载的实现形式可能因操作系统等因素而异,以上代码示例仅供参考,不能间接用于理论利用。(本人依据需要更改)
PS1:【multidex 组件的加固原理】:Android 的 DEX 文件在设计之初程序广泛较小,所以在 DEX 文件设计时,只容许蕴含 65535 个函数援用。而随着 Android 利用的倒退,大量的利用的代码曾经超过了 65535 的限度,为了解决这个问题,Android5.0 之后原生反对加载多个 dex,而为了对旧版本的兼容,Android 提供了 multidex 组件。该组件的实现原理与下面介绍的是统一的。
缺点和反抗
第一代加固技术的缺点是依赖 Java 的动静加载机制,而这个机制要求要害逻辑(Payload)局部必须解压,并且开释到文件系统,这就给了攻打机会去获取对应的文件。尽管能够通过要害逻辑(Payload)被加载后,被从文件系统删除,用于避免被复制,然而攻击者能够拦挡对应的删除函数,阻止删除。
而要害逻辑(Payload)会被加密后保留,可用于反抗动态剖析,然而攻击者能够通过自定义虚拟机,拦挡动静加载机制所应用的要害函数,在这个函数外部,复制文件系统中的要害逻辑(Payload)文件。
第二代加固技术—不落地加载
第二代加固技术——不落地加载(Non-Resident Load,简称 NRL)技术是一种加固技术,用于爱护软件代码免受反向工程和恶意代码注入等攻打。它的核心思想是将软件代码分为两局部:一个不须要爱护的加载器和一个须要爱护的主程序,加载器负责将主程序加载到内存中并执行,主程序则通过加密和混同等形式爱护代码的安全性。
相比于动静加载,不落地加载在 APK 批改方面曾经欠缺,能做到对开发的零烦扰。
次要流程
NRL 技术的次要流程如下:
1. 加载器加载: 加载器先将主程序的加密局部解密,而后将解密后的主程序加载到内存中。这个过程中,加载器须要读取主程序文件并将其解密。
2. 反调试: 为了避免被调试,主程序须要执行一些反调试的操作。例如,它能够检测调试器是否存在,或者检测是否有其余程序在对其进行调试。
3. 解密和解压: 主程序在内存中运行之前,须要进行解密和解压缩。这个过程中,主程序会调用相应的解密和解压缩函数,对本身进行解密和解压缩。
4. 复原 IAT:IAT(Import Address Table)是用于动态链接库中的函数导入的数据结构,它记录了函数在内存中的地址。为了爱护代码,主程序须要复原 IAT,并将函数地址解密和更新到 IAT 中。
5. 启动主程序: 主程序通过以上的筹备工作之后,能够开始执行了。
外围代码
以下是一个不落地加载的外围代码示例,应用 AWS Lambda 作为后端处理函数。
在本示例中,应用 AWS Lambda 解决来自 API 网关的 HTTP 申请。API 网关是一个齐全托管的服务,能够将 HTTP 申请路由到不同的 AWS Lambda 函数。
要应用这个示例,须要一个 AWS 账户和 AWS CLI(命令行界面)。
首先,创立一个新的 Lambda 函数:
- 关上 AWS 控制台,进入 AWS Lambda 控制台。
- 单击“创立函数”,而后抉择“从模板创立函数”。
- 抉择“api-gateway-authorizer-python”模板。
- 输出函数名称,并抉择一个可用的区域。
- 单击“创立函数”。
接下来,创立一个 API 网关:
- 关上 AWS 控制台,进入 API Gateway 控制台。
- 单击“创立 API”,而后抉择“REST API”。
- 抉择“新 API”并输出 API 名称。
- 单击“创立 API”。
当初,将 API 网关与 Lambda 函数关联:
- 在 API Gateway 控制台中,抉择您创立的 API 网关。
- 在左侧菜单中,抉择“资源”。
- 单击“创立资源”。
- 输出资源名称,并单击“创立资源”。
- 抉择新创建的资源,并在“操作”下拉菜单中抉择“创立办法”。
- 抉择“POST”办法,并单击“确认”。
- 在“POST – Setup”页面中,抉择“应用 Lambda 代理集成”并抉择您的 Lambda 函数。
- 单击“保留”。
ok,咱们曾经将 API 网关与 Lambda 函数关联,并筹备好解决来自 API 网关的 HTTP 申请。
上面是一个示例代码,用来解决 Lambda 函数中的申请,计算两个数字的和:
import json
def lambda_handler(event, context):
# 解析申请注释中的 JSON 数据
body = json.loads(event['body'])
num1 = int(body['num1'])
num2 = int(body['num2'])
# 计算两个数字的和
result = num1 + num2
# 返回后果
response = {
"statusCode": 200,
"body": json.dumps({"result": result})
}
return response
缺点与反抗
不落地加载技术并不是完满的,存在一些缺点和反抗办法:
- 病毒和恶意软件可能会利用不落地加载技术来暗藏它们本人的代码,从而防止被杀毒软件和平安监测零碎检测到。相应的反抗包含应用反病毒软件和平安监测零碎来检测和革除恶意软件。
- 在应用不落地加载技术时,应用程序的代码和数据是以明文模式存储在磁盘上的,这意味着攻击者能够轻松地应用工具来查看或批改这些数据。相应的反抗包含对于须要爱护的敏感数据和代码,能够应用加密技术来爱护它们以及应用虚拟化技术来隔离应用程序的运行环境,以避免恶意软件批改应用程序的代码和数据和应用硬件安全模块(HSM)来爱护要害数据和代码,以避免它们被未经受权的人员拜访和批改。
- 一些恶意软件会通过批改零碎配置文件或在启动过程中拦挡不落地加载技术来绕过它的爱护。相应的反抗包含施行平安的启动过程,包含应用数字签名验证和平安启动链(Secure Boot),以确保零碎和应用程序的完整性和安全性。
第三代加固技术—指令抽离
因为第二代加固技术仅仅对文件级别进行加密,其带来的问题是内存中的 Payload 是间断的,能够被攻击者轻易获取。第三代加固技术对这部分进行了改良,将爱护级别降到了函数级别。
指令抽离技术是一种安全性较高的代码加固技术,它将敏感指令从程序代码中提取进去,造成一个独立的指令库,程序运行时通过调用这个指令库来实现敏感操作,从而防止了指令在程序中明文存在的危险。
指令抽离技术的加固流程:
- 剖析程序代码,确定哪些指令须要抽离。通常来说,波及加密、解密、受权认证、协定解决等敏感操作的指令都须要被抽离。
- 编写指令库,将敏感指令实现为一个独立的代码库,并进行加密解决,确保指令库的安全性。
- 批改程序代码,将敏感指令的调用替换为调用指令库中的相应指令。
- 对指令库进行签名和校验,确保指令库的完整性和安全性。
- 暗藏指令库的地位和调用形式,减少攻击者的攻打难度。
- 对程序进行混同解决,减少攻击者剖析代码的难度。
- 对程序进行安全性测试和破绽扫描,确保程序的安全性。
- 对程序进行公布和更新,及时修复已知破绽,确保程序的继续安全性。
外围代码
展现一个简略的示例代码:
// 原始程序代码
void encrypt(char* data, int len, char* key) {// 执行加密操作}
// 指令抽离后的程序代码
#include "encrypt_lib.h"
void encrypt(char* data, int len, char* key) {encrypt_lib(data, len, key);
}
缺点与反抗
缺点:
- 须要对程序进行批改:指令抽离技术须要对程序进行批改,将敏感指令的调用替换为调用指令库中的相应指令,这可能会影响程序的性能和稳定性。
- 减少程序的复杂性:指令抽离技术会减少程序的复杂性和开发难度,须要进行全面的测试和评估,确保加固后的程序依然满足业务需要和性能要求。
- 指令库的安全性:指令库的安全性十分重要,如果指令库被攻击者窃取或篡改,可能导致程序的安全性被毁坏。
- 反向工程:攻击者能够应用反向工程技术来分析程序和指令库,发现和利用破绽,从而毁坏程序的安全性。
反抗:
- 优化指令库:指令库的安全性十分重要,须要采纳加密、签名、校验等措施来爱护指令库的安全性。
- 混同程序代码:通过对程序代码进行混同,能够减少攻击者剖析代码的难度,从而进步程序的安全性。
- 增强平安测试:对程序进行全面的平安测试和破绽扫描,及时发现和修复安全漏洞,确保程序的安全性。
- 多层加密和验证:能够在指令抽离的根底上减少多层加密和验证,从而减少攻击者攻打的难度和老本。
第四代加固技术—指令转换 /VMP
第三代加固技术在函数级别的爱护,应用 Android 虚拟机内的解释器执行代码,带来可能被记录的缺点,第四代加固技术应用本人的解释器来防止第三代的缺点。而自定义的解释器无奈对 Android 零碎内的其余函数进行间接调用,必须应用 JAVA 的 JNI 接口进行调用。其次要实现由两种:
A、DEX 文件内的函数被标记为 native,内容被抽离并转换成一个合乎 JNI 要求的动静库。动静库内通过 JNI 和 Android 零碎进行交互。
B、DEX 文件内的函数被标记为 native,内容被抽离并转换成自定义的指令格局,该格局应用自定义接收器执行,和 A 一样须要应用 JNI 和 Android 零碎进行调用。
兼容性第四代 VMP 加固技术个别配合第三代加固技术应用,所以第三代的所有兼容性问题,指令转换 /VMP 加固也存在。缺点与反抗不管应用指令转换 /VMP 加固的 A 计划或者 B 计划,其必须通过虚拟机提供的 JNI 接口与虚拟机进行交互,攻击者能够间接将指令转换 /VMP 加固计划当作黑盒,通过自定义的 JNI 接口对象,对黑盒外部进行探测、记录和剖析,进而失去残缺 DEX 程序。
(第四代加固 DEX 文件复原)另外,第四代 VMP 加固技术只实现 Java 代码爱护,没有做到应用 VMP 技术来爱护 C /C++ 等代码,平安爱护能力有所欠缺。
下一代加固技术—虚机源码爱护
跟第四代的 VMP 加固技术相比,虚机源码爱护加固是用虚机技术爱护所有的代码,包含 Java,Kotlin,C/C++,Objective-C,Swift 等多种代码,具备极高的兼容性;使 App 失去更高安全级别的爱护,运行更加稳固。
虚机源码爱护为用户提供一套残缺的工具链,首先把用户待爱护的外围代码编译成两头的二进制文件,随后生成独特的虚机源码爱护执行环境和只能在该环境下执行的运行程序。
虚机源码爱护会在 App 外部隔离出独立的执行环境,该外围代码的运行程序在此独立的执行环境里运行。即使 App 自身被破解,这部分外围代码依然不可见。
(虚机源码爱护加固流程)生成的虚机源码爱护领有独特的可变指令集,极大的进步了指令跟踪、逆向剖析的难度。
同时,虚机源码爱护还提供了反调试能力和监控能力。虚机源码爱护能够通过本身的探针感知到环境的变动,实时探测到外界对本环境的调试、注入等非正常执行流程变动,将调试动作引入程序陷阱,并收回警报,进而进行实时更新,进步平安强度。
结语
加固技术倒退及其攻防反抗的更迭,随同着互联网技术倒退一直降级。
作为“正义”的一方,咱们坚信邪不能胜正,而虚机源码爱护加固作为以后当先的加固技术,在将来很长一段时间,可能为 App 提供足够强度的爱护,为企业和开发者的业务倒退保驾护航。
收费试用:加固产品