Some friends who have used plug-ins should know that when playing games, we have many auxiliary functions for you, such as taking medicine and using items, at this time, we use the injection code technology. Simply put, we let another process execute the code we want it to execute. The key function in the middle is CreateRemoteThread.
The parameters of CreateRemoteThread are similar to those of CreateThread. The extra hProcess is the HANDLE of the process we want to operate on. In earlier WINDOWS versions, CreateThread was actually implemented using CreateRemoteThread, it is to pass hProcess into our own process HANDLE.
The CreateRemoteThread function is to create a thread in the specified process. This thread runs the function we specified. It looks very simple, but there is a problem, that is, problems caused by virtual memory.
Everyone knows that in WINDOWS, virtual memory is used for data management. Each process has its own address space. Suppose process A is going to inject A piece of code into process B, he wants process B to execute the InjectionCode () function of his process space. The address of this function in process A is 0X3000.
Now we start code injection. Using CreateRemoteThread, we will tell process B that we should execute code with the virtual memory address 0X3000. What should process B do at this time ?? After receiving this command, process B creates a thread and calls the 0X3000 content.Process B calls code 0 x in its memory instead of process,So what is 0X3000 of process B now ?? No one knows. If you are lucky, you may have some code to execute for you. If you are lucky, you may not know what will happen, just like you entered the wrong student apartment, in a room with the same number, good luck is the school flower room, and bad luck is like a flower room
So how can we let the process execute our corresponding code ?? We only need to open up a piece of memory in process B, copy our code or data, and then execute the corresponding code. We need to use these functions:
*
These two functions are similar to the functions we usually use. They only have the option of adding a process. The approximate steps are as follows:
Now we will perform the following operations:
The following is the program we want to inject. Before that, we 'd better fix the base address so that we won't change the function address every time we re-run the program. In VS2008, project properties-> linker-> advanced, select the default value for random and fixed base addresses
PrintMsg( * main(
Suppose our PrintMsg address is 0x401000. Now we need to inject a piece of code into this process so that she can automatically call the PrintMsg function.
* Msg = unsigned PARAM_SIZE = unsigned EXE_SIZE = InjectionCode (* mov eax, main (HANDLE hProcess = OpenProcessByProcessNmae (hProcess = printf (// you must write the code and msg of the function into the process to be injected; otherwise, a location error will occur (usually a crash) LPVOID RemoteExe = LPVOID RemoteParam = SIZE_T WriteCount = ret = WriteProcessMemory (hProcess, RemoteParam, msg, PARAM_SIZE, & ret = encrypt (hProcess, RemoteExe, InjectionCode ,, & HANDLE hThread = CreateRemoteThread (hProcess, NULL, (LPTHREAD_START_ROUTINE) RemoteExe, RemoteParam ,}
Run the above program, we can create a thread in another process, and this thread will output the thread ID and the message we want to output
The above program has several notes:
1. Resource Competition
Because it is the creation thread to execute the corresponding code, there will certainly be a resource competition problem, you must pay attention to writing code in the future, in this example I ignored this problem
2. code length issues
In this example, the code length is 0X13, but you must know that the length of the assembly code may be changed at will, probably because of an instruction or a parameter, so we need to pay attention to this point all the time. How to measure the code length is calculated after I read the disassembly code. This method is more accurate and can be roughly estimated, you only need to copy the complete code. It doesn't matter if you exceed the applied memory size.
3. Remember to back up our registers
This is very important. Once you change the register, if it is not restored, it may lead to a series of errors, especially important registers such as ESP and EBP.
3. Inject code multiple times to call functions in the system DLL
<WINDOWS core programming> the system will load the system DLL to A fixed address, such as VirtualAllocEx. Generally, when we are in process A and process B, the call or jmp addresses are the same, so if we call a system function, we generally don't need to worry about it. However, I thought of a problem yesterday, for example, process A needs to command process B to call the createconlhelp32snapshot system API. Now we assume that the createconlhelp32snapshot API is in A separate TLHELP32.DLL (in fact, in KERNEL32.DLL, all processes will load this DLL, so you don't need to worry about the following issues. This is just an example.) When the operating system loads the DLL, it will map the API address to the address 0XFF40100 of the virtual memory, according to our original idea, process B runs to call 0XFF40100. But the problem is, if our process does not load the TLHELP32.DLL, what will happen to the process call 0XFF40100 ?? This depends on your code. Some people say that the operating system will help you load the DLL, but I think it is wrong, because the DLL to be loaded by the operating system is in the import table in the PE Header, We need to display the DLL, otherwise the operating system will not know which DLL our API is in.
4. Inject code multiple times to call functions we have compiled
For example, we have IntejectionCode, which calls IntejectionCode1. At this time, we need to write IntejectionCode1 to the other process, not just the IntejectionCode, and we need to change the call IntejectionCode1 jump command in the IntejectionCode, jump to the correct position. In short, when the other party's website is owned by others and the other party's process wants to put the code wherever it is, we cannot manage it (in fact, virtualAllocEx can specify a location, but we usually try to specify the operating system as much as possible). We can only follow the common conventions, where people want us to call and where we want to call, otherwise it will easily lead to a process crash.
5. The base address of the Code
In this example, the base address of PrintMsg is fixed. We manually fix the base address. during development, few of us will fix the base address, so when the process is running, the address of the PrintMsg function will change. Of course, we can also figure out where the code will be stored during running, because the PrintMsg code is placed in the EXE file in binary format, there is also a file offset. When the operating system loads the EXE file into the memory, it calculates the position of PrintMsg in the virtual memory based on the base address and file offset, so as long as we can get the base address when the process is running, and view the file offset of this code in the EXE disassembly, we can also calculate the address of PrintMsg at each run, although it is very troublesome, in particular, the part of the code for disassembly, but there is no way. I will talk about it later.