详解银狐远控源码中那些C++编码问题

特别申明:

本文内容仅限于用作技术交流,请勿使用本文介绍的技术做任何其他用途,否则后果自负,与本号无关。

原始的银狐远程控制软件中,存在大量C++编码问题,大多数错误都属于低级错误。这些错误造成银狐远控稳定性差,容易崩溃。

本次代码以4.0为版本,Visual Studio 2010工程,希望对小伙伴们提高C/C++开发水平有帮助。

问题1

如图所示,银狐代码中有大量调用Windows API GetModuleFileName获取当前程序所在路径,该函数的最后一个参数传递的是字符串数量,不是字符串字节数目。由于工程设置使用了Unicode字符集合,一个字符占两个字节,而图中传递的是字节数目,当存放路径较长时,容易造成内存越界,引起崩溃。

DWORD GetModuleFileNameW( [in, optional] HMODULE hModule, [out] LPWSTR lpFilename, [in] DWORD nSize // 这个参数是字符数量,不是字节数目 );

修改方法:

将第三个参数改成字符数量,Windows定义了一个宏ARRAYSIZE可以编译器期间自动计算字符数据长度,其实现是数组总字节数除以第一个元素的字节数,即为字符长度。

将上述代码改成:

TCHAR ExePath[MAX_PATH] = { 0 }; GetModuleFileName(NULL, ExePath, ARRAYSIZE(ExePath));

这样的代码,在银狐代码中至少存在十几处,尤其是主控代码中。

问题2

如图所示,这里节选的是系统管理插件的代码,不知道读者能否看出标红处的问题?

这里使用memcpy将一个空字符拷贝至lpBuffer + dwOffset所在位置的内存处,拷贝长度是MAX_PATH * sizeof(TCHAR)。这里的行为是未定义的,由于宽字符_T("")指向的内存值就是L\0,这里拷贝长度过多,造成内存越界,行为未知。

修改方法:

作者的原意是将MAX_PATH * sizeof(TCHAR)长度的内存拷贝过去,所以这里可以简单改成使用memset函数做清零就可以了。

memset(lpBuffer + dwOffset, 0, MAX_PATH * sizeof(TCHAR));

这样的代码到处都是,尤其是在系统管理插件模块。

问题3

如图所示,问题所属模块为登录模块离线键盘记录功能。聪明的读者能看出这段代码的问题吗?

问题如下:

  1. GlobalSize这个API返回的就是当前剪贴板数据的字节数,即使剪贴板内容是宽字符,所以计算字节内容时不该乘以2,现在乘以2可能造成接下来的wcscmp(lpstr, Clipboard_old) != 0代码中比较时读取lpstr越界。
  2. 同理memcpy(Clipboard_old, lpstr, nPacketLen);中读取lpstr也可能越界。
  3. 使用wsprintf(temp, _T("\r\n[剪切板:]%s\r\n"), lpstr);格式化字符时,由于没有指定temp最大缓冲区长度,那么格式化lpstr时会一直往后读取直到\0为止,问题是,lpstr不一定会以\0结束,所以可能一直往后读,此时内存已经越界。应该改写指定缓冲区最大长度的格式化函数来格式化剪贴板内容。

真是10行代码,三个崩溃。。。。。。

完整修复后的代码如下:

if (GetTickCount() - m_dwLastCapture > 1500) { InterlockedExchange((LPLONG)&m_dwLastCapture, GetTickCount()); OpenClipboard(NULL); HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT); if (hglb != NULL) { //int nPacketLen = int(GlobalSize(hglb)) * 2 + 2; int nPacketLen = int(GlobalSize(hglb)); LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb); if (lpstr != NULL) { if (nPacketLen < sizeof(szClipboard_old)) // 判断长度 { //if (wcscmp(lpstr, szClipboard_old) != 0) //判断内容 if (memcmp((char*)lpstr, (char*)szClipboard_old, nPacketLen) != 0) { memcpy(szClipboard_old, lpstr, nPacketLen); //wsprintf(szTemp, _T("\r\n[剪切板:]%s\r\n"), lpstr); swprintf_s(szTemp, ARRAYSIZE(szTemp), _T("\r\n[剪切板:]%s\r\n"), lpstr); Input::SaveToFile(szTemp); memset(szTemp, 0, sizeof(szTemp)); } } } ::GlobalUnlock(hglb); } ::CloseClipboard(); }

问题4

如图所示,问题还是位于模块为登录模块离线键盘记录功能。

这段代码获取被控机器上当前活动窗口的窗口标题,同问题一,GetWindowText最后一个参数应该传入缓冲区字符数量(最后一个L'\0')也要计算在内,这里传入了缓冲区字节数目,多了一倍,如果用户的电脑上活动窗口标题比较长,会造成内存溢出,被控崩溃。

修改方法:

BOOL Input::IsWindowsFocusChange() { memset(WindowCaption, 0, sizeof(WindowCaption)); hFocus = GetForegroundWindow(); GetWindowText(hFocus, WindowCaption, ARRAYSIZE(WindowCaption)); WindowCaption[ARRAYSIZE(WindowCaption)-1] = 0; //... 省略无关代码 }

下面的:

if (lstrlen(WindowCaption) > 0) { SYSTEMTIME s; GetLocalTime(&s); wsprintf(temp, _T("\r\n[标题:]%s\r\n[时间:]%d-%d-%d %d:%d:%d\r\n"), WindowCaption, s.wYear, s.wMonth, s.wDay, s.wHour, s.wMinute, s.wSecond); SaveToFile(temp); memset(temp, 0, sizeof(temp)); memset(WindowCaption, 0, sizeof(WindowCaption)); ReturnFlag = TRUE; }

调用wsprintf格式化存在与问题三一样的问题,当活动窗口标题较长时,内存也存在越界风险。

这也就解释了为什么当被控电脑点击不同窗口时,有时候被控会掉线(已经崩溃闪退)。

问题5

如图所示,代码位于主控远程屏幕相关代码。 这里的代码也存在内存问题,但是如果不熟悉相关的Windows API,可能无法解决。

问题现象是启动远程屏幕主控偶现崩溃。

这个问题我用Visual Studio 2022去排查的,因为高版本的VS集成了Google Address Sanitizer,可以很方便定位C/C++内存问题。具体方法可以看这里

解决方法:

查阅了一下 MSDN,发现GlobalAlloc函数分配内存时可以指定标志位,当标志位为GMEM_MOVEABLE时,分配的内存为可移动内存,CreateStreamOnHGlobal函数的第二个参数如果指定为 TRUE 时,在调用IStream::Release 时会自动释放CreateStreamOnHGlobal创建的 OLE 对象的内存。CreateStreamOnHGlobal函数签名如下:

HRESULT CreateStreamOnHGlobal( [in] HGLOBAL hGlobal, [in] BOOL fDeleteOnRelease, [out] LPSTREAM *ppstm );

由于这个OLE对象只占用了GlobalAlloc分配的部分内存,所以就出现了上述现象。

因此只要将上述代码中两处调用CreateStreamOnHGlobal函数的地方改成 FALSE 就可以了,不要自动释放内存即可。

修改后代码如下:

//显示截图窗口 void CMainFrame::OnOpenDesktop(ClientContext* pContext) { //...省略无关代码... HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, pContext->m_DeCompressionBuffer.GetBufferLen() - 1); void* pData = GlobalLock(hGlobal); memcpy(pData, pContext->m_DeCompressionBuffer.GetBuffer(1), pContext->m_DeCompressionBuffer.GetBufferLen() - 1); GlobalUnlock(hGlobal); IStream* pStream = NULL; if (CreateStreamOnHGlobal(hGlobal, FALSE, &pStream) == S_OK) { CImage image; if (SUCCEEDED(image.Load(pStream))) { IStream* pOutStream = NULL; if (CreateStreamOnHGlobal(NULL, FALSE, &pOutStream) == S_OK) { image.Save(Ttime); } } pStream->Release(); } GlobalFree(hGlobal); //...省略无关代码... }

问题6

如图所示,当使用被控使用UDP模式连接主控时,如果发生断线,被控再也无法连接上主控,也就是说被控UDP断线重连不起作用,影响所有需要用到网络连接的模块(例如上线模块、登录模块、键盘记录、远程屏幕等等)。

这段逻辑是当使用UDP连接时,发起连接后会调用WaitForSingleObject这个API等待连接结果(以m_hEvent_run这个内核Event受信为标志),连接成功会调用SetEvent这个API设置m_hEvent_run为受信状态。如下图所示:

然后调用CUdpSocket::run_event_loop()进入等待状态。

问题就在这里,调用CUdpSocket::run_event_loop()时将m_hEvent_run这个对象关闭了,一并受影响的还有m_hEvent这个Event对象。这样当断线重连时,再次调用上图中的CUdpSocket::Connect函数,由于m_hEvent_run已经被关闭,WaitForSingleObject立即返回,状态为WAIT_FAILED,导致永远连不上。如下图所示:

解决方法:

  1. 方法一是连接成功后,调用CUdpSocket::run_event_loop()不要调用CloseHandle(m_hEvent_run);关闭这个事件对象。
  2. 方法二是重连时新建调用CreateEvent函数新建m_hEvent_run对象。

解决这个bug,需要读者掌握Windows多线程和socket编程知识,目前市场上没有这类成体系的资料,笔者也是工作多年后,一边学习一边总结,完整的Windows和Linux多线程编程必学知识点我把它们汇总在cppguide.cn之上。

访问链接: 第 3 章 C++多线程编程从入门到进阶

当然,站点也提供了打包下载链接,有兴趣的读者可以访问。

关于银狐远控的bug远不止于此,维护这一年多以来,我总共修复了100+问题,github提交记录就有568次之多。

当然,虽然这套源码bug挺多的,但瑕不掩瑜,它仍然是学习C/C++开发、多线程编程、网络编程、安全工程、综合项目实践、红蓝攻防非常好的材料

为了更方便排查和优化代码,我除了修复以上bug以外,还将这套代码从原来的Visual Studio 2010工程全部升级成Visual Studio 2022,并补全和重编译了所有依赖库代码,并去掉所有后门,现在它是一款可以放心使用的远控软件。

源码获取

如果对银狐(winos)有兴趣,可以通过下面的方式获取全套源码:

https://mp.weixin.qq.com/s/JY6KArUGf0vn_rHV9hPxuA

推荐阅读

银狐远控问题排查与修复——Viusal Studio集成Google Address Sanitizer排查内存问题银狐远控代码中差异屏幕bug修复银狐远程屏幕内存优化方法探究银狐远程软件bug修复记录 第03篇

银狐远程软件 UDP 断线无法重连的bug排查和修复

银狐远程软件代理映射功能优化思路分享

银狐远程软件去后门方法

银狐远控一键编译调试与开发教程

银狐远控免杀与shellcode修复思路分析 01

银狐ShellCode混淆怪招

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1119876.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

泥石流与滑坡识别图像数据集 自然灾害实时监测 地质灾害监测场景下目标检测 自然灾害早期识别代码 自然灾害风险预警 AI应急响应辅助识别(代码+模型+数据集)10356期

数据集 README核心信息汇总表项目详情类别数量及名称2 类&#xff1a;泥石流、滑坡数据数量2140 条格式种类YOLO 格式应用价值适配地质灾害监测场景下目标检测模型训练&#xff0c;可用于灾害早期识别、风险预警、应急响应辅助决策等场景 数据类别概述 数据集聚焦地质灾害核心 …

从 SEO 到 AEO:Semrush Enterprise AIO 平台如何助力出海品牌抢占 AI 流量入口?

作为企业级的 AI 优化平台&#xff0c;Semrush Enterprise AIO 专门用于追踪和提升品牌在 AI 模型上的可见性。 如今&#xff0c;全球用户都在深度使用 AI&#xff0c;把 AI 当作首选的信息来源入口。从产品调研到购买决策&#xff0c;他们不再逐页翻阅搜索引擎提供的结果&…

铅冶炼含铊污酸处理技术:Tulsimer树脂助力污泥减量与稳定达标

在环保监管日趋严格的背景下&#xff0c;铅冶炼行业含铊污酸处理已成为制约企业合规运营的核心难题。此类烟气净化废水占冶炼总废水量的20%-30%&#xff0c;受原料含铊特性及稀酸循环使用影响&#xff0c;水体中总铊浓度呈持续攀升态势&#xff0c;给处理工艺带来极大挑战。铊作…

notebook的图显示中文的设置

apt updateapt install -y fonts-wqy-microheifc-list | grep -E “WenQuanYi|Noto”清楚缓存 import matplotlib import os import shutil# 获取matplotlib缓存目录 cache_dir matplotlib.get_cachedir() print(f"Matplotlib缓存目录&#xff1a;{cache_dir}")# …

Java中synchronized和ReentrantLock锁重入机制详解

在Java中&#xff0c;锁的重入&#xff08;Reentrancy&#xff09; 是指同一个线程可以多次获取同一把锁而不会造成死锁的特性。这是Java内置锁&#xff08;synchronized&#xff09;和ReentrantLock的一个重要特性。 核心概念 当一个线程已经持有某个锁时&#xff0c;它可以再…

Java中的锁对象详解

锁是什么&#xff1f; 1. 对于实例方法&#xff08;非static&#xff09; public class Demo {// 实例方法上的synchronizedpublic synchronized void methodA() {methodB(); // 调用另一个synchronized方法}public synchronized void methodB() {// ...} }锁对象 当前实例对…

深度学习毕设选题推荐:基于python深度学习的树叶健康识别人工智能

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

Java 对接 PLC 实战:西门子 PLC 与永宏 PLC 通讯方式全面对比

下面这篇按“从 0 到能跑通”的思路&#xff0c;把 西门子&#xff08;Siemens&#xff09;PLC 和 永宏&#xff08;FATEK/永宏电机&#xff09;PLC 在 用 Java 对接硬件 时的差异讲清楚&#xff1a;差异到底来自哪里、配置要点是什么、代码通常怎么写、踩坑点有哪些。读完你应…

入校申报审批系统的设计与实现毕业论文+PPT(附源代码+演示视频)

文章目录入校申报审批系统的设计与实现一、项目简介&#xff08;源代码在文末&#xff09;1.运行视频2.&#x1f680; 项目技术栈3.✅ 环境要求说明4.包含的文件列表&#xff08;含论文&#xff09;数据库结构与测试用例系统功能结构后端运行截图项目部署源码下载入校申报审批系…

化工TPM预防性维护落地指南:从0到1实现故障降40%

作为化工企业的工程技术人员&#xff0c;你是否经常面临这样的困境&#xff1a;设备故障突发难以预判&#xff0c;熬夜抢修成为常态&#xff1b;维护计划缺乏针对性&#xff0c;过度维护浪费资源、维护不足引发故障。其实&#xff0c;通过科学搭建TPM预防性维护体系&#xff0c…

一篇讲透:芋道源码中的「租户」是如何通过程序控制的?

一、先别急着看代码&#xff1a;什么是「租户」&#xff1f; 1️⃣ 什么是租户&#xff08;Tenant&#xff09;&#xff1f; 在 SaaS 系统 中&#xff1a;租户 一套系统的一个“客户单位”举几个直观例子&#xff1a;一个 OA 系统 A 公司是一位租户B 公司是另一位租户一个进销…

凤希AI伴侣开发手记:那些只有深度使用才暴露的“缺点”

大家好&#xff0c;我是凤希。现在是2026年1月6号晚上七点多&#xff0c;我才开始整理昨天的工作。这几天&#xff0c;我的作息完全颠倒了&#xff0c;经常早上七八点才睡&#xff0c;下午四点才起&#xff0c;活脱脱一个“美国时间”。为啥&#xff1f;因为这几天我几乎把所有…

计算机深度学习毕设实战-基于python深度学习的树叶健康识别机器学习

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

芋道(Ruoyi-Vue-Pro)对接腾讯云短信:没配回调URL,为啥也能看到发送成功/失败?如何排查?

很多人第一次把芋道&#xff08;Ruoyi-Vue-Pro / 芋道源码&#xff09;接到腾讯云短信时&#xff0c;都会遇到一个“看起来矛盾”的现象&#xff1a;我在芋道里没有配置短信回调 URL但在【短信日志】里却能看到&#xff1a; ✅ 发送成功❌ 发送失败同时【接收状态】又常常显示“…

Memcached vs Redis:面试必看的彻底解析

文章目录Memcached 与 Redis 的区别&#xff1f;闫工来告诉你&#xff01;一、Memcached&#xff1a;轻量级缓存界的“闪电侠”1.1 Memcached 的特点1.2 Memcached 的适用场景1.3 Memcached 的配置与使用示例二、Redis&#xff1a;全能型的“瑞士军刀”2.1 Redis 的特点2.2 Red…

ZGLanguage 解析SQL数据血缘 之 提取子查询语句中的源表名

# 假设存在 子查询SQL代码 如下&#xff1a;SELECT CL.OBJECTNO, PP.PAYDATEFROM NYBDP.O_SS_CL_LOAN_ACCT_STATIS CLLEFT JOIN (SELECT AL.PAYDATE, BC.BUSS_NOFROM O_CWWS_ACCT_LOAN ALINNER JOIN NYBDP.O_CWWS_BUSINESS_DUEBILL BDON 11LEFT JOIN O_CWWS_BUSINESS_CONTRACT …

typescript-var和let作用域

什么是作用域&#xff1f;作用域可以理解为某个变量在某个范围内是可以访问到。var是函数作用域&#xff08;声明的函数内&#xff09;、全局作用域&#xff08;在哪里都可以访问到&#xff09;let是块级作用域&#xff08;即程序中的大括号{}包含的范围内&#xff09;看下面的…

高精度双极板体积电阻率测试仪

高精度双极板体积电阻率测试仪 说明高精度双极板体积电阻率测试仪①电阻率ρ不仅和导体的材料有关&#xff0c;还和导体的温度有关。在温度变化不大的范围内&#xff0c;几乎所有金属的电阻率随温度作线性变化&#xff0c;即ρρo(1at)。式中t是摄氏温度&#xff0c;ρo是O℃时…

2026年香港服务器走CN2线路具有哪些优势?

2026 年香港服务器走 CN2 线路具有哪些优势?先说 CN2&#xff0c;CN2 线路作为中国电信的优质网络通道&#xff0c;通过独立路由和优先级保障&#xff0c;为香港服务器赋予了显著优势。1.速度优势&#xff1a;低延迟与高带宽的完美结合CN2线路通过优化路由跳转&#xff0c;大幅…

2026年香港服务器走CN2线路具有哪些优势?

2026 年香港服务器走 CN2 线路具有哪些优势?先说 CN2&#xff0c;CN2 线路作为中国电信的优质网络通道&#xff0c;通过独立路由和优先级保障&#xff0c;为香港服务器赋予了显著优势。1.速度优势&#xff1a;低延迟与高带宽的完美结合CN2线路通过优化路由跳转&#xff0c;大幅…