Technical Foundation: Write Shell programs, understand the PE Structure, and X86 command systems.
Development tools: MASM, VC ++
1. We sometimes make a lot of changes to some of the calls when transforming a PE.
Wait, often add a section manually, complete the call transformation in this section, and then return to the original place to continue the execution. Although this method is feasible, the workload is too large and debugging is very inconvenient.
2. Implementation principle of "General and simple method:
The length of the machine code using jmp far address and call far address is the same, and only the first byte is different. The first byte of jmp is 0xE9, the first byte of call is 0xE8, and the last four byte is the same.
This method is: add a small shell to the PE, and modify the call to be transformed in the PE to the call in the call shell, the call in the shell calls the call in the original PE, while the call in the shell is easier to implement function expansion and transformation when writing.
In the call in the shell, we have two ways to expand the function:
(1) extended functions before calling the call in the original PE;
(2) extended functions after calling the call in the original PE;
Extended functions can be implemented directly in Shell call, but are difficult to debug and complex.
A simple and common method is that the new call in this shell is only completed:
1. Load an external patch DLL;
2. call the function in this external patch DLL to complete the expansion and transformation of the call function in the original PE.
3. call the call in the original PE;
Whether to call the call in the original PE first or later, depending on the specific needs. In this way, we can easily write extended functional code, because this external patch DLL can be written in advanced languages and debugging is very simple.
3. Specific implementation of functions in the shell:
(1) first, set several variables in the shell:
(1) Save the variable (DWORD) of each function address of the external DLL ):
PfnExtPatchFun_1 -- pfnExtPatchFun_n, n is too large. For details, we need to transform several functions in PE.
(2) Save the "address difference" variable (DWORD) of the call in the original PE ):
DwOrgCallDifference_1 -- dwOrgCallDifference_n, n is a large number. For details, we need to transform several functions in PE.
DwOrgCallDifference_x = the last four bytes (dual characters) of the original call machine code ).
(3) variable for saving the RVA address of the call in the original PE (DWORD ):
DwOrgCallRVA_1 -- dwOrgCallRVA_n, n is too large, depending on the Transformation of several functions in PE.
(4) Save the hModule variable (DWORD) of the external DLL ):
HExtPatchDll.
;-----------------------------------------------------------------
Example:
For example, the original PE ImageBase = 0x00400000 has the following code (IDA shows ):
.
.
004010CB 50 push eax
004010CC 57 push edi
004010CD E8 CE 08 00 00 call sub_4019A0; call to be transformed. Pay attention to the four bytes after the machine code
004010D2 85 C0 test eax, eax
.
.
Then: dwOrgCallRVA_1 = 004010CD-0x00400000 = 0x000010CD
DwOrgCallDifference_1 = 0x000008CE
;------------------------------------------------------------------
;
(2) write these calls in the shell program. Several functions need to be transformed. There are several calls in the following format:
; **************************************** *************************************
;---------------------------------------------------;
The call in the shell, used to replace the call in the original PE;
;---------------------------------------------------;
Align 4
Wjq_API_SMC_Label_1:
GeneralPEShellCall_1 proc
;{
In the original PE, the call entry parameter is passed to the new call in the Dll through the stack. Note that the EBP should not be changed in this call.
@ NPara1 EQU [ebp + 0ch]; the Call parameter in the original PE can also be obtained through esp
@ NPara2 EQU [ebp + 8 h]
;
Pusha
Call GetPatchDllFunctions; obtain all function addresses in the external DLL and save
Call GetAddressDifference; get the address difference
. If [pfnExtPatchFun_1 + eax]! = 0; If function 1 is implemented, call
Push @ nPara1; Call parameter in the original PE
Push @ nPara2
Call [pfnExtPatchFun_1 + eax]; call the patch call in the external DLL
. Endif
; ------------ Call the original call -------------------------
Call GetAddressDifference; get the address difference
Lea esi, [TempCall_1 + eax]; now call the VA
Sub esi, [Image_Base + eax]; To RVA
Sub esi, [dwOrgCallRVA_1 + eax]; subtract the RVA of the call in the original PE = the address difference between the two calls
;
Mov ebx, [dwOrgCallDifference_1 + eax]; original call xxxxxxxx instruction offset difference
. If ebx <0; negative, the original call is called up
Add ebx, esi
. Else; positive number, originally called down by call
Sub ebx, esi
. Endif
;
Mov [TempCallDifference_1 + eax], ebx; corrected difference value
Popa
; The following is equivalent to a jmp far xxxxxxxx statement, which calls the original call
This method is used here, instead of calling, for general purpose, because some calls in PE are _ stdcall and some are ___ cdecl. If all calls are implemented, it is not very common to consider stack issues.
TempCall_1 db 0E9h
TempCallDifference_1 dd 0
Ret
;}
GeneralPEShellCall_1 endp
;---------------------------------------------------------------------
; Get all function addresses in the external DLL and save
The W_LoadLibraryA and other functions used in this function are implemented by using the shell self-built table import function.
;---------------------------------------------------------------------
GetPatchDllFunctions proc
Pusha
Call GetAddressDifference
Mov edx, eax
. If [hExtPatchDll + edx] = 0; if it has been loaded, return directly.
;
Lea ebx, [pExtPatchDll + edx]
Push edx; save address difference
;--------------------------
Push ebx
Call [W_LoadLibraryA + edx]
;--------------------------
Pop edx; Recovery address difference
Mov [hExtPatchDll + edx], eax
. If eax! = 0
;-------------;
; Function_1;
;-------------;