Talking about the debugging of the shelled ELF (that is, the android so file), elfandroid
This article only discusses how to debug the shelled ELF file, including the use of Debugging techniques and solutions to possible problems during debugging, DEX does not contain how to restore the reinforcement. This article will take a shell program and a reinforcement as the target.
1. Introduction to ELF format
ELF stands for Executable and Linkable Format, which is an Executable file Format in Linux.
This file format is the same as that in WINDOWS. It can be divided into two types:
1. Executable File, which corresponds to the PE subtype: EXE
2. Shared Object File (Shared Object File), suffixed with. so, corresponding to PE subtype: DLL
Ii. ELF File Loading Process
1. Load the required image data through Section Header or Program Header
Unlike windows pe, some ELF file data is not loaded into the memory image.
2. load so need lib and SYM
Import_table similar to PE
3. Execute relocation (if any)
Reloc_table similar to PE
4. Run the INIT_ARRAY or INIT segments (if any, the address in the array is not equal to 0 xffffffff, and 0 indicates the end)
Similar to the TLS of PE, the known ELF shelled ELF and shell code appear in these two sections.
TLS in PE is often found in shells, such as Vmprotect and Execryptor.
5. Execute the entry point code (if any)
All the above loading process code is included in linker. so. For details, see Android source code or reverse linker. so.
Iii. Introduction to ARM CPU
1. Instruction Set Overview
ARMCPU adopts the Reduced Instruction Set architecture (X86 is a CISC, Complex Instruction Set) and has a long instruction set. Compared with the CISC architecture, ARMCPU saves power and improves execution efficiency.
2. Three ARM Instruction Sets
ARM (4-byte), THUMB (2-byte), and THUMB2 (4-byte ). The three instruction sets can be switched in the same execution program. The switching principle is as follows:
ARM <-> THUMB, THUMB <-> ARM (the highest bit of the PC determines the instruction set type: 1 is THUMB; 0 is ARM)
THUMB <-> THUMB2 (determined by the 27-31 bits)
Thumb2 is actually an extension of thumb. It aims to complete multiple 2-byte commands for a 4-byte command.
3. Registers
General registers: r0-r15
Special Registers: r13 = SP (stack address), r14 = LR (function return address), r15 = PC (current instruction stream address)
It also includes CPSR, APSR, floating point and other registers. For details, refer to the ARM Instruction Set manual.
4. shell SO debugging
1. ELF code execution sequence
As described above, the SO shell code of shelling is in the INIT_ARRAY and INIT segments.
Let's first look at the SO after being shelled. in linux, run the readelf-a command to view ELF information:
In ELF-HEADER, we can see an Entry with the address 0x22a8.
The INIT_ARRAY array is displayed in the dynamic section. The array address is 0 × 21000, And the size is 12 BYTES.
Use IDA to check the array content:
As mentioned above,-1 is invalid, and 0 indicates the end. The INIT valid address is only 0 × 2418.
That is to say, after the SO is loaded, it will start from the address 0 × 2418 and then execute the Entry after the execution is complete.
Let's take a look at the reinforced ELF information:
In ELF-HEADER, we can see that there is an Entry with the address 0 × 3860:
When IDA opens a reinforced file, it will prompt an error and cannot open it. The following explains why in Anti-Anti Debugger.
The INIT_ARRAY array is displayed in the dynamic section. The array address is 0x28CA4 and the size is 8 BYTES.
We also see the INIT segment with the address 0 × 11401.
Here, we will summarize the execution sequence:
According to the linker code, when both the INIT and INIT_ARRAY segments exist, run the INIT segment first and then the INIT_ARRAY segment. Otherwise, run the corresponding function separately and finally execute the ENTRY:
2. Prepare SO_LOADER yourself
Debugging SO and PE_DLL is actually the same. Both require a host process. here we need to write a SO_LOADER. Refer to the code below and compile it through NDK. (The code was provided by Wang Chen at an early stage ):
3. Prepare the environment
Here I use IDA6.6 as the debugger.
Step 1: copy the debugger to your Android phone:
Command: adb push android_server/data/local/tmp/and
Why do we change the name of android_server to "and ~~~, In fact, this is to prevent the Debugger from being detected. I will detail this part in Anti-Anti Debugger later.
Step 2: Start the debugger
Enter the/data/local/tmp/directory and start the debugger.
Step 3: redirect the debugging port
Adb forward tcp: 23946 tcp: 23946
So far, the mobile phone end has been set up. Let's see how to set it in IDA.
IDA loads our own so_loader. At 854C, press the F2 breakpoint:
Select Debuger-Select Debugger in the menu bar and Select Remote Arm linux/Andoid debugger
Click OK and run F9:
In configuration, enter 127.0.0.1 in Hostname and click OK.
If this file does not exist on your mobile phone, you will be prompted to COPY it. Click OK. If this file exists, the following options will appear. You can select USE FOUND, if the program you want to debug has been modified, select copy new to overwrite a NEW one.
Then all the way to OK, the debugging status will appear ~~, The current PC is the breakpoint we set in F2 just now:
4. How to break the SO INIT_ARRAY and INIT segments
As mentioned above, SO is loaded in linker. so. What we need to do is to set the breakpoint in linker. so.
Find the code first. IDA opens linker. so and finds it in the string window.
Call_constructors_recursive, double-click and view the reference:
Double-click the second reference, and then find blx r3 (call the init segment), B. w xxxxxxxx (call the init_array segment ):
Now we have found the address 0x54d0 and 0x3af0, and return to the IDA we just debugged.
Choose debugger-Debugger windows-module list on the menu bar to open the process module list:
The base of linker. so is 40002000, which corresponds to the following two addresses:
0 × 40002000 + 0x54d0 = 0x400074d0
0x40002000 + 0x3af0 = 0x40005af0
GO in the IDA View-PC window:
At 0x74d0, press the C key to turn it into code. Strange, why didn't it respond !! The following prompt is displayed in the bottom output window:
Here is a very important question. As I mentioned above, the program to be debugged can be switched between the three instruction sets, at this time, IDA does not know whether the current Code address is ARM or THUMB. In this case, we need to compare it with static, or you are absolutely familiar with the instruction set, you can see the byte code to know which instruction set is used:
In static mode, IDA obviously gives a 2-byte command, which must be THUMB. We need to change the current address to THUMB.
Method: press ALT + G on the keyboard to call out the window:
T, DS don't worry about it. We just need to change the VALUE to 1, which is the THUMB instruction set, to 1 and click OK.
CODE16 appeared on the original address, and then we went to C again:
After C, the code will appear !!! This is how ARM and THUMB switch. Remember:
Let's look at another address, 0x400a5af0, and try again in the same way:
The problem comes again. It's strange. Why isn't the command below ?! This is IDA's BUG. In analyticdb 6.6, the parsing of the THUMB2 command in the debugging status is problematic... but it doesn't matter. Let's look at it:
This code is the most important. Execute the address function in every init_array in blx r2.
So far, we have finished talking about how to break the INIT and INIT_ARRAY segments. Let's debug the rest!
5. Anti Debugger
1. Anti IDA
In fact, this problem is caused by the inconsistency between ida elf and linker ELF, and IDA is more rigorous.
Use ida to open a reinforced so, and the prompt is:
This prompt indicates that a data description is invalid. Let's see which one is.
SHT is about Section Table. Let's take a look at the ELF header data as follows:
Shoff is the value. Let's use the hexadecimal editor to see it:
All are 0. Obviously there is a problem here. First, we need to clear the value 0 and save the file.
Re-load, there are still problems, the prompt is as follows:
After debugging IDA's ELF plug-in, this error occurs when the physical offset in the program header is greater than the file size.
Apparently, p_offset of the first group of data in the Program Header exceeds the file size. According to the ELF structure, the data offset is located and changed to 0. IDA is loaded successfully.
2. Anti Debugger
Debug the shelling program and summarize the methods used.
Method 1: Check the file name of the parent process
Call getppid to obtain the id of the parent process. open ("/proc/ppid/cmdline") to obtain the name of the parent process and check the name of the common debugger. This is the name of the above COPY file, why do we need to change android_server to a random file name.
Countermeasure: Modify the return value of getppid. Just give it a usable one.
In fact, there are other ways to get the ppid, such as open ("/proc/pid/status"), read the handle content, and find the ppid in it.
Method 2: exception traps
Similar to WINDOWS, you can set a trap to detect the debugger.
Countermeasure: by default, all traps of IDA are handled by the debugger, so we need to modify the corresponding settings. Select debugger-debugger options from the menu, and click edit exceptions.
On trap, right-click and edit it as follows:
Of course, there are still many ways to check the debugger. You can refer to the recruitment and splitting methods. I will not detail them here.