This article is the third in the Android reverse series. It begins to introduce the relevant knowledge of Dalvik VIRTUAL machine, understand dex and SMALI file formats, and get familiar with Dalvik bytecode and instruction set. Once you have a general understanding of Dalvik instruction set, you can start simple decompilation static analysis. In the following part, four components of Android development and a simple EXAMPLE of APK development using Eclipse are mentioned. In the end, a cracking example is used to deepen the knowledge and concepts of the whole paper and further familiarizing with the use of tools and Dalvik instruction set.
A, Dalvik
1. Introduction to Dalvik
Dalvik is a virtual machine specially designed by Google for Android operating system. Dalvik VM is register-based, while JVM is based on stack. Dalvik has its own file execution format, DEX (Dalvik Executable), while the JVM executes Java bytecode. The Dalvik VM is faster and takes up less space than the JVM.
We can’t modify a certain logic in Java code, so we need to translate the Java code into smali code, which is a dex file into a SMALI file. It can be understood that the SMALI in Dalvik can be modified, but the Java code cannot be modified. Therefore, we want to crack the code, that is, change the Java code to smALI code, modify the SMALI code and then compile it back. Meanwhile, the Java logic has changed.
Smali format is an intuitive and readable form of DEX format
Smali files can be thought of as Davilk’s bytecode files
See the follow-up introduction to Smali
2. Dalvik register nomenclature
In the Dalvik VIRTUAL machine parameter passing method, if a function uses M registers and the parameters of the function are N, then the parameter uses the last N registers and the local variable uses the first M-N registers from the beginning
The Dalvik register has two nomenclatures
V nomenclature
The v naming method uses a lowercase letter “V” to indicate the local variables and parameters used in the function. All registers are named in ascending order starting from v0.
Parameter register V (M-N) VM Local variable register v0 vn
P nomenclature
Basically similar, the main is that the parameter register is named using P register, while the local variable register is still named using V register
Parameter register P0 PN Variable register v0 vN
3, V naming Smali code analysis
The Smali code is shown below, starting with the first line
static public DecryptDemo->getHelloWorld(Ljava/lang/string; I)Ljava/lang/string;Copy the code
The first line calls a getHelloWorld() method. The parentheses indicate two arguments, Ljava/lang/String and I, with semicolons; Separated, the return value is of type Ljava/lang/String
Regsize :[5] indicates that there are five registers
The first red box calls the method to store the values of the v2 and v3 registers and returns a v2. In the second red box, the method is called to store the values of registers V0 and V4, returning a v0.
Invoke-virtual virtual method invocation. The invoked method is recognized at runtime as the actual invocation, with reference to the actual object referenced by the instance, dynamically recognized
4. Code analysis of P naming method Smali
Again, the first line shows that a getHelloWorld() method is called with two arguments, Ljava/lang/String and I, using a semicolon; Separated, the return value is of type Ljava/lang/String
invoke-virtual {v1, p0}, Ljava/lang/stringBuilder; ->append (Ljava/lang/String;) Ljava/lang/StringBuilder; move-result-object v1Copy the code
The first red box calls an append method in the LJava/lang/StringBuilder class to concatenate the passed String and return LJava/lang/StringBuilder with an incoming argument at p0 and an outgoing argument at v1. A move-result-object is returned
The second red box is similar in that it calls an append method to concatenate the incoming String and returns a LJava/lang/StringBuilder type with an incoming argument at p1 and an outgoing argument at v0
Dex file decompiler tool
Dalvik VM does not support the direct execution of JAVA bytecode, so it translates, reconstructs, interprets and compresses the compiled.class file. This process is processed by DX, and the generated product ends in.dex, which is called dex file.
The tools and processes involved in the whole compilation/decompilation are as follows:
1) Compile the SMALI file flow
.java ==> .class ==> .dex ==> .smali
Copy the code
2) The dx.jar script packages the class file as a dex file
dx --dex --output=Test.dex com/xxx/ooo/Test.class
Copy the code
3) The baksmali. jar script decompiles dex files into smali files
java -jar baksmali.jar -o smali_out/ source.dex
Copy the code
4) The smali.jar script packages smali files into dex files
java -jar smali.jar smali_out/ -o source.dex
Copy the code
6. Dalvik bytecode type
Davilk bytecode has only two types: primitive and reference types, and objects and arrays are both reference types.
Both the basic type and the void type, which has no return value, are represented by an uppercase letter representing the object type by the letter L plus the fully qualified name of the object and the array type by [
What is a fully qualified name?
In the case of String, whose full name is java.lang.String, its fully qualified name is Java /lang/String; . The Java. Lang. String. “” Replace “/” with a semicolon at the end; Do the terminator
The specific rules are as follows:
Type Descriptor Java type V voidZ BooleanB byteS stringC charI intJ longF floatD doubleL Java object type \[Array typeCopy the code
To explain Java object types: L can stand for any class in a Java type, such as java.lang.String in Java code, which is described in Davlik as Ljava/lang/String
2. Dalvik instruction set
The above is just a brief understanding of Dalvik bytecode, and the specific logic involved in each method still needs to be explained by Dalvik instruction set. Dalvik instruction set is introduced below. Since Dalvik VIRTUAL machine is based on register architecture, its instruction set style is more inclined to assembly instruction in x86
Data definition instruction
The const directive defines data such as variables, constants, classes, etc. in code
instruction | describe |
---|---|
const/4 vA,#+B | Assign the value to register vA by expanding the value symbol to 32 |
const-wide/16 vAA,#+BBBB | Assign a register to vAA after extending the numeric symbol to 64 bits |
const/high16 vAA, #+BBBB0000 | Extend the zero on the right of the value to 32 bits and assign to register vAA |
const-string vAA,string[@BBBB](https://github.com/BBBB “@BBBB”) | High string is assigned to register vAA by string index |
const-class vAA,type[@BBBB](https://github.com/BBBB “@BBBB”) | Get a class reference by type index and assign it to register vAA |
Data manipulation instruction
The move directive is used to manipulate data in code
instruction | describe |
---|---|
move vA,vB | Assign the value of the vB register to the vA register, both of which are 4 bits |
move/from16 vAA,VBBBB | Assign the value of the vBBBB register (16 bits) to the vAA register (7 bits),from16 indicating that the source register vBBBB is 16 bits |
move/16 vAAAA,vBBBB | Assign the value of register vBBBB to register vAAAA,16 means that both source register vBBBB and target register vAAAA are 16 bits |
move-object vA,vB | Assign an object reference in the vB register to the vA register, which is 4 bits, as is the vB register |
move-result vAA | Assign the single-word (32-bit) non-object result of the last invoke instruction (method call) operation to the vAA register |
move-result-wide vAA | Assign the binary (64-bit) non-object result of the previous invoke instruction operation to the vAA register |
mvoe-result-object vAA | Assign the result of the object operated on by the previous Invoke instruction to the vAA register |
move-exception vAA | Save the exception that occurred at the previous run time to the vAA register |
More instructions
CMP/CMPL is used to compare two register values, CMP greater than the result means 1, CMPL greater than the result means -1.
instruction | instructions |
---|---|
cmpl-float vAA,vBB,vCC | Compares two single-precision floating point numbers. If the value in the vBB register is greater than that in the vCC register, -1 is returned to the vAA, 0 is returned for equality, and 1 is returned for less |
cmpg-float vAA,vBB,vCC | Compare two single-precision floating-point numbers and return 1 if the value in the vBB register is greater than the vCC value, 0 for equality, and -1 for less than |
cmpl-double vAA,vBB,vCC | Compare two double-precision floating-point numbers, returning -1 if the value in the vBB register is greater than the vCC value, 0 if equal, and 1 if less |
cmpg-double vAA,vBB,vCC | Compare a double – precision floating-point number |
cmp-double vAA,vBB,vCC | Equivalent to CMPG -double vAA,vBB,vCC instruction |
Jump instruction
Davlik provides three jump instructions for jumping to different addresses: goto, Swicth and if
instruction | operation |
---|---|
goto +AA | Unconditionally jump to the specified offset (AA is the offset) |
packed-switch vAA,+BBBBBBBB | The value in the vAA register is the value that needs to be determined in the switch branch, and the value in the BBBBBBBB register is the index value in the offset table (Packed -switch-payload). |
spare-switch vAA,+BBBBBBBB | The random branch hop command is similar to the Packed -switch, except that the index value in the BBBBBBBB offset table (spread-switch-payload) is payload |
if-eq vA,vB,target | The equality in the vA and vB registers is equivalent to if(a==b) in Java, such as if-eq v3,v10,002c, which means jump to current position+002c if the condition is true. The rest are similar |
if-ne vA,vB,target | Equivalent to if(a! =b) |
if-lt vA,vB,target | The value in the vA register is less than vB, equivalent to if(a ‘<‘ b) in Java. |
if-gt vA,vB,target | Equivalent to if(a > b) in Java |
if-ge vA,vB,target | Equivalent to if(a ‘>=’ b) in Java |
if-le vA,vB,target | Equivalent to if(a ‘<=’ b) in Java |
Return instructions
The return directive returns the result of the execution of a method
instruction | instructions |
---|---|
return-void | Return nothing |
return vAA | Returns a 32-bit value of a non-object type |
return-wide vAA | Returns a 64-bit value of a non-object type |
return-object vAA | A reference to an object type is returned |
Method call instruction
Invoke-virtual: invokes the instance's virtual method (normal method)invoke-super: invokes the instance's parent/base class method invoke-direct: invokes the instance's direct method invoke-static: Invoke-interface: Invokes the instance's interface methodCopy the code
Instance operation instruction
The operation object instance is related
instruction | describe |
---|---|
new-instance vAA,type[@BBBB](https://github.com/BBBB “@BBBB”) | Constructs an object of the specified type to assign a reference to the vAA register. Array objects are not included here |
instance-of vA,vB,type[@CCCC](https://github.com/CCCC “@CCCC”) | Determines if the reference to an object in the vB register is of the specified type, if so, assign v1 to 1, otherwise 0 |
check-cast vAA,type[@BBBB](https://github.com/BBBB “@BBBB”) | Converts a reference to an object in the vAA register to the specified type. On success, the result is assigned to vAA; otherwise, a ClassCastException is thrown. |
Null operation instruction
Nop directives have no practical meaning and are generally used for code alignment
There are also some instructions not introduced, a little understanding of the next can be, in the actual test encountered to explain learning
Three, Android development four components
When it comes to Android development, it is inevitable to mention its four components Activity, Service, BroadcastReceiver and ContentProvider, whose functions are respectively
BroadcastReceiver provides BroadcastReceiver functions. ContentProvider supports multiple applications to store and read dataCopy the code
1
An Activity provides an interface for users to complete related operations. An APK usually contains multiple activities that can be invoked only after being declared in the Android manifest.xml.
Activity Lifecycle
To start the AcElasticity process, call the onCreate() method to create an Acelasticity, call the onStart() method to make it visible instead of invisible, and call the onResume() method to enable users to manipulate the interface to gain focus and to run On Advantage. Call onPause() to make the page unfocused (call onResume() to get the focus to continue), and call onStop() to make the page invisible (if the dialog box is visible). At this point, the onRestart() method can be called to restore to the onStart() state, or after onDestroy(), the AcElasticity interface disappears and the AcElasticity process ends.
B: Yes, it is
If a Service does not end when we exit an application, it is still running in the background, when do we need a Service? For example, when we play music, we might want to do other things while listening to music. When we quit the music application, if we don’t use Service, we can’t hear the song, so we need Service. Or when we get data from an application over the network, When the data is different at different times, we can use Service to update the data periodically in the background, instead of retrieving the data every time we open the application.
Service Life cycle
A Service life cycle is not as complex as an Activity. It inherits onCreate(), onStart(), and onDestroy(). When we first start the Service, we call onCreate() and onStart(). When the Service is stopped, the onDestroy() method is executed. It is important to note that if the Service is already started, when we start it again, the onCreate () method is not executed, but the onStart() method is executed directly.
3. BroadcastReceiver
BroadcastReceiver receives and sends system-wide notifications, allowing any Android application to receive messages from the system and other applications
4. ContentProvider
ContentProvider is used to share data between different applications. It provides a complete mechanism that allows one application to access data in another application while ensuring the security of the accessed data. Using ContentProvider is the standard way for Android to share data across applications
There are two ways to implement ContentProvider:
-
Use existing content providers to read and manipulate data in programs
-
Create your own content provider to provide external access to our program’s data.
An application provides an external access interface API to its data through a content provider, which can be accessed by any other application. For example, the Android phone book, SMS, and media library programs all provide similar access apis.
Iv. Use Eclipse development tools
This section takes a quick look at Eclipse and develops a simple APK to run on an emulator/real machine
1. Create an Android app project
1) Create an Android Application Project
2) Enter the name of the new application
3) Set the icon of the application
4) Select blank components
Select the Activity component, there are different types, you can choose, here select the blank component first
Then select Finish
2. Introduction of project files
After creating the project in step 1, the following page is displayed
The main program code mainactivity. Java can be found in the left project bar. Double-click to view it
Androidmanifest.xml is the manifest file for any application. It contains all of the application’s declarations and some configuration information, such as the version of Android and some android icon names
Eclipse provides the following graphical and code operations for manifest.xml
3. Build projects
Just add some components in the left sidebar. For further study, please develop your own Google Android
4. Run projects
Export the new project to run
Select lightning Simulator
Double-click the start
Five, jADX-GUI decompiler tool use
Here is an introduction to the simple use of Jadx tool steel, and then into the cracking example of section 6
Tip: Just drag it in and click on the search class to complete the decompilation
1. Loading files and introduction
Load snake APK file, there are two main decompile files, source code and resource file, resource file corresponding to the file in APK (here with compression software open APK file view)
2. Simple search classes
3. Function jump
Select the function and press Ctrl+ to jump directly to the function declaration. For example, BuyFailed() here
Six, snake APK crack
1, Snake APK cracking introduction
The BuyFailed() and BuySccess() functions can be repositioned or modified, but cannot be modified in the Java code layer. It can only be modified in the Smali code layer. Let’s take a look at the Smali code and some underlying knowledge
2. Study on apK program
Click the buy button on the store page and the payment failure is displayed as shown below.
Our goal: to buy all skins for free
3, Jadx tool decompilation analysis
Drag into the file, search for the location of “payment cancellation”, and simply check the code. It can be found that both payment cancellation and payment failure will jump to BuyFailed() method, while successful payment will jump to BuySccess() method. We can think of overwriting the successful method to achieve the effect of free purchase. Then follow up the analysis at the Smali code layer.
4, Android Killer decompiling tools | Smali code analysis
Drag the APK program into Android Killer to decompile, search for the word “cancel payment” in project search, and jump to the SMali code that contains that character
However, there is a small problem. How can I be sure that the SMali code here corresponds to the Java code I just saw? Android Killer provides the ability to decomcompile back to Java code. Click the logo at the top of the image below to view the Java source code, and you can see that it is consistent.
5, replace | smali code to compile
Find the smALI code that successfully paid, as shown in the red box below
Overwrites the smALI code where the payment failed and the payment was cancelled
Save and compile back
6, check the effect
As you can see, it’s already available for free