Android APK Reinforcement Technology Exploration (1)
In order to ensure the source security of Android applications, we usually obfuscate the online applications. However, it is not enough just to obfuscate the code. We also need to harden our applications to prevent others from obtaining our source code through decompilation. At present, APK reinforcement technology is mature and perfect, and “360 reinforcement” is popular on the market. This paper makes a technical exploration on APK reinforcement technology, hoping that readers can understand the principle of reinforcement after reading, and can realize the reinforcement scheme by themselves.
In the Android APK reinforcement technology exploration (a), the process of decompilation and the reason why we can obtain the source code are roughly introduced. Here is to explain the basic process of reinforcement.
I. Reinforcement process
- Create a new Android project and create a shell module to generate the hardened shell ARR file
- The Shell Module contains a SteadyApplication subclass of Application that contains the logic to decrypt the dex file
- Compile the shell to generate the shell.aar file
- Decompress the APK to the apkUnzip directory and obtain all dex files
- Change the name attribute of the application root in the androidmanifest.xml file under apkUnzip to SteadyApplication created in 2. Meanwhile, the Application path of the original APK is saved to the meta-data node for parsing and generation in SteadyApplication
- Use an encryption algorithm to encrypt the dex file obtained in the previous step and delete the original dex file
- Decompress the AAR file generated in 3 to obtain the JAR file inside, and then convert the JAR file into dex file through the DX tool provided in SDK, and put the generated dex file into apkUnzip file
- Zip the apkUnzip folder to generate a new APK
- The signature again
Second, concrete implementation
1. Process of decrypting the hardened DEX file
- GetApplicationInfo ().sourcedir gets the Base APK in the Application. This APK contains all of our Application code.
- Using the Application’s getDir() method, we create a private folder SteadyDir in the Application’s private directory
- We unzip bask.apk in the directory created in 2
- After unpacking, we get all files of APK, and then filter out all files with dex as the suffix. We don’t need the classes.dex file because it’s already loaded into the system, so we just need to deal with the encrypted dex file
- Load the decrypted dex file into the program
2. How to decompress apK files
Zip decompression is mainly used in Java ZipFile class, specific implementation directly on the code, the code contains annotations are not explained.
public static void unZip(File zip, File dir) {
try {
// Clear the directory where the decompressed files are stored
deleteFile(dir);
ZipFile zipFile = new ZipFile(zip);
// each entry in the zip file
Enumeration<? extends ZipEntry> entries = zipFile.entries();
/ / traverse
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
// Name of the file/directory in zip
String name = zipEntry.getName();
// The original signature file is no longer needed
if (name.equals("META-INF/CERT.RSA") || name.equals("META-INF/CERT.SF") || name
.equals("META-INF/MANIFEST.MF")) {
continue;
}
// The empty directory is ignored
if(! zipEntry.isDirectory()) { File file =new File(dir, name);
// Create directory
if(! file.getParentFile().exists()) { file.getParentFile().mkdirs(); }/ / write file
FileOutputStream fos = new FileOutputStream(file);
InputStream is = zipFile.getInputStream(zipEntry);
byte[] buffer = new byte[2048];
int len;
while((len = is.read(buffer)) ! = -1) {
fos.write(buffer, 0, len);
}
is.close();
fos.close();
}
}
zipFile.close();
} catch(Exception e) { e.printStackTrace(); }}private static void deleteFile(File file){
if (file.isDirectory()){
File[] files = file.listFiles();
for(File f: files) { deleteFile(f); }}else{ file.delete(); }}Copy the code
3. How to decrypt dex files
Base. Apk can be easily unpacked into a private directory using the decompression method in step 2. We then filter out all dex files by their.dex suffix (excluding classes.dex), read each dex into a byte array, and decrypt the byte array. AES is used for encryption and decryption here. In order to increase security, JNI is used for decryption here. Decryption methods are as follows:
jbyteArray decrypt(JNIEnv *env,jbyteArray srcData) {
jstring type = (*env).NewStringUTF("AES");
jstring cipher_mode = (*env).NewStringUTF("AES/ECB/PKCS5Padding");
jbyteArray pwd = (*env).NewByteArray(16);
char *master_key = (char *) "huangdh'l,.AMWK;";
(*env).SetByteArrayRegion(pwd,0.16.reinterpret_cast<jbyte *>(master_key));
jclass secretKeySpecClass = (*env).FindClass("javax/crypto/spec/SecretKeySpec");
jmethodID secretKeySpecMethodId = (*env).GetMethodID(secretKeySpecClass,"<init>"."([BLjava/lang/String;)V");
jobject secretKeySpecObj = (*env).NewObject(secretKeySpecClass,secretKeySpecMethodId,pwd,type);
jclass cipherClass = (*env).FindClass("javax/crypto/Cipher");
jmethodID cipherInitMethodId = (*env).GetMethodID(cipherClass,"init"."(ILjava/security/Key;) V");
jmethodID cipherInstanceMethodId = (*env).GetStaticMethodID(cipherClass,"getInstance"."(Ljava/lang/String;) Ljavax/crypto/Cipher;");
jobject cipherObj = (*env).CallStaticObjectMethod(cipherClass,cipherInstanceMethodId,cipher_mode);
jfieldID decryptModeFieldId = (*env).GetStaticFieldID(cipherClass,"DECRYPT_MODE"."I");
jint mode = (*env).GetStaticIntField(cipherClass,decryptModeFieldId);
(*env).CallVoidMethod(cipherObj,cipherInitMethodId,mode,secretKeySpecObj);
jmethodID doFinalMethodId = (*env).GetMethodID(cipherClass,"doFinal"."([B)[B");
jbyteArray text = (jbyteArray)(*env).CallObjectMethod(cipherObj,doFinalMethodId,srcData);
return text;
}
Copy the code
4. Load the dex file
Through the decompression and decryption operations above we get the original DEX files, we put these dex files into a collection