puahad and popad#
Packager: unpackme.aspack.2.2
, can be searched online.
Open with IDA, manually load the program, and uncheck create input segment.
Entry of the packager
In the image above, the first instruction is pushad
, which pushes 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 pops 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
?
There are two ways to run the program using a debugger. The first is through menu bar - debugger - select debugger, choose local windows debugger
, and the second 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
, then press the tab key to autocomplete, findidc.load_debugger
, with the parameteridc.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, and 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 before triggering 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, which will be read by the popad instruction later, so a breakpoint can be set on the first line.
In this debugging session, the breakpoint is at 0x19ff54, which is the position in the ESP execution stack.
Click the small arrow on the right side of ESP to jump to that address in the corresponding window (in this case, the assembly window).
Press F2 to set a breakpoint here, changing it to trigger on read and write rather than 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 using Python, you can set the breakpoint 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 you 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
to execute to OEP.
Now that the OEP entry has been found, you can reanalyze the executable file and identify functions.
Reanalyze the program
Using idapython for dumping#
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 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, currently executing at the OEP entry.
Load this process.
Enter the OEP as 004271B0, click IAT Autosearch and Get Imports.
Click show invalid, and you will find an API that could not be recognized. The attempt to auto-fix failed and needs to be manually fixed.
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 the first unrecognized address at 0x46080c to see what it contains, press D to change the data type and reorganize the bytes, as shown below:
The content here does not point to any valid address, and pressing CTRL+X shows no references.
For a real API function, there should be reference information indicating where it is 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, and 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 is complete.