First, preface
In the first two cracked articles, we introduced how to use dynamic debugging to crack the APK, one is through debugging Smali source code, one is through debugging so code to carry out the tracing of the crack, then today we on the two article crack method, Take a look at how to develop an application in Android, how to do a layer of security protection of our application, of course, most of the applications in the market now have done some protection strategy, but there is no absolute security, crack is only a matter of time. So the breach and protection is fits together, never-ending war, there is no absolute security, there is no universal way to crack.
Let's take a look at how we can make our application more secure, and we'll take a look at these five things:
1. Obfuscation strategy
2, the application of the signature
3. Modify the name of native function
4, anti-debugging anomaly detection
5, the application of reinforcement strategy
Of course there are other protection methods, we will introduce these five types today, follow up and continue to add
Second, the principle of technology
The first way: Obfuscation strategy
The obfuscation strategy is a protection strategy that each application must add, and not only for protection, but also to reduce the size of the app installation package, so he is a feature that must be added before each app release, and now there are two common obfuscation strategies:
1, the confusion of the code
After we decompile the APK, see the code class name, method name, already the code format does not look like normal Android project code, then this time will increase the reading difficulty, increase the crack difficulty, like this code confusion:
We generally present the crack view Java layer code is two ways:
One is to extract the Classes.dex file directly first, use the Dex2jar tool to convert to a jar file, and then use the Jd-gui tool to view the class structure
One is to use Apktool tool directly decompile apk, get Smali source code, read Smali source code
But this kind of code obfuscation can sometimes add confusion to some degree, but sometimes it's not very safe, because we know that we are usually looking for the entrance of the program in the process of cracking, then these portals are usually application or mainactivity, But these Android component classes are not confusing, so we still have a portal to find, we can locate the entry code, and then trace.
2, the confusion of engineering resources
We said that the confusion of the code can increase the difficulty of code reading, sometimes we to prevent the protection of resources can also be confused, this resource confusion principle here is not much explanation, the team has this function open source, do not know the students can move to GitHub to view:
Https://github.com/shwenzhang/AndResGuard
Of course, there is a great advantage of resource obfuscation is to reduce the size of the APK package, of course, this is not the knowledge point discussed in this article, we are talking about confusing resources to increase the difficulty of finding resources, first look at the results after confusing resources:
Here we can see that an application of confusing resources, After the anti-compilation view of his string.xml content, found that his name is all simple confusion of letters, then this for our previous kind can be used by the name of the value, to find the corresponding string content to get the message, this will be a very painful thing, because you at this time if the global search for a name value, such as the N Ame= ' A ', then have to search out how many such name, find is also very good time, it is not confused before, the general string name is a unique value, the search will not have so many search results, and find the time is also very short.
The way to crack:
But for this kind of confusing resources is also the absolute protection of security, because we know that generally in the post-compilation Java code, see the resource value is not the name value, but the resource corresponding to the value of the int type, such as:
Here we get the value of a string, and the values of these int types can be found in the res/values/pulblic.xml after the anti-compilation:
For example here the 2131230929 becomes 16 binary is: 0x0x7f0800d1, we find in the Public.xml, found Name= ' ey ' an item, and then go to String.xml in the name lookup:
Well, still found the value of this string, the public.xml after the ID of all resources and integer value corresponding value, the confusion after the code is seen in the resource ID of the integer value, then there is no use of confusion. Only the small white.
From the above two confusion strategy to see, confusion for the crack is not much of a hindrance, is just a decoy, but the confusion of another feature is to reduce the size of the APK package, this is the most important reason for each application to add confusion.
The second way: the applied signature
We know that every app in Android has a unique signature, if an app is not signed is not allowed to install to the device, generally we run the Debug program also has a default signature file, but the IDE helped us do the signature work, Generally in the application of the release will be signed with a unique signature file, then we can see in the previous hack, we sometimes need to decompile the application, and then the new signature in the packaging run, which gives a lot of two times the packaging team for the benefit of a means, is to decompile the market package, Then add some ad code, and finally use your own signature in this new packaging to the market, because the signature is not available after the anti-compilation, so can only use their own signature file to sign, but the installation of the application device to install a signature inconsistent application is also failed to install, This also has a problem is that some users installed these two times packaged applications, no longer install the normal application, only unloading load. Then we can make use of the signature of the application is the only feature to do a layer of protection.
In order to prevent the application is two times packaging, or need to crack our apk operation, add signature verification at the entrance, if found that the application's signature is not correct immediately quit the program, we can get the app's signature value when the app starts, and then match the normal signature value, if not meet the program can be directly returned , here we do a simple case test:
Here defines a simple tool class to compare the application of the signature, here is simply to deal with, under normal circumstances should be compared to the signature of the MD5 value, here in order to simply ignore, and then we at the entrance of the program to do a comparison, if not correctly exit the program:
Then we get the above apk, the following to decompile, and then install from the new signature (about how to decompile and sign here, do not explain, using the Apktool and signapk tools, the signature file is their own), and then run:
The discovery program doesn't work at all, and it's a little bit of a hit, and here's a security policy that prevents applications from being packaged two times.
The way to crack:
But this is not the safest, because we know that since there is a place for the signature than the method, then I just need to decompile the APK, modify the Smali syntax, put the method called the local comment:
Just use # to annotate this line of code, and then go back to the new package installation. So this way can only cheat a little white, but it is important to note that how to find this method of signature detection is the most critical, such as some programs in the native layer, but no matter where, as long as in the code, we can find out.
Third Way: Modify the name of the NAITVE function
This method is not very common, because his security measures are not very strong, but also can play a certain strategy, in this knowledge point, we first to understand the so loading process:
In Android, when the program runs System.loadlibrary ("Jnitest") in the Java layer, after this line of code, the program will load the libjnitest.so file, at the same time, generate a "Load" event, after this event is triggered, By default, the program looks for the Jni_onload function in the list of functions loaded with the. So file and executes, relative to the "load" event, when the loaded. So file is unloaded, the "Unload" event is triggered, and the program defaults to the list of functions loaded in the. so file to find Jni_ OnUnload the function and executes, and then unloads the. so file. It is important to note that the jni_onload and jni_onunload functions are not mandatory in. So components, and the user can not implement the same, Java code can be called into the C component of the function, the reason in the C component to implement these two functions (especially the Jni_ onload function), often doing an initialization work or "aftercare" work. It is possible to think of jni_onload as the initialization function of the. So component, which is executed when it is first loaded (the DLL file under window can also have a similar mechanism in the _dll_main () function, through a swith Case statement to identify whether it is currently loaded or unloaded). Consider the Jni_onunload function as a destructor, which is called when it is unloaded. Thus, it is not difficult to understand why many of the JNI C components implement the Jni_onload function. In general, the Jni_onload function in the C component is used to implement the registration interface to the VM, so that the VM can quickly find the C function that the Java code needs to call. (in addition, the Jni_onload function has another feature that tells the VM that the C component uses that JNI version, and if the Jni_onload function is not implemented, the default is JNI version 1.1).
The Java category of the application layer is called to the native function through the VM. In general, the VM is searched for the native function in *.so. If you need to call a number of times, you need to look it over and spend a lot of time. At this point, the C component developer can register the local function with the VM, In order to speed up the efficiency of subsequent calls to the native function. Imagine, assuming that the VM inside a native function list, the initial is empty, before the explicit registration of this native function list is empty, each time the Java call native function first in this list to find needs to call the Nativ The e function, if found, is used directly, if not found, We have to go through the list of functions in the. so file, and each time Java calls the native function to do such a process, the efficiency naturally falls, and in order to overcome this, we can do so by loading the. So file into the initialization, the Jni_onload function, First register the native function in the VM's native function list so that each subsequent Java call to the native function will find the corresponding function in the list of native functions in the VM to speed up
Through the above analysis, we know the original we know so file loading and unloading time, while we can display the manual registration of our own native method, then we know that in general we define the native method, the corresponding native layer function name is: Java_ class name _ Method name This style:
So there are two problems:
The first problem is that when we look at the so file in the Ida tool, it's very easy to find the corresponding native method, and if we know the Java layer's native method name and type, we can directly navigate to this native function:
The second problem is that the malicious hack can get this so file, look at the parameters and return type of the native method is the method signature, and then write a demo program on the Java layer, and then construct a corresponding native method in a and so file, Then we can execute this native method, if we have a checksum password or the method is a native, then it is easy to be executed by the malicious person to obtain the results.
The simple point is, for example, the isequals example above:
Now there is a person who wants to execute this isequals method of my application, then he just needs to unzip my apk, get so file, view the function in so file, or look at the upper Java code, get the return value and signature of this method, then he can write a simple program, Constructs a class:
Cn.wjdainkong.encryptdemo.MainActivity
Then define a native method within him:
Public native Boolean isequals (String str);
Then use system.loadlibrary to load my so file and then execute the Isequals method where appropriate, so that it is equivalent to calling the Isequals method in my so file.
So from the above two to see, if we native layer of functions to follow such a format, is undoubtedly a simple way for the cracker, so we can do so, is to display the registration of our JNI method, only need to call these three functions in our native layer code:
First function: (*env)->registernatives (Env,clazz, methods, Methodslenght)
This function is the manual registration of a native method, this function belongs to jnienv*, the parameters are relatively simple
1 "Clazz is the class that needs to register the native method, is the Jclass type, this we can use the Findclass method of JNIEnv, passing the name of the class to get this object, similar to this:
2 "Methods is a struct, defined as follows:
typedef struct {
const char* name;
Const char* Signature;
void* fnptr;
} Jninativemethod;
The first variable name is the name of the function in Java.
The second variable, signature, uses a string to describe the function's arguments and return values
The third variable, fnptr, is the function pointer, which points to the C function.
A structure similar to this:
Second function: Jint jni_onload (javavm* vm, void* reserved)
This function is said above, so is loaded when the call to, and we can see here can also get the JVM parameters, generally in this function is mainly to perform the above functions of the registration function, and here also need to get a jnienv* variable:
Here, the jnienv variable is obtained through the JVM, and then the registration function is called:
Implementing a manual Registration function
Third function: void Jni_onunload (javavm* vm, void* reserved)
This function is relative to jni_onload and is called when so is unloaded.
Through the above three functions we can manually display the registration of our native function method, then we can also modify the function name of the native layer, do not follow the format of the previous one, increasing the difficulty of the cracker to find the key native layer function:
Here we turn the Isequals function name into Jiangwei:
Then modify the structure of the registration method:
Compile and run, using Ida to view:
At this time the cracker can not follow the regular routine, found the function of the native layer, then the above two problems can be avoided. Increased security
The way to crack:
But the problem comes, now the cracker, generally open so file, if not found the corresponding native method, will find the Jni_onload function, and then in the analysis Arm assembly code, find register function, analysis registration method structure, Find the corresponding native method, then this way is still not reliable, but also can only fool a little white crack. However, we can also learn from this example that there are many things that can be done in jni_onload, such as the signature mechanism verification mentioned above, and we can do it once in jni_onload to increase security:
Look at the Equal_sign function function:
In this method, we actually use the JNIENV variable to call the Java layer method, to obtain the application signature, and then do the comparison
So we use this signature verification method to do security assurance is also a way of thinking at least native layer of code analysis is more difficult than Smali code analysis, and this signature verification mechanism must be static way to crack the apk, that is, by analyzing the code to crack, Because the program is not running, it cannot be cracked by the dynamic way. Then we can only add to the difficulty of reading the code in response to the static way of cracking.
Fourth Way: Anti-debug anomaly detection
This is a way to deal with the fact that many of today's crackers are using IDA to dynamically debug so files to get important information, and if you don't yet know how to use Ida for dynamic debugging so files, you can view this article: Using IDA to dynamically debug so files in Android, After reading this article, we can know that Ida for so dynamic debugging is process-based injection technology, and then use the Linux ptrace mechanism to debug the target process, then the ptrace mechanism has a feature, that is, if a process is debugged, In the status file of his process, there is a field tracerpid that records the debugger's process ID value, such as:
Viewing files:/proc/[mypid]/status
In line sixth, there is a tracerpid field that records the debugger's process ID
Then we can do this to achieve the effect of anti-debugging, that is, we can rotation the status of the process of the file, and then read the Tracerpid field value, if found that he is greater than 0, then represent their own application in the debugging, so immediately quit the program. The principle knows, the code implementation is also very simple, here creates a thread with pthread, then carries on the rotation operation:
Create a thread with Pthread_create, execute the thread_function function after the thread starts
Look at the Thread_funcation function:
Start rotation, read the value of the Tracerpid field, find that more than 0, immediately quit the program, we run the results to see:
See, when we use the IDA tool for debugging, the program exits immediately, and Ida's debug page exits.
So here we see this rotation mechanism to implement anti-debugging strategy, can deal with the general crack small white.
The way to crack:
But there is still a problem, because now the cracker, they are already immune, know that there will be this detection, so will use the IDA tool to the Jni_onload function breakpoint, and then debug, find the detection rotation code, use NOP instruction, replace the detection command, it is equivalent to the detection code to comment, Function of the premature, so this anti-debugging method is still not good, know more people, there is no point, but there is always better than not.
The fifth way: the reinforcement strategy applied
In this way, that is now a lot of applications are used in a way, but also the security of the highest protection, he reinforced three main aspects:
1. Encrypt the Dex file
So we use Dex2jar tools, or apktools tools such as anti-compilation failure, about this Dex encryption here, also do not do too much introduction, before an article has introduced the principle of Dex reinforcement: Android APK reinforcement principle Analysis
The way to crack:
But unfortunately, this approach also fell, because we know, no matter how you dex encryption, and finally need to use DVM to load the Dex file into memory, we know that all the functions of the DVM on Android are in the libdvm.so file, and this file is the existence of the device/ In the System/lib directory, load Dex has an important function:
int dvmdexfileopenpartial (const void* addr, int len, dvmdex** ppdvmdex)
The first parameter is the DEX memory start address
The second argument is the size of Dex. So the breakpoint in this function can be directly dump out of the plaintext Dex
So we use the IDA debugger, find the memory address of the module libdvm.so, find the function dvmdexfileopenpartialpkvipp6dvmdex, in this function breakpoint can dump the Dex file.
In this case, an article will also be written to explain how to crack the application that reinforces Dex now.
2. Encrypt the so file
Now many applications put the function of the medium into the native layer, then if our team so file encryption, then IDA tool can not open so file, so as to achieve security, about the so encryption in Android can refer to this article: Android in the so-hardening principle analysis
The way to crack:
But unfortunately, this way has also been the fall, after reading this article, we know that the reinforcement so has a feature is that you must be in so when called to decrypt, otherwise it will affect the normal native layer call, then this time is very important, is usually at the entrance of so file, that is, with:
__attribute__ ((constructor)) This attribute to label a decryption function, so that the decryption function execution time than any one of the functions of the time, or can be understood to be a class of constructors execution time, However, functions with this attribute are usually in the. Inin_array section of So.
So now the question becomes, if I know. The position of the Init_array end, and then in the view of the arm instruction code in this section, you can get the decryption function, and then in the interpretation of the decryption function logic. So the final point is how difficult it is to see this cryptographic function.
In this case, a subsequent article will be written to explain how to solve the existing reinforcement so application.
3. Reinforcing resource files and androidmanifest.xml files
This hardening is generally to deal with the most popular anti-compilation tool Apktool Now, he is open source, such as the following application:
So see, this reinforcement is the use of apktool tools to strengthen the loopholes, but this apktool tool is also updated in real time, but also to solve the current APK this resource file hardening caused the problem of anti-compilation failure.
The way to crack:
So for this kind of anti-compilation failure, we should compile the source code of Apktool, find the specified save location, and then modify the exception, but this is not a simple job, it requires patience and experience.
Project Download: http://download.csdn.net/detail/jiangwei0910410003/9534543
iii. Safe Work Flow
1, in order to deal with the low-level crack small white, but also to reduce the size of the APK package, we will be a confusion of code and resources, increase the difficulty of cracking
2, in order to deal with the primary crack small white, we will manually register our native method, let the cracker can not find the corresponding native method, at the same time solve some important native method called problem, increase the crack difficulty
3, in order to deal with the intermediate crack small white, we will use the application's signature, to prevent the application of two signature packaging, while preventing dynamic debugging problems, increase the difficulty of cracking
4, in order to deal with the advanced crack small white, we will increase the application of anti-debugging function, to prevent the application is dynamic debugging and process injection problems, increase the difficulty of cracking
5, in order to deal with the senior crack small white, we will adopt the application of the reinforcement strategy, the DEX,SO, resource files to strengthen, increase the anti-compilation work and debugging difficulty
Iv. Summary
Through this article we see, introduced a number of methods of security protection applications, but we also introduced in each method how the cracker responded to this method, so said here that these security protection can be cracked, just a matter of time, over time, we see no absolute security, There is no unified way to crack, only a security strategy came out, the way to crack is also relative to come out, so fits together, each other progress. But the personal feeling of cracking or more than protection, because the crack is reverse thinking, this requirement will be higher, especially for that kind of perverted encryption algorithm of the crack and reverse, especially the egg pain. Finally, I hope this article will let you know that the Android hack is not so easy, security is not so easy. Both are progressing, and we have to make progress!
The security of the Android app battle against attack