puahad and popad#
Packaged program: unpackme.aspack.2.2
, can be searched on the internet.
Open with IDA, manually load the program, and uncheck create input segment.
Entry of the packed program
In the above image, the first instruction is pushad
, which will push the values of all general-purpose registers onto the stack.
pushad
saves the values of all registers onto the stack in the following order.
Order of values pushed by pushad
popad
is the opposite operation of pushad
, it retrieves the values from the stack and saves them to the registers in the following order.
In most simple packers, the pushad
instruction is used to save the initial state of the registers, and then before jumping to the OEP
to execute the original code in memory, POPAD
is used to restore the initial state of the registers. According to this rule, the pushad-popad method
can easily find the OEP
.
Debugging with idapython#
What is the pushad-popad method
?
Use a debugger to run the program. There are two ways to run the debugger: the first is through menu bar - debugger - select debugger, choose local windows debugger
, the second way is to use Python to run the debugger, this time we will use the second method to run the debugger.
- Import the idc module with
import idc
, typeidc.load
and then press the tab key to autocomplete, findidc.load_debugger
, the parameter isidc.load_debugger("win32",0) //1 for remote debugging
, returning True indicates success.
Load load_debugger and set parameters
At this point, the local windows debugger
has been loaded.
Set a breakpoint after pusha
Set a breakpoint after pusha
The pushad-popad method
involves finding the location on the stack where the register values are saved just before the instruction following pushad
, then setting a breakpoint at that location. After the program decrypts the original code and jumps to the OEP, it will restore the initial values of the registers through popad
, then trigger the breakpoint to pause execution, thus determining the location of the OEP.
Press F2 to set a breakpoint on the instruction following pusha, so that the debugging will pause after executing pusha. (pusha is similar to pushad)
If you want to set a breakpoint using Python, you can use the following statement:
idaapi.add_bpt(0x46b002,0,idc.BPT_SOFT)
idaapi.add_bpt(0x46b002,0,0)
- The first parameter is the breakpoint address
- The second parameter is the length of the breakpoint
- The third parameter is the type of breakpoint (software breakpoint BPT_SOFT or 0)
After selecting the debugger and setting the first breakpoint, you now need to start the debugger to run the program and pause at that breakpoint. Press F9 or use the Python statement to run the program.
idc.StartDebugger("","","");
In higher versions (ida 7.7), the above command will report an error, and you need to use the following command.
idc.start_process("","","")
After executing the above statement, the debugger will run to the previously set breakpoint, which is at 0x46b002
.
Running to breakpoint 0x46b002
In the image below, the values in the stack view are the register values saved by pushad, and a breakpoint can be set on the first line.
In this debugging session, the breakpoint is at 0x19ff54, which is the location in the ESP execution stack.
Click the small arrow on the right side of ESP to jump to the corresponding window at that address (this is the assembly window).
Press F2 to set a breakpoint here, changing it to trigger on read and write instead of on execution.
If the window does not appear, you can open the menu Debugger - Breakpoints - Breakpoint List, then right-click to select Edit Settings.
If you want to set a breakpoint using Python, you can install it as follows:
idaapi.add_bpt(0x19ff54,1,3)
The breakpoint list is as follows
Breakpoint list
In the above code, the first parameter indicates the breakpoint address, the second parameter indicates the size of the breakpoint, and the third parameter indicates the type, where 3 represents read-write access (reading and writing). Executing this statement is the same as manually setting the breakpoint.
BPT_EXEC = 0,
BPT_WRITE = 1,
BPT_RDWR = 3,
BPT_SOFT = 4,
Parameters for setting breakpoint types
In the breakpoint list, right-click the first breakpoint set and select DISABLE to disable it, or use the following Python statement.
idaapi.enable_bpt(0x46b002,0)
- The first parameter is the breakpoint address
- The second parameter, if 1, enables the breakpoint; if 0, disables the breakpoint
0x46b002 marked in green indicates disabled, while the breakpoint on the stack is red indicating enabled.
Press F9 to continue debugging or enter the following Python statement
idaapi.continue_process()
In the image below, debugging is interrupted after the popad
instruction retrieves the initial values of the registers, and the program will jump to the OEP, which is 0x4271b0
, because push ret
is similar to jmp
.
popa instruction
Continue stepping through
to execute to OEP.
Now that the OEP entry has been found, we can reanalyze the executable file and identify functions.
Reanalyze the program
Dumping with idapython#
After finding the OEP, the next step is to perform a dump, which requires the base address of the file and the highest address of the last section of the executable file.
From the image above, the base address is 0x40000, and the highest address is 0x46e000.
The dump script is as follows:
import idaapi
import idc
import struct
start_ea = 0x400000
end_ea = 0x46e000
step = 4 # Each address occupies 4 bytes of data
file_path = "dump.bin"
with open(file_path, "wb") as f:
for ea in range(start_ea, end_ea, step):
# Read 4 bytes of data from the specified address and convert to little-endian byte order
bin_data = struct.pack("<L", idaapi.get_32bit(ea))
f.write(bin_data)
Load the above Python script through File - Script File, and after execution, a dump.bin file will be generated in the current directory. Change its extension to exe.
Open it with peeditor, go to the section view, right-click on all sections, and select dumpfixer.
At this point, the icon has been fixed.
After fixing the icon, use Scylla 0.98 to attach the process to the packed file being debugged, currently executing at the OEP entry.
Load this process.
Enter the OEP as 004271B0, click IAT Autosearch and Get Imports.
Clicking show invalid will reveal an unrecognized API, and attempts to auto-fix will fail, requiring manual repair.
API at 0x460818
In the image above, 0x460818 is the API function of the first valid section, and there are more unrecognized API addresses above it.
Check what the first unrecognized API at 0x46080c contains, press D to change the data type and reorganize the bytes, as shown in the image below:
The content here does not point to any valid address, and pressing CTRL+X shows no references.
For real API functions, there should be reference information indicating where they are called.
Therefore, since these are not API functions, they can be deleted.
Click the clear button, then click IAT Autosearch, and select No in the pop-up window.
Now the IAT starting point is at 0x460810, then click Get Import to find all APIs.
Click Fix Dump and dump the file, selecting the previously exported file.
Finally, the unpacked program runs normally.
At this point, the entire unpacking process of the program is complete.