Windows注册表包含大量的键值,而且这些键值可能被用户修改过,犯罪嫌疑犯也可能 借用它隐藏一些隐私,如用户密码。故,一些注册表键值在许多取证分析中都要被调查,它 们并无必要对应一个给定领域,但是它们和大量计算机调查相关。这些键值包括基本的系统 信息(谁使用了该系统,安装了什么应用程序)和关键系统领域的更详细信息(安装了哪些 硬件,装载了哪些驱动器)等,如运行过的程序、访问过的网站、编辑过的文档等。这些信 息可以为取证分析提供一定的帮助,而且容易获取,因为都是明文显示即通过注册表API函 数RegOpenKeyEx、RegEnumKeyEx、RegQueryValueEx即可获取。读者若对这些关键值路径感 兴趣,可参考《Initial Case Analysis using Windows Registry in Computer Forensics》。 本文重点论述注册表中隐藏的关键信息即用户ID和密码。 若系统事先设有密码,则用户输入密码进行系统之前, windows 会对此明文密码采用MD4 算法进行散列,然后将散列值与系统中保存的 HASH 值进行比对。这些 MD4 散列值即存放在 安全账户管理器SAM文件内。尽管SAM文件在最高权限下都无法访问,但所有的信息都存放 在注册表内,故完全可以在 SYSTEM 权限下,通过调用注册表 API 函数获取到这些信息。本 文是在参考Vincent Roch在 codeproject上发表的文章“Retrieve the Windows 7 Password Hash on the Fly”、 Dustin Hurlbut的文章 “Forensic Determination of a User’s Logon 的基础上并结合自己的理解分析完成的,探讨windows下SAM文件用户密码的破解分析。 1.SAM 结构分析 SAM结构中包含两内hash分别为:LanMan hash和NT hash,然而从Vista版本开始,因为 LM hash的安全性略低于后者,故已经基本上不再使用了。在注册表中用户信息都存放在 HKLM\SAM\SAM\Domains\Account\users\<userRID>下,其下的子键即每个账号的SID相对标 志符。每个账户下包含两个子项,F和V。V值包含了用户名、以及散列后的用户密码值等。 它实际上可以看作一个微型文件系统,包含多个指针即指向一个可变长度的结构体。每个指 针结构为一个12字节值,例如第2个12字节结构体,前4字节即用户数据起始相对偏移值(需 要加上偏移量0xCC),中间4字节表示用户名的长度,后4字节保留未用。如图1所示, 0xBC+0xCC=188,即偏移188处即为用户名。 图1. SAM文件中Adminitrator用户的SID结构 而且,图1中的12字节结构体中的第二个4字节包含特殊含义,即 0x BC 00 00 00 — ADMINISTRATIVE USER 0x D4 00 00 00 USER — ONLY PRIVILEGE LEVEL即受限制的用户权限 0x B0 00 00 00 — GUEST ACCOUNT “F”键值中保存的是一些登录记录,如上次登录时间、错误登录次数等。从上面的分 析,可知用户密码保存在SAM文件中,虽然系统默认下config文件受保护,但用户信息全部 存在在注册表内,故这里我们可以在SCM(服务控制管理器)内注册一个系统服务实现SAM 文件取证。 2.创建新服务实现SAM文件取证 当一个服务控制程序请求开启一个新的服务时,SCM开启服务的同时,向控制调度器发 送一个开始请求。控制调度器为服务创建一个新的线程来运行ServiceMain函数。代码如下: int APIENTRY WinMain(HINSTANCE hProg, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ /*SERVICE_TABLE_ENTRY 结构类型的数组,设置调用进程所提供的GetSAM服务的入口函 数及字符串名*/ SERVICE_TABLE_ENTRY DispatchTable[] = { {"GetSAM", (LPSERVICE_MAIN_FUNCTION)&GetSAMServiceMain}, {NULL, NULL} }; //连接程序主线程到服务控制管理程序 StartServiceCtrlDispatcher(DispatchTable); } DWORD WINAPI HandlerFunction(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext){ SERVICE_STATUS ServiceStatus; if (dwControl == SERVICE_CONTROL_SHUTDOWN || dwControl == SERVICE_CONTROL_STOP){ ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_STOPPED; |