初始化后,数字考试监视器将定期向监控服务器发送五种类型的数据:
这些信息“数据包”被序列化并https://1qk4wqinaf.execute-api.eu-west-1.amazonaws.com/test
使用 api-key发送到 api-server bFywbPRqgF5uSnpfH4EhR45u36wIZjP46yQ3eDWX
。
数字考试监视器还添加了当前未使用的各种数据包标识符,暗示这些功能可能会在以后的修订版中出现,或者由于决定在初始发布之前废弃这些功能。以下是定义的数据包标识符:
public enum ColTypeEnum
{
Heartbeat = 1,
NetworkControl,
Screenshot,
VmDetection,
ProcessList,
Clipboard,
Keylog,
Sites,
ActiveApplication,
Usb,
Bttrf
}
我们既不会讨论 api 系统的全部范围,也不会讨论数据包格式,因为它与我们在文章末尾的结论无关。
使用 .NET 的 Graphics 库实现定期截图方法:
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())
};
}
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 并复制所选文本。
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;
}
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);
}
}
}
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 删除。