浅析一款非驱动考试网关程序(一)

前言

监考程序需要对网络流量进行过滤,不允许除了考试网站以外的其他网站的访问。其实就是实现了一个小型的网关程序,一般地有驱动实现和非驱动实现两种方式。本文只针对一款简易的非驱动实现的监考程序进行分析。

注意:本文通过对某考试监考网关客户端程序的逆向分析研究,节选其中断网的实现过程进行讲解。所有内容均在本地计算机测试环境中完成,敏感标记均已加码处理,仅供参考,谢绝用于其他用途。

系列文章:

编号文章标题ID
1非驱动考试网关程序(一):断网模块136037076
2非驱动考试网关程序(二):USB热拔插

(暂未发布)


一、初步分析调用树

初步分析时,分析软件是否依赖相应的 Windows 接口,如果有,那么问题就会非常简单。我们使用 API Monitor 程序实现对程序的监视。

分析结果截图 1

在监视时,我们发现了关键的函数:GetIpForwardTable、DeleteIpForwardEntry、GetIpForwardTable2、DeleteIpForwardEntry2、CreateIpForwardEntry。

GetIpForwardTable 函数检索 IPv4 路由表。DeleteIpForwardEntry 函数删除本地计算机的 IPv4 路由表中的现有路由,而 DeleteIpForwardEntry2 则是 IPv6 版本。CreateIpForwardEntry 则用于恢复路由表。

由此,我们可以推测程序通过修改路由表实现断网的功能,这应该是非常简单了。

二、通过 IDA 深入分析

确定了关键的调用接口,我们接下来就要通过 IDA 去分析程序的具体实现。

我们从导入表很快定位到这个调用的具体实现,关键调用在 sub_4028E0 中(记住这个 IDA 标记的函数名)。

首先通过一次调用 GetIpForwardEntry 获取需要为 MIB_IPFORWARDROW 结构分配的内存的大小,随后,再次调用 GetIpForwardEntry 获取 IPv4 路由表。

分析结果截图 2

随后就是遍历这个链表:

分析结果截图 3

在遍历过程中删除路由:

分析结果截图 4

最后通过全局变量 pRoute 将原始的路由表存储起来:

分析结果截图 5

随后通过 sub_402690 检测并尝试删除默认的 IPv6 路由:

分析结果截图 6

sub_402690 的实现:

分析结果截图 7

随后分析 CreateIpForwardEntry,有两次需要用到该函数,一种情况是在删除所有路由后,恢复一部分路由,还有一种就是在考试完成时,完全恢复路由。

恢复部分:

分析结果截图 8

全部恢复(通过全局变量 pRoute): 

分析结果截图 9

至此,我们已经了解了该程序是如何实现考试断网的功能,但是我们知道只修改一次肯定不能防止之后其他程序恢复系统默认路由的。下面我们分析他是如何在考试期间防止路由恢复的。

我们注意到 API 监视日志中的 SetTimer 调用,以及窗口的消息线程中 GetMessageW 以每秒一次的频率测试 WM_TIMER 消息,于是猜测到程序可能通过计时器不停地拉闸删除 IPv4 路由。

然后,我们通过查找 SetTimer 的交叉引用,锁定了来源,这是程序的窗口回调:

我们注意到 SetTimer 设置 ID == 1 的计时器,时间间隔为 1 秒。

分析结果截图 10

在点击关闭客户端时,才会处理 KillTimer 删除计时器。 

分析结果截图 11

继续往下看,我们发现出现 WM_TIMER 消息时候,程序执行删除路由的例程 sub_4028E0,这个函数是前面分析过的:

分析结果截图 12

三、编写测试工具

分析了具体的程序的实现,接下来,我们可以尝试编写一款修改器模拟对抗对吧?

这里的方法就比较多了,毕竟该程序完全依赖 Win32 API,并且不做防护。

提供两种思路如下:

思路1:在程序启动时挂钩 DeleteIpForwardEntry 和 DeleteIpForwardEntry2 ,不做任何操作并返回成功。

思路2:在程序启动前备份路由表,程序启动后任意时间均可挂钩 GetMessageW 并拦截 WM_TIMER 消息(wPararm == 1),在消息中 KillTimer,或者不做任何操作均可。随后通知挂钩程序恢复原始路由表。

提供一份挂钩例程的 Dll 代码(不包含编程恢复原始路由表的处理代码):

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"#include <Windows.h>
#include "detours.h"
#include <ipmib.h>
#include <string>
#include <iphlpapi.h>
#include <atlstr.h>#pragma comment(lib, "detours.lib")
#pragma comment( lib, "Iphlpapi.lib" )PVOID pFunGetMessageW = NULL;typedef BOOL(WINAPI* __GetMessageW)(LPMSG lpMsg,HWND  hWnd,UINT  wMsgFilterMin,UINT  wMsgFilterMax
);BOOL WINAPI HookedGetMessageW(LPMSG lpMsg,HWND  hWnd,UINT  wMsgFilterMin,UINT  wMsgFilterMax
);BOOL APIENTRY InstallHook();
BOOL APIENTRY UnInstallHook();BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:InstallHook();break;case DLL_THREAD_ATTACH:break;case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:UnInstallHook();break;}return TRUE;
}BOOL APIENTRY InstallHook()
{DetourTransactionBegin();DetourUpdateThread(GetCurrentThread());pFunGetMessageW = DetourFindFunction("User32.dll", "GetMessageW");DetourAttach(&pFunGetMessageW, HookedGetMessageW);LONG ret = DetourTransactionCommit();return ret == NO_ERROR;
}BOOL APIENTRY UnInstallHook()
{DetourTransactionBegin();DetourUpdateThread(GetCurrentThread());DetourDetach(&pFunGetMessageW, HookedGetMessageW);LONG ret = DetourTransactionCommit();return ret == NO_ERROR;
}BOOL WINAPI HookedGetMessageW(LPMSG lpMsg,HWND  hWnd,UINT  wMsgFilterMin,UINT  wMsgFilterMax
)
{// 判断是否是我们要取消的计时器if (lpMsg->wParam == 1 && lpMsg->message == WM_TIMER){// 删除计时器,解除路由检查lpMsg->message = WM_USER;KillTimer(lpMsg->hwnd, lpMsg->wParam);return TRUE;}return ((__GetMessageW)pFunGetMessageW)(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
}

路由备份和检查的代码可以参考下面的代码:

CString SwitchStatusStr(int iSwitch)
{CString strSwitch;strSwitch.Format(_T("%d"), iSwitch);return strSwitch;
}BOOL GetIsRestoreDefaultRoute(DWORD bToDeleteorAdd)
{BOOL bRet = FALSE;// Declare and initialize variablesPMIB_IPFORWARDTABLE pIpForwardTable = NULL;PMIB_IPFORWARDROW pRow = NULL; // 默认网关,添加时使用DWORD dwSize = 0;BOOL bOrder = FALSE;DWORD dwStatus = 0;// 默认路由保存路径std::wstring wsFilePath = L"C:\\DefaultRoute.ini";// Identify the required size of the buffer.if (bToDeleteorAdd == 0){dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);if (dwStatus == ERROR_INSUFFICIENT_BUFFER){// Allocate memory for the table.if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE)malloc(dwSize))){printf(("pipForwardTable Malloc failed. Out of memory.\n"));goto _END_;}// Retrieve the table.dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);}if (dwStatus != ERROR_SUCCESS){printf(("getIpForwardTable failed.\n"));if (pIpForwardTable){free(pIpForwardTable);}goto _END_;}// Search for the required row in the table. The default gateway has a destination// of 0.0.0.0. Be aware the table continues to be searched, but only// one row is copied. This is to ensure that, if multiple gateways exist, all of them are deleted.for (DWORD i = 0; i < pIpForwardTable->dwNumEntries; i++){if (pIpForwardTable->table[i].dwForwardDest == 0){// Delete the old default gateway entry.if (bToDeleteorAdd == 0){if (!pRow) {// Allocate some memory to store the row in; this is easier than filling// in the row structure ourselves, and we can be sure we change only the// gateway address.pRow = (PMIB_IPFORWARDROW)malloc(sizeof(MIB_IPFORWARDROW));if (!pRow) {printf("Malloc failed. Out of memory.\n");exit(1);}// Copy the rowmemcpy(pRow, &(pIpForwardTable->table[i]),sizeof(MIB_IPFORWARDROW));}// 先保存默认路由相关信息printf("开始保存默认路由\n");WritePrivateProfileString(L"RECOVER", L"dwForwardDest", SwitchStatusStr(pRow->dwForwardDest), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardMask", SwitchStatusStr(pRow->dwForwardMask), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardPolicy", SwitchStatusStr(pRow->dwForwardPolicy), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardNextHop", SwitchStatusStr(pRow->dwForwardNextHop), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardIfIndex", SwitchStatusStr(pRow->dwForwardIfIndex), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardType", SwitchStatusStr(pRow->dwForwardType), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardProto", SwitchStatusStr(pRow->dwForwardProto), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardAge", SwitchStatusStr(pRow->dwForwardAge), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardNextHopAS", SwitchStatusStr(pRow->dwForwardNextHopAS), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardMetric1", SwitchStatusStr(pRow->dwForwardMetric1), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardMetric2", SwitchStatusStr(pRow->dwForwardMetric2), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardMetric3", SwitchStatusStr(pRow->dwForwardMetric3), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardMetric4", SwitchStatusStr(pRow->dwForwardMetric4), wsFilePath.c_str());WritePrivateProfileString(L"RECOVER", L"dwForwardMetric5", SwitchStatusStr(pRow->dwForwardMetric5), wsFilePath.c_str());// 删除默认路由printf("保存默认路由成功。\n");}bRet = TRUE;goto _END_;}}}else if (bToDeleteorAdd == 1) // 恢复默认路由{printf("正在恢复默认路由......\n");pRow = (PMIB_IPFORWARDROW)malloc(sizeof(MIB_IPFORWARDROW));pRow->dwForwardDest = GetPrivateProfileIntW(L"RECOVER", L"dwForwardDest", -1, wsFilePath.c_str());pRow->dwForwardMask = GetPrivateProfileIntW(L"RECOVER", L"dwForwardMask", -1, wsFilePath.c_str());pRow->dwForwardPolicy = GetPrivateProfileIntW(L"RECOVER", L"dwForwardPolicy", -1, wsFilePath.c_str());pRow->dwForwardNextHop = GetPrivateProfileIntW(L"RECOVER", L"dwForwardNextHop", -1, wsFilePath.c_str());pRow->dwForwardIfIndex = GetPrivateProfileIntW(L"RECOVER", L"dwForwardIfIndex", -1, wsFilePath.c_str());pRow->dwForwardType = GetPrivateProfileIntW(L"RECOVER", L"dwForwardType", -1, wsFilePath.c_str());pRow->dwForwardProto = GetPrivateProfileIntW(L"RECOVER", L"dwForwardProto", -1, wsFilePath.c_str());pRow->dwForwardAge = GetPrivateProfileIntW(L"RECOVER", L"dwForwardAge", -1, wsFilePath.c_str());pRow->dwForwardNextHopAS = GetPrivateProfileIntW(L"RECOVER", L"dwForwardNextHopAS", -1, wsFilePath.c_str());pRow->dwForwardMetric1 = GetPrivateProfileIntW(L"RECOVER", L"dwForwardMetric1", -1, wsFilePath.c_str());pRow->dwForwardMetric2 = GetPrivateProfileIntW(L"RECOVER", L"dwForwardMetric2", -1, wsFilePath.c_str());pRow->dwForwardMetric3 = GetPrivateProfileIntW(L"RECOVER", L"dwForwardMetric3", -1, wsFilePath.c_str());pRow->dwForwardMetric4 = GetPrivateProfileIntW(L"RECOVER", L"dwForwardMetric4", -1, wsFilePath.c_str());pRow->dwForwardMetric5 = GetPrivateProfileIntW(L"RECOVER", L"dwForwardMetric5", -1, wsFilePath.c_str());// Create a new route entry for the default gateway.dwStatus = CreateIpForwardEntry(pRow);printf("恢复默认路由成功。\n");}_END_:if (pRow){free(pRow);}// Free the memory. if (pIpForwardTable){free(pIpForwardTable);}return bRet;
}

以上代码仅供学习参考使用,请勿用于其他用途。

四、总结&后续更新

其实没有什么好总结的。主要建议就是不要直接用微软接口,如果你做监考程序的话最好还是用驱动,用自己的实现,不然的话 R3 的挂钩就就全完了。当然,我们需要秉持着诚信考试的原则。


【保留备用】

发布于:2024.02.06;更新于:2024.02.06。

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

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

相关文章

程序员要不要考PMP

程序员要不要考PMP&#xff0c;我的建议是NO。 当然了&#xff0c;公司需要的除外。 考证的目不外乎两个&#xff1a;学东西&#xff0c;拿证书。 这里有一个证书和PMP证书很有对比性&#xff1a;软考 对比如下&#xff1a; 1. 学东西 pmp 和软考内容基本是重合的。 2. 证书…

c#cad 创建-点(六)

运行环境 vs2022 c# cad2016 调试成功 一、代码说明 创建一个点的命令方法。代码的主要功能是在当前活动文档中创建一个点&#xff0c;并将其添加到模型空间块表记录中。 代码的主要步骤如下&#xff1a; 获取当前活动文档、数据库和编辑器对象。使用事务开始创建点的过程…

Docker进阶篇-CIG重量级监控系统

一、简介 通过docker stats命令可以很方便的查看当前宿主机上所有容器的CPU、内存、网络流量等数 据&#xff0c;可以满足一些小型应用。 但是docker stats统计结果只能是当前宿主机的全部容器&#xff0c;数据资料是实时的&#xff0c;没有地方存储、 没有健康指标过线预警…

Redis(十二)Bigkey

文章目录 游标案例生成100万测试数据key生产上限制keys */flushdb/flushall等危险命令不使用keys *&#xff1a;scan Biigkey案例多大算大发现bigkey渐进式删除生产调优示例问题 游标案例 生成100万测试数据key shell: for((i1;i<100*10000;i)); do echo "set k$i v…

基于CNN+LSTM深度学习网络的时间序列预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 卷积神经网络&#xff08;CNN&#xff09; 4.2 长短时记忆网络&#xff08;LSTM&#xff09; 4.3 CNNLSTM网络结构 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MA…

【pikachu csrf】

cxrf 个人理解getPOST 个人理解 当被攻击用户登陆访问网站时&#xff0c;在保持登陆状态时点击小黑子&#xff08;黑客&#xff09;搭建的恶意链接而导致用户受到攻击。 举个例子 我去攻击网站&#xff0c;但是我找不到漏洞&#xff0c;这个时候我注册一个账号&#xff0c;发现…

探索Web API SpeechSynthesis:给你的网页增添声音

Web API SpeechSynthesis是一项强大的浏览器功能&#xff0c;它允许开发者将文本转换为语音&#xff0c;并通过浏览器播放出来。本文将深入探讨SpeechSynthesis的控制接口&#xff0c;包括其功能、用法和一个完整的JavaScript示例。 参考资料&#xff1a;SpeechSynthesis - Web…

【芯片设计- RTL 数字逻辑设计入门 8 -- 四选一多路器】

文章目录 四选一多路输出器verilog case 语句verilog 代码testbench 代码仿真波形 问题小结 四选一多路输出器 制作一个四选一的多路选择器&#xff0c;要求输出定义上为线网类型 状态转换&#xff1a; d0 00 d1 01 d2 10 d3 11verilog case 语句 case(express…

微软AD域替代方案,助力企业摆脱护网期间被攻击的窘境

在红蓝攻防演练&#xff08;护网行动&#xff09;中&#xff0c;AD域若被攻击成功&#xff0c;是其中一个扣分最多的一项内容。每年&#xff0c;宁盾都会接到大量AD在护网期间被攻击&#xff0c;甚至是被打穿的企业客户。过去&#xff0c;企业还会借助2FA双因子认证加强OA、Exc…

2024数据分析管理、数字经济与教育国际学术会议(ICDAMDEE2024)

会议简介 2024年数据分析管理、数字经济和教育国际学术会议&#xff08;ICDAMDEE 2024&#xff09;将在武汉举行。会议不仅展示了来自世界各地的研究专家围绕数据分析管理、数字经济和教育的最新科研成果&#xff0c;还为来自不同地区的代表们提供了面对面的交流意见和实验经验…

MySQL学习记录——사 表结构的操作

文章目录 1、创建表2、查看表结构3、改变表结构4、删除表5、总结 1、创建表 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; 例子 create table users ( id int, name varchar(20) c…

[office] Excel如何快速统一数字编号长度 #经验分享#其他

Excel如何快速统一数字编号长度 我们在办公室使用Excel统计数据的时候&#xff0c;经常会遇到第一列数据全部是数字编号&#xff0c;但是因为数字的位数不一样&#xff0c;长短不一的样子看起来不是很协调。那么如何快速统一数字编号长度呢&#xff1f;一起来了解一下吧 我们在…