Author: Gality
Number of words in this article: 7928 words
Reading duration: 20 minutes
This article belongs to the original reward plan of [wolf group safety community], and it is prohibited to reprint it without permission
Any direct or indirect consequences and losses caused by the dissemination and use of the information provided in this article shall be borne by the user himself, and the wolf group security team and the author of the article shall not bear any responsibility for this.
Wolf group security team has the right to modify and explain this article. If you want to reprint or disseminate this article, you must ensure the integrity of this article, including the copyright statement and other contents. Without the permission of wolf group security team, the content of this article shall not be modified or increased or decreased arbitrarily, and it shall not be used for commercial purposes in any way.
Recently, relevant things have been injected into the research process, mainly for reference process-inject Project, want to realize all the above process injection methods, and find out the principle. In process injection, the current process needs to be authorized first, so as to obtain the write permission to the memory of other processes and complete process injection. Therefore, this article mainly focuses on the relevant knowledge of process injection.
Conventional power raising method
The first is the conventional process right raising method. You can not understand the meaning of the following code, but only need to obtain it in the process SeDebugPrivilege Copy the code and call it at the beginning to get permission
BOOL EnableDebugPrivilege() { HANDLE TokenHandle = NULL; TOKEN_PRIVILEGES TokenPrivilege; LUID uID; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) { if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID)) { TokenPrivilege.PrivilegeCount = 1; TokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; TokenPrivilege.Privileges[0].Luid = uID; if (AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { CloseHandle(TokenHandle); TokenHandle = INVALID_HANDLE_VALUE; return TRUE; } else goto Fail; } else goto Fail; } else goto Fail; Fail: CloseHandle(TokenHandle); TokenHandle = INVALID_HANDLE_VALUE; return FALSE; }
explain
During process injection, any process (including system security process and service process) should be specified**Write correlation**Of access rights OpenProcess Operation, as long as the current process has SeDeDebug Permission is OK. If a user is Administrator Or given the corresponding permission, you can have the permission. But even if we use Administrator The account is used to execute a system security process OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID)You will still encounter an "access denied" error.
This is because some access permissions of the process are not Enabled by default, so the first thing we need to do is to enable these permissions. Some API functions related to this include OpenProcessToken, LookupPrivilegevalue and AdjustTokenPrivileges. If we want to modify the access token of a process
windows After each user logs into the system, the system will generate an access token( access token),It is associated with the permission information of the current user. Each process created after the user logs in contains the user access token When a process tries to perform some operations that require special permissions or access protected kernel objects, the system will check it acess token To determine whether to authorize the operation.
The access token of members of the Administrator group contains some privileges that can perform system level operations, such as terminating any process, shutting down / restarting the system, loading device drivers and changing system time. However, these privileges are disabled by default. When the process created by members of the Administrator group contains some operations that require privileges, The process must first open these disabled privileges to enhance its permissions, otherwise the system will refuse the operation of the process. Note that processes created by non members of the Administrator group cannot elevate their permissions
First, obtain the handle of the process access token, which can be obtained through OpenProcessToken. The prototype of the function is as follows:
BOOL OpenProcessToken( HANDLE ProcessHandle, //Process handle to modify access rights DWORD DesiredAccess, //Specify the type of operation you want to perform. If you want to modify the token, we need to specify the second parameter as TOKEN_ADJUST_PRIVILEGES PHANDLE TokenHandle //Returned access token pointer );
Through this function, we can get the handle of the access token of the current process (the first parameter of the specified function is GetCurrentProcess()It's OK). Then we can call AdjustTokenPrivileges Modify this access token. AdjustTokenPrivileges The prototype is as follows:
BOOL AdjustTokenPrivileges( HANDLE TokenHandle, //Handle to access token BOOL DisableAllPrivileges, //Decide whether to modify permissions or Disable all permissions PTOKEN_PRIVILEGES NewState, //Indicates the permission to modify. It is a pointer to token_ Pointer to the privileges structure, which contains an array. Each item of the data group indicates the type of permission and the operation to be performed DWORD BufferLength, //The length of the structure PreviousState. If the PreviousState is empty, the parameter should be NULL PTOKEN_PRIVILEGES PreviousState, //It is also a pointer to token_ The pointer of the privileges structure, which stores the information of the access permission before modification. It can be null PDWORD ReturnLength //The size returned for the actual PreviousState structure );
There are two data structures to be mentioned here. One is PTOKEN_PRIVILEGES,The structure is defined as follows:
typedef struct _TOKEN_PRIVILEGES { DWORD PrivilegeCount; //Refers to the number of array elements LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; //A luid_ AND_ Array of types attributes } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
So let's move on to the second structure, which is LUID_AND_ATTRIBUTES Structure:
typedef struct _LUID_AND_ATTRIBUTES { LUID Luid; //The type of permission is a value of LUID DWORD Attributes; //Indicates the type of operation to be performed. There are three options: SE_PRIVILEGE_ENABLED,SE_PRIVILEGE_ENABLED_BY_DEFAULT,SE_PRIVILEGE_USED_FOR_ACCESS } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;
Luid Means locally unique identifier,contrast Guid,that is global unique identifier,and GUID The requirements of ensure that the overall situation is unique, LUID As long as local uniqueness is guaranteed, it means that it is guaranteed to be unique during each operation of the system.
In addition, the same as GUID, luid is also a 64 bit value. You can query the luid value corresponding to a permission through the API LookupPrivilegeValue:
BOOL LookupPrivilegeValueA( LPCSTR lpSystemName, //The name of the system. If it is a local system, just indicate NULL LPCSTR lpName, //Indicates the name of the permission, such as "SeDebugPrivilege" PLUID lpLuid //Returns a pointer to the LUID );
Winnt.h also defines macros with other permission names, such as:
#define SE_CREATE_TOKEN_NAME TEXT("SeCreateTokenPrivilege") #define SE_ASSIGNPRIMARYTOKEN_NAME TEXT("SeAssignPrimaryTokenPrivilege") #define SE_LOCK_MEMORY_NAME TEXT("SeLockMemoryPrivilege") #define SE_INCREASE_QUOTA_NAME TEXT("SeIncreaseQuotaPrivilege") #define SE_UNSOLICITED_INPUT_NAME TEXT("SeUnsolicitedInputPrivilege") #define SE_MACHINE_ACCOUNT_NAME TEXT("SeMachineAccountPrivilege") #define SE_TCB_NAME TEXT("SeTcbPrivilege") #define SE_SECURITY_NAME TEXT("SeSecurityPrivilege") #define SE_TAKE_OWNERSHIP_NAME TEXT("SeTakeOwnershipPrivilege") #define SE_LOAD_DRIVER_NAME TEXT("SeLoadDriverPrivilege") #define SE_SYSTEM_PROFILE_NAME TEXT("SeSystemProfilePrivilege") #define SE_SYSTEMTIME_NAME TEXT("SeSystemtimePrivilege") .... //For a complete list of privileges, please refer to the security chapter of msdn
By calling these three functions, we can use OpenProcess to open the handle of any process and specify write access for process injection
Advanced power raising technique
The advanced rights raising method involves a system that has not been disclosed by Microsoft API,Which means you're msdn I can't find him on the Internet. It's encapsulated in ntdll In (all programs will load the Dll),That is, the legendary RtlAdjustPrivilege.
Function prototype:
NTSTATUS RtlAdjustPrivilege ( ULONG Privilege, // The required permission name can be found in MSDN. You can find the content about process token & privilege BOOLEAN Enable, // If True, the corresponding permission is opened; if False, the corresponding permission is closed BOOLEAN CurrentThread, // If True, only the permissions of the current thread will be promoted; otherwise, the permissions of the whole process will be promoted PBOOLEAN Enabled // Output the status of the original corresponding permission (open | close) )
We can analyze the function through IDA analysis ntdll. First analyze the general process without relying on F5:

The definitions of the two native functions used are as follows:
pub unsafe extern "system" fn NtOpenThreadToken( ThreadHandle: HANDLE, DesiredAccess: ACCESS_MASK, OpenAsSelf: BOOLEAN, TokenHandle: PHANDLE ) -> NTSTATUS pub unsafe extern "system" fn ZwOpenProcessToken( ProcessHandle: HANDLE, DesiredAccess: ACCESS_MASK, TokenHandle: PHANDLE ) -> NTSTATUS
After obtaining the token, you can upgrade the permission

Among them, the function prototype of ZwAdjustPrivilegesToken is:
pub unsafe extern "system" fn ZwAdjustPrivilegesToken( TokenHandle: HANDLE, DisableAllPrivileges: BOOLEAN, NewState: PTOKEN_PRIVILEGES, BufferLength: ULONG, PreviousState: PTOKEN_PRIVILEGES, ReturnLength: PULONG ) -> NTSTATUS
Later, the status of the original corresponding permission will be assigned to the enabled address

This is the general function. After the analysis, you can F5 look at the function:
int __stdcall RtlAdjustPrivilege(int Privilege, bool Enable, char CurrentThread, int *Enabled) { int result; // eax int v5; // esi int isEnabled; // [esp+8h] [ebp-2Ch] BYREF HANDLE TokenHandle; // [esp+Ch] [ebp-28h] BYREF int OldState[3]; // [esp+10h] [ebp-24h] BYREF int v9; // [esp+1Ch] [ebp-18h] int NewState; // [esp+20h] [ebp-14h] BYREF int dwPrivilege; // [esp+24h] [ebp-10h] int v12; // [esp+28h] [ebp-Ch] int v13; // [esp+2Ch] [ebp-8h] if ( CurrentThread == 1 ) result = NtOpenThreadToken(-2, 40, 0, &TokenHandle); else result = ZwOpenProcessToken(-1, 40, &TokenHandle); if ( result >= 0 ) { dwPrivilege = Privilege; NewState = 1; v12 = 0; v13 = Enable ? 2 : 0; v5 = ZwAdjustPrivilegesToken((int)TokenHandle, 0, (int)&NewState, 16, (int)OldState, (int)&isEnabled); NtClose(TokenHandle); if ( v5 == 0x106 ) v5 = 0xC0000061; if ( v5 >= 0 ) { if ( OldState[0] ) *(_BYTE *)Enabled = (v9 & 2) != 0; // Actually (oldstate. Privileges [0]. Attributes & se_privilege_enabled) else *(_BYTE *)Enabled = Enable; } result = v5; } return result; }
Relatively speaking, it is very clear, so how to use this function to raise the weight? Here is a demo code:
#include <iostream> #include <windows.h> #include <string> using namespace std; const unsigned long SE_DEBUG_PRIVILEGE = 0x13; typedef int(_stdcall *_RtlAdjustPrivilege)(int, BOOL, BOOL, int *); int main(int argc, char* argv[]) { HMODULE hNtDll = LoadLibrary("NTDLL.dll"); //Import ntdll if (!hNtDll) cout << "Error.." << endl; _RtlAdjustPrivilege pfnRtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(hNtDll, "RtlAdjustPrivilege");//Get the address of RtlAdjustPrivilege function int nEn = 0; pfnRtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &nEn); //Use this function return 0; }