Shelled Pediy unknown encrypted shell

1, Simple analysis

1. Browse roughly to find the OEP address, fill in the IAT address, obtain the API address, and preliminarily write the general script
①OEP

② Populate IAT address

③ Get API address

Fill the address into the script for testing

// 1. Define variables
MOV dwOEP,0047148B 
MOV dwGetAPI,001E1914 
MOV dwWriteIAT,001E0897 

// 2. Clean the environment
BC    // Clear all software breakpoints
BPHWC // Clear all hardware breakpoints
BPMC  // Clear memory breakpoints

// 3. Set breakpoints
BPHWS dwOEP, "x" //An interrupt is generated when executing to this address
BPHWS dwGetAPI, "x" //An interrupt is generated when executing to this address
BPHWS dwWriteIAT, "x" //An interrupt is generated when executing to this address

// 4. Circulation
LOOP0:
  RUN // F9  
  CMP dwGetAPI,eip  
  JNZ CASE1  
  MOV dwTMP,eax
  JMP LOOP0
CASE1:
  CMP dwWriteIAT,eip    
  JNZ CASE2
  MOV [edx],dwTMP       //MOV DWORD PTR DS:[EDX],EAX
  JMP LOOP0
CASE2:
  CMP dwOEP,eip  
  JNZ LOOP0  
  MSG "OEP Arrived"

2. Run the script again. It is found that the addresses of API acquisition and IAT filling are invalid, indicating that the code address has changed.
Generally speaking, there are two cases where the address is random. One is the random base address, and the other is that the code is located in the applied memory space. In this case, the solution is to find the code base address, calculate the offset, and break points at the code according to the offset.
Obviously, the random address of this place is caused by applying for memory.

Therefore, you can lower the breakpoint at virtuallalloc. After dynamic debugging, you can find that there are many broken positions at virtuallalloc. After the first stack backtracking, the code address is in the module of the program. It is speculated that the memory space applied in this place is to repair the base address of IAT code. Offset the previous code, subtract the base address and add the offset. The code is the same as before, So this place is the place to get the code base address.



After analysis, the script is modified as follows:

// 1. Define variables
MOV dwOEP,0047148B 
MOV dwGetAPI,1914 
MOV dwWriteIAT,0897
MOV dwBase,0047A37F  

// 2. Clean the environment
BC    // Clear all software breakpoints
BPHWC // Clear all hardware breakpoints
BPMC  // Clear memory breakpoints

// 3. Set breakpoints
BPHWS dwOEP, "x" //An interrupt is generated when executing to this address
BPHWS dwBase, "x" //An interrupt is generated when executing to this address

// 4. Circulation
LOOP0:
  RUN // F9     
  CMP dwBase,eip  
  JNZ CASE0    
  ADD dwGetAPI,eax      // Add base address
  ADD dwWriteIAT,eax    // Add base address              
  BPHWS dwGetAPI, "x"   // Lower breakpoint
  BPHWS dwWriteIAT, "x" // Lower breakpoint     
  BPHWC dwBase          // Clear hardware breakpoints
  JMP LOOP0    
CASE0:
  CMP dwGetAPI,eip  
  JNZ CASE1  
  MOV dwTMP,eax
  JMP LOOP0
CASE1:
  CMP dwWriteIAT,eip    
  JNZ CASE2
  MOV [edx],dwTMP       //MOV DWORD PTR DS:[EDX],EAX
  JMP LOOP0
CASE2:
  CMP dwOEP,eip  
  JNZ LOOP0  
  MSG "OEP Arrived"

3. Other methods
First obtain the following base address, and then execute the hardware breakpoint under the known address of filling IAT and obtaining API

After the program is disconnected, it is found that the API address is saved in EAX

Set RUN tracking to pause when EIP equals IAT filling, and Ctrl+F7 will automatically enter

View RUN trace

Focus on the data beginning with 7, which may be an AIP address

Up to this location, the API address is still saved in EDX

Decryption idea: save the API address to an unused register, and then fill the IAT directly into the memory pointed to by EDX when filling the IAT

Modification code:

001E14DC       8BDA        MOV EBX,EDX          user32.BeginPaint
001E14DE        90             NOP
001E14DF        90             NOP

001E0895       891A        MOV DWORD PTR DS:[EDX],EBX

Run under breakpoint



DUMP file and fix it with impec

Convert manual operations to scripts

// 1. Define variables
MOV dwOEP,0047148B
MOV dwPatch1,14DC 
MOV dwPatch2,0895 
MOV dwBase,0047A37F  

// 2. Clean the environment
BC    // Clear all software breakpoints
BPHWC // Clear all hardware breakpoints
BPMC  // Clear memory breakpoints

// 3. Set breakpoints
BPHWS dwOEP, "x" //An interrupt is generated when executing to this address
BPHWS dwBase, "x" //An interrupt is generated when executing to this address

// 4. Circulation
LOOP0:
  RUN // F9     
  CMP dwBase,eip  
  JNZ CASE0    
  ADD dwPatch1,eax      // Add base address
  ADD dwPatch2,eax    // Add base address              
  BPHWS dwPatch1, "x"   // Lower breakpoint  
  BPHWC dwBase          // Clear hardware breakpoints
  JMP LOOP0    
CASE0:
  CMP dwPatch1,eip  
  JNZ CASE1  
  FILL dwPatch1,4,90           //NOP 4 bytes    
  ASM dwPatch1,"MOV EBX,EDX" //Modify the current instruction to MOV DWORD PTR DS:[EDI],EAX
  ASM dwPatch2,"MOV DWORD PTR DS:[EDX],EBX"    
  BPHWC dwPatch1
  JMP LOOP0
CASE1:
  CMP dwOEP,eip  
  JNZ LOOP0  
  MSG "OEP Arrived"

2, Single step tracking

Load the program into OD and check the program entry first. It is found that there are standard push instructions (pushad/pushfd), so ESP law is used to find OEP

Use the ESP law to step to 470A036, break the ESP, and then run the program

The program is broken under popfd, and then it can reach OEP in a few steps

After reaching OEP, it can be seen from experience that this should be VC6 0 or easy language program. Looking down, you can find that the function address in the first called function CALL[XXX] is encrypted, that is, IAT is encrypted

The general way to decrypt IAT is to write breakpoints in the hardware above and below the IAT function table.
Observe the function call of OEP attachment. According to experience, VC6 The first function called by the 0 program should be GetVersion, but now you can see a random function address, such as applying for a memory address

F7 enters, continues to single track the code in the 275039 address, it can be found that the function address of the original IAT is stored in an address, the code gets the function address of GetVersion after calculating the address, and then calls the GetVersion function.


Encryption function of the program, GetVersion

00275039      68 0A000080     PUSH 0x8000000A      ;push A random number, occupied, will be stored at that time API address
0027503E      53              PUSH EBX             ;Save register environment
0027503F      57              PUSH EDI             ;Save register environment  
00275040      E8 00000000     CALL 00275045
00275045      5B              POP EBX
00275046      81EB 0C104000   SUB EBX,0x40100C    ;
0027504C      81C3 24104000   ADD EBX,0x401024    ;SUB and ADD Can be resolved into an instruction
00275052      8BFB            MOV EDI,EBX         ; EDI=EBX=0027505D
00275054      8B3F            MOV EDI,DWORD PTR DS:[EDI] ; from EDI Get in API address
00275056      897C24 08       MOV DWORD PTR SS:[ESP+0x8],EDI  ; take API The address is stored in the stack and the placeholder address just now is modified
0027505A      5F              POP EDI             ;Recovery register
0027505B      5B              POP EBX             ;Recovery register
0027505C      C3              RETN                ;Return to api address
0027505D      C744F6 76 00000>MOV DWORD PTR DS:[ESI+ESI*8+0x76],>    ;Stored API address

Some instructions can be resolved, such as
00275046      81EB 0C104000   SUB EBX,0x40100C
0027504C      81C3 24104000   ADD EBX,0x401024
 After it is resolved ADD EBX,0x18

The approximate steps to generate the encrypted IAT function are as follows:
1. Obtain the original IAT function address and store it in a certain location
  use loadlibrary A / W, GetProcAddress function
2. Apply for space and construct a new IAT function
  use VirtualAlloc to apply for space and copy the code
3. Calculate the encrypted value according to the original IAT function address and hide the real address
  calculate the x value in the code similar to the GetVersion function
    sub edx,x;
    add ebc,x;
4. Write the new IAT function address to the IAT
  fill address to IAT

Encryption function

00274986    / EB 73           JMP SHORT 002749FB
002749FB      4C              DEC ESP
002749FC      E8 DBFFFFFF     CALL 002749DC
002749DC      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
002749E0      4C              DEC ESP
002749E1      EB 51           JMP SHORT 00274A34
00274A34      4C              DEC ESP
00274A35      E8 D1FFFFFF     CALL 00274A0B
00274A0B      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
00274A0F      4C              DEC ESP
00274A10    ^ EB E4           JMP SHORT 002749F6
002749F6      83EC 04         SUB ESP, 0x4
002749F9      EB 2A           JMP SHORT 00274A25
00274A25      50              PUSH EAX
00274A26    ^ EB DD           JMP SHORT 00274A05
00274A05      51              PUSH ECX
00274A06      E8 12000000     CALL 00274A1D
00274A1D      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
00274A21      8BC8            MOV ECX, EAX
00274A23    ^ EB B3           JMP SHORT 002749D8
002749D8      03C4            ADD EAX, ESP
002749DA      EB 36           JMP SHORT 00274A12
00274A12      2BC1            SUB EAX, ECX
00274A14      E8 0F000000     CALL 00274A28
00274A28      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
00274A2C      8958 08         MOV DWORD PTR DS : [EAX + 0x8] , EBX
00274A2F    ^ E9 6EFFFFFF     JMP 002749A2
002749A2      59              POP ECX
002749A3      E9 92000000     JMP 00274A3A
00274A3A      58              POP EAX
00274A3B      E8 85FFFFFF     CALL 002749C5
002749C5      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
002749C9      6A 00           PUSH 0x0
002749CB    ^ EB D2           JMP SHORT 0027499F
0027499F      50              PUSH EAX
002749A0      EB 50           JMP SHORT 002749F2
002749F2      03C4            ADD EAX, ESP
002749F4    ^ EB 9B           JMP SHORT 00274991
00274991      2B0424          SUB EAX, DWORD PTR SS : [ESP]
00274994      E8 EFFFFFFF     CALL 00274988
00274988      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
0027498C      8978 04         MOV DWORD PTR DS : [EAX + 0x4] , EDI
0027498F      EB 41           JMP SHORT 002749D2
002749D2      58              POP EAX
002749D3      E8 0B000000     CALL 002749E3
002749E3      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
002749E7      E8 00000000     CALL 002749EC
002749EC      5B              POP EBX
002749ED      E8 C7FFFFFF     CALL 002749B9
002749B9      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
002749BD      81EB 66104000   SUB EBX, 0x401066
002749C3      EB 54           JMP SHORT 00274A19
00274A19      8BFB            MOV EDI, EBX
00274A1B    ^ EB 8B           JMP SHORT 002749A8
002749A8      81C7 C0104000   ADD EDI, 0x4010C0
002749AE      EB 51           JMP SHORT 00274A01
00274A01      8B3F            MOV EDI, DWORD PTR DS : [EDI]
00274A03    ^ EB AB           JMP SHORT 002749B0
002749B0      897C24 08       MOV DWORD PTR SS : [ESP + 0x8] , EDI
002749B4      E9 87000000     JMP 00274A40
00274A40      5F              POP EDI
00274A41    ^ E9 53FFFFFF     JMP 00274999
00274999      5B              POP EBX
0027499A      E8 2E000000     CALL 002E49CD
002749CD      8D6424 04       LEA ESP, DWORD PTR SS : [ESP + 0x4]
002749D1      C3              RETN

According to the speculation and the operation of the IAT of the shell, we can roughly deduce that whether the IAT is encrypted or not, the shell will actually fill the IAT, but the encrypted IAT will fill the encrypted function.
Therefore, as long as the IAT function address before encryption and the place where IAT is filled can be found, and the function address before encryption can be written when IAT is filled, the IAT is equivalent to decryption.
Therefore, the next two key points we analyze are the place where IAT is written and the place where IAT function address appears before encryption. These two key points can be cracked and decrypted.

To sum up, let's start with the place written in IAT.
First, at the original OEP, write the breakpoint under the IAT of the GetVersion function and re run the program.

The program is disconnected and the position of filling IAT is found

Generally speaking, the place where the IAT is written should be a loop, which should include operations such as loading modules and obtaining function addresses.
Therefore, we can set the software breakpoint on the LoadLiibraryA/W and GetProcAddress functions, and the hardware execution breakpoint on the next line of code written to the IAT.
Note: the code in the shell is usually decompressed and decrypted, and the general address is unreliable

Continue the one-step tracking analysis. It is found that the code calculates an address and obtains a number of 4 bytes from this address, which is irregular. Generally speaking, this value is the hash value.

Continue the one-step tracking analysis and find that it has obtained the base address of the Kernel32 module

Continue the single step tracking analysis and access the data catalog table

Continuing the one-step tracking analysis, it is found that the exported function string is obtained. Combined with the context analysis, it is speculated that the code is obtaining the exported function string, calculating the hash value of the string, and then comparing it with the hash value just obtained.

Continue tracking and find that the program loads each byte of the function string and calculates it

Program hash function for function string

00311CB2      33D2              XOR EDX,EDX
00311CB4     /EB 1E             JMP SHORT 00311CD4
00311CD4      FC                CLD
00311CD5    ^\EB AA             JMP SHORT 00311C81
00311C81      AC                LODS BYTE PTR DS:[ESI]
00311C82      E8 13000000       CALL 00311C9A
00311C9A      8D6424 04         LEA ESP,DWORD PTR SS:[ESP+0x4]
00311C9E      84C0              TEST AL,AL
00311CA0      E8 EDFFFFFF       CALL 00311C92
00311C92      8D6424 04         LEA ESP,DWORD PTR SS:[ESP+0x4]
00311C96      74 33             JE SHORT 00311CCB
00311C98      EB 29             JMP SHORT 00311CC3
00311CC3      C1C2 03           ROL EDX,0x3
00311CC6      E8 0C000000       CALL 00311CD7
00311CD7      8D6424 04         LEA ESP,DWORD PTR SS:[ESP+0x4]
00311CDB      32D0              XOR DL,AL
00311CDD    ^ EB AF             JMP SHORT 00311C8E
00311C8E    ^\EB F1             JMP SHORT 00311C81
 Simplify to
START:
   LODS BYTE PTR DS:[ESI]
   TEST AL,AL
   JE SHORT 00311CCB
   ROL EDX,0x3
   XOR DL,AL
   JMP START
 After asking, hash Values are saved in EDX in

When the hash value is calculated, it will be compared

Run the breakpoint directly at the position of 1A28, that is, when the hash is equal

Continue the one-step tracking analysis and find the place to obtain the function address

Continue the one-step tracking analysis. It is found that the function address is processed. Memcpy is used to copy a piece of code, and the function address is written into the code. The new function address is the first address of memcpy copy, which is written into IAT.



So far, we have known the operation process of the program when writing an IAT function address, which can be summarized as the following steps.
  1. Get the pre calculated hash value
  2. Circularly obtain the name of the exported function in the module currently being obtained, calculate the hash value, and compare it with the pre stored. If it fails, continue to obtain circularly
  3. If correct, get the address of the exported function
  4. Copy the pre stored code to the buffer and write the address of the exported function to the buffer
  5. Write the first address of the buffer to the IAT to complete the operation of filling the IAT

How to decrypt IAT? Starting from the function address here, if we get the original function address and the original function address is still saved in the register when writing the IAT, it will become easy to decrypt the IAT. If the code is executed linearly, we only need to change the jump to complete it, but now the code confusion is relatively high, and it is difficult to find the law. Although it should be possible to change the jump as long as we are patient enough. After carefully tracking the code, we can find that the function address is initially saved in EAX, then saved in EDX, and then EDX is modified to IAT address, EAX is modified to an encrypted address. In this process, as long as we can make EAX the last function address.

After analysis, modify two codes.
First code:

The modification here is to save the function address to EBX, because EBX seems to be of no practical use

Second code:

The modification here is to save the function address to EAX, because the code that fills in IAT last uses EAX


It can also be changed into a script. Similar to method 3 above, you can change the address and part of the code

  FILL dwPatch1,4,90           //NOP 4 bytes    
  ASM dwPatch1,"MOV EBX,EDX" 
  FILL dwPatch2,2,90           //NOP 2 bytes    
  ASM dwPatch2,,"MOV EAX,ECX" 

Try modifying the shell code directly
8D642404895401FC
Search in the memory window and navigate to the address

0047BB6C      8D6424 04      LEA ESP,DWORD PTR SS:[ESP+0x4]
0047BB70      895401 FC      MOV DWORD PTR DS:[ECX+EAX-0x4],EDX                ; user32.BeginPaint

Calculate another address
Address = 0047BB70-(002214DC-00220895)

0047AF29      8902           MOV DWORD PTR DS:[EDX],EAX

(execute breakpoints for hardware at 0047BB70 and 0047AF29 addresses)
Change to

0047BB70 mov ebx,edx
0047AF29 MOV DWORD PTR DS:[EDX],EBX




Also scriptable...

  FILL dwPatch1,4,90           //NOP 4 bytes    
  ASM dwPatch1,"MOV EBX,EDX" 
  ASM dwPatch2,,"MOV DWORD PTR DS:[EDX],EBX" 


Keywords: security

Added by flyersman on Fri, 31 Dec 2021 09:17:36 +0200