This article was reproduced from: http://drops.wooyun.org/tips/6840
0x00 sequence
With mobile security getting more and more fire, a variety of debugging tools are also emerging, but because of the different environments and requirements, and no tools are omnipotent. Another tool is dead, people are alive, if you can understand the principle of tools and then combine their own experience, you can also create their own debugging weapons. Therefore, the author will be in this series of articles to share some of their own often used or original debugging tools and means, hoping to the domestic mobile security research to play a role in some catalysts.
The directory is as follows:
Android Dynamic debug seven kinds of weapons immortal sword-Smali instrumentation
Android Dynamic debug seven kinds of weapons peacock feather –ida Pro
Android Dynamic debug seven kinds of weapons of departure hook-hooking
Android Dynamic debug seven kinds of weapons of Jasper knife-Customized DVM
Android Dynamic debug seven kinds of weapons of the passionate ring-customized Kernel
Android Dynamic Debug Seven weapons of the Overlord gun-Anti anti-debugging
Android Dynamic debug Fist of Seven weapons-Tricks & Summary
0x01 Peacock Feather
The world's concealed weapon a total of more than 360 species, but the most successful, the most terrible is the peacock plume. It is simple to use, but powerful. It is said that when the Peacock Ling launched, concealed weapon, like Peacock, brilliant, and in the enemy dazzled by the spirit of the moment, they have lost their wits. The description of the weapon is similar to Ida! So android dynamic debug seven kinds of weapons in the peacock feather non-Ida mo. Because Ida is too famous, the corresponding tutorials are flying all over the sky, but many are not related to the Android content, so I decided to put some classic Android debugging skills summarized. Because of the space reason, the author cannot guarantee that this article can cover all aspects of Ida debugging, crossing if interested can continue to study deeply.
0x02 Restore Jni Function method name
In Android debugging, you will often see this type of function:
The first is a pointer plus a number, such as v3+676. The address is then invoked as a method pointer, and the first parameter is the pointer itself, such as (v3+676) (V3 ...). This is actually the JNIEnv method we often use in JNI. Because IDA does not automatically recognize these methods, when we debug the so file we often see that it is not clear what this function is doing, because the function is too abstract. The workaround is very simple and only requires a type conversion of the jnienv pointer. For example, with the V3 pointer mentioned above, we select and click the "Y" key, then declare the type as "jnienv*".
Ida will then automatically find the corresponding method and display it:
Is the moment clear a lot? Others (seemingly on the Snow Forum) also summed up the numbers, addresses, and method declarations for all jnienv methods:
Interested students can go to my github download.
0x03 debugging. Init_array and Jni_onload
We know that the so file will be executed first when it is loaded. Init_array function, and then execute the jni_onload () function. The Jni_onload () function is very easy to find because it has a symbol table, but the function of the. Init_array needs to find out for itself. First open the View->open subviews->segments. Then click. init.array to see the functions in the. Init_array.
But generally, when we use Ida for attach, the. Init_array and Jni_onload () have already been executed, and it is not urgent to debug. At this time we can use jdb this tool to solve, this tool is installed after the JDK comes with, can be found in the JDK bin directory. Here we use the second question of Ali Mobile Security Challenge 2014 as an example to illustrate how to debug Jni_onload ().
After opening the program, the interface is like this:
Our goal is to get the password. Using Ida to decompile the so file will see that the password we entered will be compared to the string that off_628c this pointer to.
So we looked at the corresponding pointer to the OFF_628C address, and found that the corresponding string is "Wojiushidaan".
So we entered the password and found that the password was wrong. It seems that so files dynamically modify the password string when it is loaded. Now that we've modified it dynamically, we're going to use IDA to debug it, we open the program, then we use Ida to attach it, we find that the program is out of the way, and Ida doesn't have any useful information. This is the meaning of self-destruct program. So we dynamically debug jni_onload () to see what the program has done. The steps are as follows:
1 DDMS
Be sure to open DDMS, otherwise the debug port is off, and you cannot pause at the beginning of the program. I didn't know to open Ddms to use JDB, I thought the Android system or SDK problem, re-installed several times. Sweat.
2 adb push androidserver/data/local/tmp/
ADB shellsuchmod 777/data/local/tmp/androidserver/data/local/tmp/androidserver
Here we push Ida's androidserver to the phone and execute as root.
3 adb forward tcp:23946 tcp:23946
Forward the debug port of Ida so the PC-side Ida can connect to the phone.
4 adb shell am start-d-n com.yaotong.crackme/. Mainactivity
Here we start the program in debug mode. The program will appear waiting for debugger debug interface.
5 Ida Attach target app
This is when we start Ida and attach the app process.
6 Suspend on Libary loading
We checked suspend on library load in debugger setup. then click Continue.
7 Jdb-connect com.sun.jdi.socketattach:hostname=127.0.0.1,port=8700
Use JDB to restore the app to execution.
8 Add breakpoint at Jni_onload
The program will then stop when loading the so file libcrackme.so. At this time Ida will be unable to find the file hint, do not control him, click Cancel. Then can see in the modules libcrackme.so this so file, we point in, then at Jni_onload Place next breakpoint, then click Execute, the program enters Jni_onload () this function.
PS: Sometimes you can not F5 in a function, this time you need to press the "P" key, the program will be the code as a function analysis, and then click "F5", you can see the disassembly function.
Because the process is a bit cumbersome, I recorded a debug Jni_onload () video on my GitHub, interested classmates can go to download to watch. Because of the other techniques involved, we will continue to explain how to debug in the following "Ida double-Open Location" section. The function in Init_array.
0x04 Ida Dual-open positioning
Ida Dual-open positioning means that you first use IDA to analyze so files statically and then open an IDA dynamic debug so file. Because Ida does not perform detailed analysis of the entire dynamically loaded so file in dynamic debugging, many functions are not recognized. For example, there are many sub_xxxx functions in static analysis:
However, IDA in dynamic debugging does not have this information.
So we need to double-open Ida and then use the content of IDA static analysis to locate the functions of IDA dynamic Debugging. Of course, many times we also need to dynamically debug the information to help understand the functions of static analysis.
In the previous section, we mentioned that there is a sub_2378 () in the. Init.array, but we cannot find this function in module after IDA dynamically loads so. What should we do then? At this point we are going to locate the target function by statically analyzing the address and the so file in the in-memory base. First we see sub_2378 () The address of this function in static analysis is. text:00002378. In dynamic loading, this so in-memory base address is: 4004f000.
So sub_2378 () the real address of this function in memory should be 4004f000 + 00002378 = 40051378. Below we enter "G" in the Dynamic debugging window, jump to 40051378 this address. Then found the whole is garbled rhythm:
Don't worry, this is because Ida thinks this is a data segment. When we press "P" or select part of the data to press "C", Ida will analyze this data as assembly code:
We can then press "F5" to decompile the assembly code to C.
Is it similar to the sub_2378 () length in static analysis?
We can then add breakpoints in this position, and then combine the debugging techniques mentioned in the previous section to dynamically debug the functions in the Init.array.
We went on to analyze the self-destruct program, and when we were debugging Init.array and Jni_onload (), we found that the program was hung up after executing the DOWRD_400552B4 ().
So here we press "F7" into the function to look at:
Originally Libc.so's phread_create () function, it is estimated that the app itself opened a new thread for anti-debug detection.
Interestingly, in static analysis we don't know what this function does, because the address of this function is not initialized in the. BSS segment: DWORD_62B4
But when we debug dynamically, the value of this address has been modified to phread_create () the address of the function. :
So the self-destruct program password This app uses Pthread_create () to open a new thread to counter-debug the app. The thread will run the SUB_16A4 () function. So we analyzed this function and found that there was a lot of confusion in the contents and it looked very laborious. Here I introduce a small trick: Common anti-debugging methods will use fopen to open some files to detect whether their process is attach, such as the status of the tracerpid in this file is the value of 0, if 0 indicates that no other process is debugging this process, If you do not specify a program for 0, you are debugging. So we can wait for the next breakpoint in fopen () in libc.so, and then we set the data in the Hex view window to synchronize with the R0 value:
In this case, when the function stops at the fopen, we can see which files the program has opened. Sure enough, the program opened the/proc/[pid]/status file. We "F8" continue to execute the fopen function to see where the address is after the return. Then found that our program is stuck in the middle of a function, the PC is data, the PC is the assembly. What should we do about it?
Solution or Ida double open, we know that the address of the PC is now the 40050420,libcrackme.so file base is 4004f000. So the position of this code in so should be: 40050420-4004f000 = 1420. So we go back to the IDA static analysis interface and we can navigate to the fact that we're in the sub_130c () function. So we suspect that this function is used for anti-debug detection.
So we can locate sub_130c () This function in memory by base address: 40050420 + 130C = 4005030C. Then we press "P" at this address at 4005030C, and Ida can identify the entire function correctly.
So the dynamic debugging can help us to understand a lot of static analysis difficult to obtain information. This is the meaning of Ida's double-opening: Static help dynamically locates function addresses and dynamically helps to obtain runtime information statically.
0x05 Ida dynamically modifies memory data and register values
We continue to analyze the self-destruct program password This app, we found that the program will open/proc/[pid]/status this file with fopen (), then use Fgets () and STRSTR () to obtain, so we at Strstr () at the next breakpoint, and then let the hex The view data is synchronized with the R0. Each time you click Continue, we will see the parameters passed in Strstr. Let's stop when the incoming parameter becomes tracerpid:xxxx. Because under normal circumstances, the value of Tracerpid should be 0. But it becomes the debugger's PID when it is debugged.
To prevent the program from discovering that we are debugging, here we need to change the value back to 0. We right-click on the 2 hex view and select Edit. We then enter 30 and 00, and then click "Apply Changes". You can change the tracerpid to 0. You can then bypass this time with the anti-debug detection.
But this program detects tracerpid times very frequently, we want to constantly modify the value of Tracerpid, this method is a bit of a palliative, so we will introduce the patch so file method in the next section to solve this problem.
In addition, in the process of IDA dynamic debugging, the data of the register can be modified dynamically except that the data in memory can be modified. For example, the program executes to CMP R6, #0. Originally R6 value is 0, after comparison, the program will jump to 4082a3fc this address.
But if we were to 4082a1f8 this statement on the PC, we would dynamically change the value of R6 to 0. The program is not going to jump.
You can even modify the value of the PC register to jump to any location you want to jump to, just like the principle of ROP. But remember to pay attention to stack balance and so on.
0x06 Patch so file
In the previous section, we used the analysis to locate the function of sub_130c () which is very likely to do anti-debug detection, and the author opened a new thread, and used a while to continuously execute sub_130c () this function, So it is unrealistic to say that every time we manually modify Tracerpid.
So why don't we give the sub_130c () This function to NOP? In order to prevent NOP error, we first select all the code in the "F5" interface, and then use the "Copy to assembly" function, you can put the C language code into the assembly code.
Here we see that if we want to comment out the sub_130c () function, we just need to comment out the code in this position 000016b8, if we want to comment out the dword_62b0 (3) function, We need to comment out the code in the three locations of the 000016BC-000016C4. Next we select the 000016b8 line and then click Hexview. Hexview will help us to locate the 000016B8 position automatically.
Because ARM does not have a separate NOP instruction. So we use Movs R0,r0 as NOP. The corresponding machine code is "xx A0 E1". So we changed the content of "FF FF EB" to "xx A0 E1".
We'll go back to the "F5" interface and we'll see that the sub_130c () function is gone.
Finally we click "Edit->plugins->modifyfile", then we can save the new so file. We overwrite the so file in the original apk and then re-sign it.
This time we run the program and then use IDA to load it, and the app doesn't flash back, which means we've made a patch. So we first place the breakpoint at "Java_com_yaotong_crackme_mainactivity_securitycheck". Then enter a password in the app and click the "Enter Password" button on the app.
The program pauses at "Java_com_yaotong_crackme_mainactivity_securitycheck". We can see the C language of disassembly by pressing "P" and then "F5". The unk_4005228c here is a pointer to a pointer that holds the password string.
Because it is a pointer pointer, we will first double-click into this address.
Then, at this address, press three "D" to convert the data format from the character to the pointer form.
Then we can double-click into this address to see the last flag. The answer is "aiyou,bucuoo."
In this problem we just use a very simple patch so technique, in the actual combat we can not only NOP, we can also change the conditional judgment statement, such as "BNE" into "BEQ". We can even modify the jump address, such as directly to the program B to an address to execute, so that there is no need for a single NOP a lot of statements. Note that the jump instruction in arm is calculated based on the relative address, so you have to calculate the relative jump value based on the current instruction address and the destination address.
For example, 00001bcc:beq loc_1c28 corresponding assembly code for "0A".
0x0a represents beq, "15 00 00" Represents the relative address of the jump, because the value of the PC in arm is the address of the next two (next) instruction of the current instruction, so we need to add the 0x15 to 2. You can then calculate the last address to jump to: (0x15 + 0x2) + 0X1BCC = 0x1c28. The results of Ida disassembly also verify that the result is beq loc_1c28.
Next we want to modify the assembly code to 00001bcc:bne loc_1c2c. Just change "0A" to "1 a" and "15" into "16".
0x0a represents beq, "15 00 00" Represents the relative address of the jump, because the value of the PC in arm is the address of the next two (next) instruction of the current instruction, so we need to add the 0x15 to 2. You can then calculate the last address to jump to: (0x15 + 0x2) + 0X1BCC = 0x1c28. The results of Ida disassembly also verify that the result is beq loc_1c28.
Next we want to modify the assembly code to 00001bcc:bne loc_1c2c. Just change "0A" to "1 a" and "15" into "16".
0X07 Kill Debugging Tips
This technique is qever in the MSC report on pseudo-problem solving. With kill we can suspend the program, then use Ida to mount it, get useful information, and then use Kill to restore the program to work. We're still taking the self-destruct code. This application example is implemented as follows:
1 First use PS to get the PID of the running app.
2 then use the KILL-19 [PID] to hang up the app.
3 then we used Ida attach on this app. Since the entire process is suspended, the app has not been flashed since the IDA was mounted. Then you can find the answer in memory.
4 If you want to restore the app to run, you need to exit Ida and then use KILL-18 [PID].
0x08 dump Dex file in memory
In today's mobile security environment, the program Packers have become commonplace, if not shelling can not be able to crack the rhythm of the world. Zjdroid as a universal shell is very useful, but when the author publicly released the project has been a variety of Packers, such as the preemption of Zjdroid broadcast receiver so that zjdroid can not receive commands. We will also introduce the universal shell of another architecture in the article "The passionate Ring of seven weapons-customized DVM" in the "Android Dynamic commissioning". But tools are tools that may be targeted by similar zjdroid when we release them. So, manual shelling of this skill still needs to be studied. In this section we will describe the most basic memory dump process. We'll cover more tips in the next article.
Here we take the apk300 in alictf2014 as an example to introduce the basic flow of Ida de-shell. First, we use the technique of debugging Jni_onload to suspend the program before it runs:
ADB shell am start-d-n com.ali.tg.testapp/. Mainactivity
! [Enter image description here] [59]
Then the next breakpoint in the Dvmdexfileopenpartial function in libdvm.so:
Then we click Continue to run, the program will be paused at dvmdexfileopenpartial () This function, R0 register to point to the address is the Dex file in memory address, R1 Register is the size of the Dex file:
Then we can use Ida's script command to dump the Dex file in memory.
123456789 |
static main(
void
)
{
auto fp, begin, end, dexbyte;
fp =
fopen
(
"C:\\dump.dex"
,
"wb"
);
begin = r0;
end = r0 + r1;
for ( dexbyte = begin; dexbyte < end; dexbyte ++ )
fputc
(Byte(dexbyte), fp);
}
|
After we dump the Dex file, we can use Baksmali to decompile the Dex file.
Because the process is a bit cumbersome, I recorded a dump Dex file video on my github, interested classmates can go to download to watch.
Of course, this is just the simplest way of shelling, and many high-level shells dynamically modify Dex's structure, such as pointing Codeoffset to other addresses in memory, so that the Dex file you dump is actually incomplete because the code snippet is stored elsewhere in memory. But you don't have to worry, we'll introduce a very simple solution in the next article, so please look forward to it.
0x09 function rewrite functions override
Sometimes we want to extract the logic of a function in the app and recompile an executable file with GCC, for example, if we want to write a keygen, we need to extract the logic of the app's key generation. However, the direct compilation of C language after Ida "F5" often has many errors, such as undefined macros, undefined declarations, and so on. This is because these macros are in one of Ida's header files. It defines all of Ida's custom macros and declarations, such as the frequently seen Byten () macros:
123 |
#define BYTEn(x, n) (*((_BYTE*)&(x)+n)) #define BYTE1(x) BYTEn(x, 1) // byte 1 (counting from 0) #define BYTE2(x) BYTEn(x, 2) |
After adding this "defs.h" header file, you can compile the C language of Ida "F5" normally.
In addition, we can create a NDK project ourselves, and then write ourselves a so or elf using Dlopen () and Dlsym () to invoke the function in the target so. For example, if we want to invoke the Dvmgetcurrentjnimethod () function in libdvm.so, we can write this in our NDK project:
12345 |
typedef void * (*dvmGetCurrentJNIMethod_func)(); dvmGetCurrentJNIMethod_func dvmGetCurrentJNIMethod_fnPtr; dvm_hand= dlopen( "libdvm.so" , RTLD_NOW); dvmGetCurrentJNIMethod_fnPtr =dlsym(dvm_hand, "_Z22dvmGetCurrentJNIMethodv" ); dvmGetCurrentJNIMethod_fnPtr(); |
0x10 Summary
Still, there is still no guarantee that this article will cover all aspects of Ida debugging, because Ida is so profound. Crossing can continue to study in depth if interested. In addition, all the code and tools mentioned in the article can be downloaded to my github address:
Https://github.com/zhengmin1989/TheSevenWeapons
0X06 Reference Articles
- MSC Problem Solving report http://bbs.pediy.com/showthread.php?t=197235
- Pseudo- MSC Problem Solving report http://bbs.pediy.com/showthread.php?p=1349632
[Reprint] Android dynamic debug seven kinds of weapons peacock feather-Ida