前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >监视的最低点(2)

监视的最低点(2)

原创
作者头像
franket
发布2021-12-24 12:37:30
1.9K0
发布2021-12-24 12:37:30
举报
文章被收录于专栏:技术杂记

功能

初始化后,数字考试监视器将定期向监控服务器发送五种类型的数据:

  • 主监视器的屏幕截图(每 30 秒)
  • 正在运行的进程列表(每 61 秒)
  • 网络接口列表(每 63 秒)
  • 在任何打开的浏览器中打开标签的 URL(每 67 秒)
  • 心跳(每 10 秒)

这些信息“数据包”被序列化并https://1qk4wqinaf.execute-api.eu-west-1.amazonaws.com/test使用 api-key发送到 api-server bFywbPRqgF5uSnpfH4EhR45u36wIZjP46yQ3eDWX

数字考试监视器还添加了当前未使用的各种数据包标识符,暗示这些功能可能会在以后的修订版中出现,或者由于决定在初始发布之前废弃这些功能。以下是定义的数据包标识符:

代码语言:txt
复制
public enum ColTypeEnum
{
	Heartbeat = 1,
	NetworkControl,
	Screenshot,
	VmDetection,
	ProcessList,
	Clipboard,
	Keylog,
	Sites,
	ActiveApplication,
	Usb,
	Bttrf
}

我们既不会讨论 api 系统的全部范围,也不会讨论数据包格式,因为它与我们在文章末尾的结论无关。

截图

使用 .NET 的 Graphics 库实现定期截图方法:

代码语言:txt
复制
using (Image image = ScreenCaptureTool.CaptureScreenNew())
{
	var data = ScreenCaptureTool.ImageToByteArray(image, 30);
	packet = new List<DataPackage> {
		new DataPackage(
                    DataPackage.ColTypeEnum.Sshot, false, data, 
                    DataPackageEnvelopeAwsReceiver.ServerTime, base.GetAndIncrementWorkSequence())
	};
}
代码语言:txt
复制
public static Image CaptureScreenNew()
{
	Rectangle bounds = Screen.PrimaryScreen.Bounds;
	Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
	Size blockRegionSize = new Size(bitmap.Width, bitmap.Height);
	using (Graphics graphics = Graphics.FromImage(bitmap))
	{
		graphics.CopyFromScreen(0, 0, 0, 0, blockRegionSize);
		return bitmap;
	}
}

这些功能本身很好,因为大多数人在考试情况下不会有多个屏幕,但由于没有明确规定您不得携带另一台显示器,因此相应的屏幕截图实现是不够的。

Digital Exam Monitor 还为EVENT_SYSTEM_FOREGROUND事件设置了一个钩子,每次机器上的活动窗口更改时都会引发该事件,从而强制调用上述用于定时屏幕截图的相同例程。

打开网站列表

数字考试监视器将使用自动化元素解析四个已知浏览器中任何一个的任何选定选项卡的 URL。我们决定只展示Chrome的实现,因为它们都非常相似。该算法归结为将CTRL+L热键(标记 URL 选项卡的内容)发送到 Chrome 并复制所选文本。

代码语言:txt
复制
private static CurrentBrowserUrlsTool.BrowserType Parse(string processName)
{
	processName = processName.ToLower();

	if (processName.Contains("chrome"))
	{
		return CurrentBrowserUrlsTool.BrowserType.GOOGLE_CHROME;
	}

	if (processName.Contains("applicationframehost"))
	{
		return CurrentBrowserUrlsTool.BrowserType.MICROSOFT_EDGE;
	}

	if (processName.Contains("iexplore"))
	{
		return CurrentBrowserUrlsTool.BrowserType.INTERNET_EXPLORER;
	}

	if (processName.Contains("firefox"))
	{
		return CurrentBrowserUrlsTool.BrowserType.FIREFOX;
	}

	return CurrentBrowserUrlsTool.BrowserType.Empty;
}
代码语言:txt
复制
Process[] processes = Process.GetProcesses();

using (Dictionary<string, CurrentBrowserUrlsTool.BrowserType>.Enumerator enumerator = 
		CurrentBrowserUrlsTool._processSearchStringsForBrowsertypes.GetEnumerator())
{
	while (enumerator.MoveNext())
	{
		KeyValuePair<string, CurrentBrowserUrlsTool.BrowserType> browserPair = enumerator.Current;
		foreach (Process process in from p in processes
			where p.ProcessName.ToLower().Contains(browserPair.Key)
				select p)
		{
			if (process.MainWindowHandle == IntPtr.Zero)
				continue;
			
			string urlfromProcess = CurrentBrowserUrlsTool.GetURLFromProcess(
							process, browserPair.Value, process.MainWindowHandle);

			if (string.IsNullOrEmpty(urlfromProcess))
				continue;
			
			StaticFileLogger.Current.LogEvent(
				"CurrentBrowserUrlsTool._timer_Elapsed()", "Url harvested", 
				urlfromProcess, EventLogEntryType.Information);

			
			CurrentBrowserUrlsTool._urlSet.Add(urlfromProcess);
		}
	}
}
代码语言:txt
复制
if (browser.Equals(CurrentBrowserUrlsTool.BrowserType.GOOGLE_CHROME))
{
	AutomationElement automationElement2 = null;
	try
	{
		AutomationElement automationElement3 = automationElement.FindFirst(
					TreeScope.Children, 
					new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));

		if (automationElement3 == null)
			return null;

		automationElement2 = 
			TreeWalker.RawViewWalker.GetLastChild(automationElement3).
				FindFirst(TreeScope.Children, 
					new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane)).
				FindFirst(TreeScope.Children, 
					new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane)).
				FindFirst(TreeScope.Descendants, 
					new PropertyCondition(AutomationElement.AccessKeyProperty, "Ctrl+L"));

		if (automationElement2 == null)
			automationElement2 = 
				automationElement3.FindFirst(
					TreeScope.Descendants, 
					new PropertyCondition(AutomationElement.AccessKeyProperty, "Ctrl+L"));
	}
	catch
	{
		return null;
	}

	if (automationElement2 == null)
		return null;

	if ((bool)automationElement2.GetCurrentPropertyValue(AutomationElement.HasKeyboardFocusProperty))
		return null;

	AutomationPattern[] supportedPatterns = automationElement2.GetSupportedPatterns();

	if (supportedPatterns.Length != 1)
		return null;

	string text = "";
	try
	{
		ValuePattern.ValuePatternInformation valuePatternInformation = 
			((ValuePattern)automationElement2.GetCurrentPattern(supportedPatterns[0])).Current;

		text = valuePatternInformation.Value;
	}
	catch
	{
	}

	if (text == "" || !Regex.IsMatch(text, "^(https:\\/\\/)?[a-zA-Z0-9\\-\\.]+(\\.[a-zA-Z]{2,4}).*$"))
		return null;

	
	if (!text.StartsWith("http"))
	{
		text = "http://" + text;
	}

	return text;
}

我们注意到,他们没有为已安装的浏览器解析shell/open注册表项,而是决定硬编码进程名称以区分浏览器。这是非常懒惰的,因为它允许任何人在磁盘上重命名他们的浏览器,以防止监视软件抓取活动的浏览器选项卡。

这个数据点也受到这样一个事实的影响,即任何人都可以在他们的浏览器中修改地址栏的内容,基本上无需任何技术努力就可以欺骗结果,如下所示:

如前所述,修改 PE 标头以强制打开控制台允许我们检查每个事件的结果: eventSource='CurrentBrowserUrlsTool._timer_Elapsed()', title='Url harvested', description='https://hello.com', entryType='Information'

因此,无需任何实际努力,就可以绕过此检查。对于好奇的读者来说:“否则你会怎么做?”,你不会。你永远不想依赖这样的可变信息。记录考试参加者浏览内容的正确方法是创建特定于浏览器的模块,这些模块挂钩负责打开网站的相应功能(易于查找)。使用这种方法,您可以绝对肯定地知道学生是否在使用任何被禁止的网站。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 功能
  • 截图
  • 打开网站列表
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档