PE series learning notes 8 actual HOOK program add pop-up window

After learning the structure of PE, try to modify the PE file to realize the function of adding pop-up window to the program

PS: this note does not cover the knowledge points of PE, but focuses on HOOK, disassembly and hard coding. If you don't know much about PE, you can see that the contents related to PE knowledge points are put in the following notes: PE learning note 9: add pop-up window of HOOK program in actual combat (Continued) , you can eat it at ease ( ̄)  ̄) ↗

PE actual combat adds pop-up window to the program

Modification process

To add pop-up windows to the program, the first thing is to understand its modification process

The first thing to modify is the original entry address of the program, and modify it to the address where the pop-up code is located

For the address of the pop-up code, find an area in the PE file that needs to meet the permissions of executable, readable and writable, and then write the pop-up code in this area. Finally, the pop-up code should jump back to the original entry address

The modification process is a very classic HOOK idea, that is, the program executes according to the original process. You modify the code it originally executes, modify it to do what we want to do, and put it back to continue executing the original code after we finish what we want

Figure HOOK modification process

The place that is HOOK is B

Normal process A → B → C

HOOK process A → HOOK B → own code → restore the modified part in B → jump back to the place where B originally wanted to execute → C

Add pop-up window to the program

Pop up code

Since you want to add a pop-up window to the program, you naturally need to know how to display a simple pop-up window through code

Here is a simple pop-up code

#include <Windows.h>
int main() {
    //Call MessageBoxA function
    //Display a window with no owner window, the content is lyl610abc, the title is tips, and contains only one button: OK
    MessageBoxA(0, "lyl610abc", "tips", 0);
    return 0;
}

Operation results

MessageBoxA

The following is the document that introduces the use of MessageBoxA. If you are familiar with MessageBoxA, you can skip it and directly skip to the later view disassembly

Function prototype

int MessageBoxA(
  HWND   hWnd,
  LPCSTR lpText,
  LPCSTR lpCaption,
  UINT   uType
);

parameter

uType

The content and behavior of the dialog box. This parameter can be a combination of flags in the following flag groups

To indicate the button displayed in the message box, specify one of the following values:

To display an icon in a message box, specify one of the following values:

To specify a default button, specify one of the following values:

To indicate the mode of the dialog box, specify one of the following values:

To specify additional options, use one or more of the following values:

Return value

Return value type: int

  • If the message box has a cancel button, if the ESC key is pressed or the Cancel button is selected, the function will return the IDCANCEL value. If the message box does not have a cancel button, pressing ESC has no effect - unless MB_ The OK button is present. If MB appears_ OK button, press "ESC" key, and the return value is "IDOK".
  • If the function fails, the return value is 0. To get extended error information, call GetLastError
  • If the function succeeds, the return value is one of the following menu item values:

By setting the uType parameter to the corresponding flag value, the following system icons can be used in the message box

View disassembly

9:        MessageBoxA(0,"lyl610abc","tips",0);
00401028 8B F4                mov         esi,esp
0040102A 6A 00                push        0
0040102C 68 28 20 42 00       push        offset string "tips" (00422028)
00401031 68 1C 20 42 00       push        offset string "lyl610abc" (0042201c)
00401036 6A 00                push        0
00401038 FF 15 AC A2 42 00    call        dword ptr [__imp__MessageBoxA@16 (0042a2ac)]
0040103E 3B F4                cmp         esi,esp
00401040 E8 2B 00 00 00       call        __chkesp (00401070)

The key code is intercepted here

0040102A 6A 00                push        0
0040102C 68 28 20 42 00       push        offset string "tips" (00422028)
00401031 68 1C 20 42 00       push        offset string "lyl610abc" (0042201c)
00401036 6A 00                push        0
00401038 FF 15 AC A2 42 00    call        dword ptr [__imp__MessageBoxA@16 (0042a2ac)]

Description of other codes

00401028 8B F4                mov         esi,esp                //Save the ESP (stack top) before execution to esi
0040103E 3B F4                cmp         esi,esp                //Compare the esi with the ESP after the call (top of stack)
00401040 E8 2B 00 00 00       call        __chkesp (00401070)        //Call the function to detect esp

It is used to detect whether the stack remains balanced after calling the function. It is automatically generated by C language. There is no need to pay attention here

Parsing disassembly

0040102A 6A 00                push        0
0040102C 68 28 20 42 00       push        offset string "tips" (00422028)
00401031 68 1C 20 42 00       push        offset string "lyl610abc" (0042201c)
00401036 6A 00                push        0

The first four lines of code press in four parameters in turn (from right to left), which is related to the call agreement. In Reverse basic note 9 C language inline assembly and call protocol As already said, I won't repeat it here

00401038 FF 15 AC A2 42 00    call        dword ptr [__imp__MessageBoxA@16 (0042a2ac)]

Call here, you can see that this is an indirect call in the form of call [address], so here you need to check what is stored in its actual address, that is, its actual call address

Through the memory window, the actual call address is 77D507EA

So the code at this time is equivalent to

00401038        call        77D507EA

Why is this an indirect call? What does 77D507EA stand for?

The reason for using indirect call here is that the import table is referenced. The notes about the contents of the import table and export table will be explained later

The address of the message5077ea function here is box7a

The address of MessageBoxA function of each computer is not necessarily the same, which depends on the user32 of the system DLL (the MessageBoxA called here is provided by user32.dll)

IDA can be used to verify this:

Find user32.0 in the system DLL (in 32-bit xp, there are two in C:\WINDOWS\system32 and in 64 bit system, one in C:\Windows\SysWOW64 (32-bit dll) and one in C:\WINDOWS\system32 (64 bit dll). Which one to use depends on whether the program is 32-bit or 64 bit)

Open it with IDA, find the export table, and then search the export table to get MessageBoxA

You can see that its address is the same as that obtained earlier. It is 77D507EA. The source is verified

Note that the system at this time is XP32-bit. In other higher version systems, the Address here is not necessarily the same as the previous one. This is mainly to explain that the source of the function is related to the import table and export table, and also explain why the addresses of MessageBoxA in different systems are not necessarily the same, so as to pave the way for later learning

The method to accurately obtain the function address is as follows:

If you want to get the address of MessageBoxA function (the same with other functions), first find a program that calls the module to which this function belongs (the module to which MessageBoxA belongs is user32.dll), and use OD attachment to find:

Generally speaking, any graphical program will call user32 DLL, so just take an exe program here. Here, take Dbgview exe as an example

Use OD to open Dbgview Exe, and then click e in the figure, or use the shortcut key Alt+E

In the pop-up window, find user32 DLL, double click

In the pop-up disassembly interface, press the shortcut key Ctrl+N

Then find the address corresponding to MessageBoxA in the new window

The address obtained at this time is the exact MessageBoxA address

Self writing disassembly test

Test code

Now that the function address of MessageBoxA is obtained, there is no need to call it indirectly. You can call it directly, so the self written code is as follows:

#include<stdio.h>
#include <windows.h>
int MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
        int ret;
    //The address here is filled in the MessageBoxA address obtained through OD. Each system is not necessarily the same
    //It was previously tested in xp system. At that time, the MessageBoxA was 77D507EA
    //At this time, switch to WIN10 X64 test and modify the value of MessageBoxA
        int addr = 0x76a3ee90;
        __asm {
                push hWnd                
                push lpCaption
        push lpText
                push uType
                call addr
                mov ret, eax
        }
        return ret;
}

int main(int argc, char* argv[])
{
        MyMessageBoxA(0, "lyl610abc", "tips", 0);
        return 0;
}

test result

It can still pop up normally, and the test is successful

Disassembly to hard coding (bytecode)

Through the previous study, we can know that in the computer, both the executed code and data are stored in binary. In order to facilitate viewing, the hexadecimal viewing tool displays the content in hexadecimal

Therefore, in order to add a pop-up window to the program, it is obvious that instead of directly writing the disassembly into the PE file, it is necessary to write the hard coding (bytecode) corresponding to the disassembly into the PE file

How to convert disassembly into hard coding? This is what the disassembly engine does. The disassembly engine in OD or VC has given the hard code (bytecode) corresponding to disassembly

The content of disassembly engine also belongs to the knowledge related to hard coding. Let's see if we want to open this pit in the future

Slightly modify the disassembly written earlier, write the parameters dead, and remove the reception of the return value

Modify disassembly code

#include<stdio.h>
#include <windows.h>
void MyMessageBoxA() {
    //Get the address of MessageBoxA
        int addr = (int)&MessageBoxA;
        printf("addr:%X\n", addr);
    //ASCII code corresponding to "lyl610abc" 6c 79 6c 36 31 30 61 62 63
        unsigned char bytes[] = { 0x6c,0x79,0x6c,0x36,0x31,0x30,0x61,0x62,0x63,0x00 };
    //ASCII encoding corresponding to "tips"
        unsigned char bytes2[] = { 0x74,0x69,0x70,0x73 };
        //Apply for memory whose attribute is executable, readable and writable
        LPVOID _lpText = VirtualAlloc(NULL, sizeof(byte), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        //Apply for memory whose attribute is executable, readable and writable
        LPVOID _lpCaption = VirtualAlloc(NULL, sizeof(byte), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        //Hard coded into the requested privileged memory
        WriteProcessMemory(INVALID_HANDLE_VALUE, (LPVOID)_lpText, (BYTE*)bytes, sizeof(bytes), 0);
        //Hard coded into the requested privileged memory
        WriteProcessMemory(INVALID_HANDLE_VALUE, (LPVOID)_lpCaption, (BYTE*)bytes2, sizeof(bytes2), 0);
        __asm {
                push 0
                push _lpCaption
                push _lpText
                push 0
                call addr
        }
}

int main(int argc, char* argv[])
{
        MyMessageBoxA();
        return 0;
}

OD analog code operation

First summarize what the above modified disassembly code has done

  1. Get the function address of MessageBoxA
  2. Write the ASCII code corresponding to the string to a piece of readable and writable memory
  3. Press in parameters
  4. Call MessageBoxA function
  5. After calling, jump back to the original code to be executed

Then reproduce in OD operation

Use OD to load a random file containing user32 DLL module program, here I directly take the program compiled by the previous code as a demonstration

Follow the steps summarized above

1. The address of messageboxa in OD is known and does not need to be obtained

2. Write the ASCII code corresponding to the string into a piece of readable and writable memory

First, select a piece of readable and writable memory. Obviously, the code to be executed next must be readable, writable and executable. Therefore, there is no need to apply for memory here. Just overwrite the following code

The problem of selecting memory is solved. The next step is to write the ASCII code corresponding to the string into memory, but this should be put after the pressing parameter. The reason will be explained later. Now let's look at the pressing parameter first

3. Press in parameters

The first parameter is 0. Directly modify its corresponding disassembly to push 0

The second parameter is_ lpCaption, the string to be written is "tips", but at this time, the string has not been written into memory, that is, the memory address of the string is not determined, but an approximate range can be determined. The place where the string is to be stored is not far below, so you can fill in any memory address not far from here for occupation, After determining the memory address of the string, come back and modify it

Here, just choose the following address 00401156

Therefore, the disassembly is modified to push 0x00401156

The third parameter is_ lpText, the string to be written is "lyl610abc", and the previous_ Like lpCaption, first fill in the space occupied by push xxxx, and then come back to modify it

Therefore, the disassembly is still modified to push 0x00401156

The fourth parameter is 0. Directly modify its corresponding disassembly to push 0

4. Call MessageBoxA function

Directly modify the disassembly to call MessageBoxA

5. After calling, jump back to the original code to be executed

This is to jump back to the program entry after the pop-up code is executed after the simulation

Here, just simulate a long jump, and modify the disassembly code to jmp 000412D0

6. Fill string into memory

The following code can be used as a data area because the previous absolute jump will not be executed, which is why the filling of the string should be placed later

Fill first_ lpText, which is "lyl610abc"

Select the line under jump, right click → binary → edit

Select ASCII and fill in the string "lyl610abc" to be written to memory

After filling in, select the Hex line and add 00 after it (the string ends with '\ 0')

Get after adding 00

Then confirm

Get after modification

This is the third parameter_ lpText can be corrected by changing the previous disassembly of pressing in the third parameter to push 0x00401148

Fill in the second parameter in the same way_ lpCaption, or "tips"

Here, first record its address as 0x00401152, and then start to modify it

After modification:

Finally, correct the address used to occupy the previous push

After modification, record the modified hexadecimal code as

6A 00 68 52 11 40 00 68 48 11 40 00 6A 00 E8 A7 F6 94 77 E9 88 01 C4 FF 6C 79 6C 36 31 30 61 62
63 00 74 69 70 73 00

After the modification is completed, press F8 continuously and step by step until you call user32 Messageboxa this

You can see that the corresponding parameters are OK

Finally, press F8 to step by step to see the results:

Be able to pop up the box according to the code and complete (•) ̀ ω •́ ) ✧

explain

Due to space limitation, this note mainly explains the compilation process of pop-up code. Later, the notes slightly modify the above hard code, write it into the PE file, and then modify the PE entry point to achieve the ultimate goal

Finally, we have come to the actual combat link. The number of people in the previous notes on the introduction of knowledge points is endless. I hope that through the actual combat, we can arouse everyone's interest and learn together to make common progress O(∩∩) O

The download links of the program modified by OD in this article are in the links below. Welcome to download and communicate

https://www.dslt.tech/article-436-1.html

Copyright notice: This article was originally created by lyl610abc. Welcome to share this article. Please keep the source for reprint

Added by CodeToad on Sat, 19 Feb 2022 18:03:59 +0200