文摘出处:
http://www.xfocus.net/articles/200411/749.html
Author : eyas
Email : eyas at xfous.org
Date : 2004-11-08
前段时间ADSL密码忘记了,但幸好还保存在拨号连接里面,于是到网上找了些星号密码
显示工具,可惜不起作用。后来找到一种名为dialupass的工具,这家伙不负重望把密码给
我还原出来了。(用的dialupass v2.42,我的系统是windows xp)
看起来dialupass非普通的星号密码显示工具,那它的原理是什么呢?上GOOGLE查了
一翻,没找到相关资料(可能是我用的关键字有问题)。 一生气便操起家伙(windbg)
准备把它大卸八块。郁闷的是,用windbg加载后,密码就还原不出来了,显示是星号。换替
补ollydbg上场,情况依旧。莫非这小工具有Anti-Debug功能?当时只是一丝怀疑,因为实
在不相信这样的小工具作者会花心思来保护。
后来在用s-ice跟踪的过程中,发现有这么一个调用:
GetProcAddress(xx, "IsDebugPresent")。
晕倒,原来真的有Anti-Debug功能,好在比较简单。统计了一下,总共有5处进行了
Anti-Debug检查。
情况查明了,便换回windbg来调试,在windbg里面下这么一个断点便可绕过Anti-Debug
检测:
bp KERNEL32!IsDebuggerPresent "g poi(esp);r eax=0;g"
花了些时间跟踪了一下,把dialupass恢复密码的流程都搞清楚了。这小程序猫腻还
挺多的,总结如下:
1. 关键函数不直接调用,而是用LoadLib***yA和GetProcAddress来获取函数地址
后再CALL。
2. 函数名是经过编码的,反汇编后看字符串是看不到的。
3. 关键地方一概用花指令来迷惑你和反汇编软件。
其实原理很简单,就是用rasapi32.dll里面的一些函数来获取拨号连接的一些信息,
再用 ADVAPI32!LsaRetrievePrivateData 函数来获取密码。
根据dialupasss的原理,写了个类似的工具,源代码参见后面的x_dialupass.c。
后来用"LsaRetrievePrivateData"和"RasDialParams"做关键字,重新在GOOGLE搜索了
一遍,找到一些类似的代码。
参考资源[1]和[2]的是俄罗斯人公布的演示代码,没有对LsaRetrievePrivateData返回
的数据进行拆分用户名和密码。参考资源[3]是日本人公布的完整的应用程序的代码,可惜
在对LsaRetrievePrivateData返回的数据进行拆分处理时存在BUG,导致有些情况下用户名
和密码取的不正确。
后来发现lsadump2 DUMP出来的数据里面包含了"LsaRetrievePrivateData"返回的数
据。lsadump2的原理大致如下:
1)插入一线程到lsass.exe进程
2)打开LSA Policy database
3)从注册表"HKLM/SECURITY/Policy/Secrets"中枚举子键
4)LsarOpenSecret
5)LsarQuerySecret
进一步跟踪后发现,其实ADVAPI32!LsaRetrievePrivateData是通过NdrClientCall2
发送RPC调用到lsass.exe进程,lsass.exe里面再调用LsarOpenSecret、LsarQuerySecret
来完成获取拨号连接信息过程的。(注:LsarOpenSecret里面有权限判断,非ADMIN组用
户是没有权限来调用ADVAPI32!LsaRetrievePrivateData的)
跟踪了一下LsarQuerySecret,发现它返回的数据其实是从注册表中读取。保存拨号
连接信息的注册表键值为:
HKLM/SECURITY/Policy/Secrets/RasDialParams!SID#0/CurrVal
SID对应的是用户的string SID。(“HKLM/SECURITY”这个键只有SYSTEM有权限读
写,连admin都没有权限)
LsarQuerySecret从注册表中读取出来数据后,接着调用LsapCrDecryptValue函数来
解密,对于同一台机器来说,解密时用的KEY始终都是固定的,这个KEY在lsasrv.dll里面
变量名为"_LsapDbSecretCipherKey"。在windows 2003里面,变量名不一样,对应的有两
个,分别为"LsapDbSecretCipherKeyWrite"和"LsapDbSecretCipherKeyRead",但这两个
变量里面的数据是一样的。
LsapCrDecryptValue用的似乎是标准DES算法,解密时主要流程如下:
lsasrv!LsapCrDecryptValue
|_ advapi32!SystemFunction005
|_ advapi32!DecryptDataLength
|_ advapi32!SystemFunction002
|_ advapi32!DES_ECB_LM
|_ advapi32!des
解密后,在"<<"标示处还有一个判断:
.text:785462F0 call _LsapCrDecryptValue@12
.text:785462F5 test eax, eax
.text:785462F7 mov [ebp+var_8], eax
.text:785462FA jl loc_785838E1
.text:78546300
.text:78546300 loc_78546300:
.text:78546300 cmp byte ptr [esi+45h], 0 <<<<<<<<<<<<
.text:78546304 jz short loc_7854632E
......
.text:7854632E loc_7854632E:
.text:7854632E lea eax, [ebp+var_10]
.text:78546331 push eax
.text:78546332 push [ebp+arg_8]
.text:78546335 push [ebp+var_C]
.text:78546338 call _LsapCrEncryptValue@12
假如[esi+45h]为0的话(esi是LsarOpenSecret函数返回的HANDLE),它会把解密后的
数据再进行一次加密,不管是2000还是2003,这时用的KEY始终都是固定为
“SystemLib***yDTC”。
lsadump2里面调用LsarOpenSecret得到的HANDLE,偏移0x45处值为1,所以
LsarQuerySecret函数返回的就是解密后的数据了。
而在调用ADVAPI32!LsaRetrievePrivateData时,LsarOpenSecret返回的HANDLE偏移
0x45处值为0x0,所以LsarQuerySecret返回的是解密后又加密的数据,所以在
ADVAPI32!LsaRetrievePrivateData里面还有一个对应的解密过程。相应的,
LsapCrEncryptValue加密的主要流程如下:
lsasrv!LsapCrEncryptValue
|_ advapi32!SystemFunction004
|_ advapi32!EncryptDataLength
|_ advapi32!SystemFunction001
|_ advapi32!DES_ECB_LM
|_ advapi32!des
开始我以为在同一版本的windows里面,_LsapDbSecretCipherKey是固定的,后来
发现我错了。那么这个_LsapDbSecretCipherKey是如何产生的?流程如下:
(1)调用ntdll!NtConnectPort打开 L"/Security/WxApiPort"
(2)调用ntdll!NtRequestWaitReplyPort得到一些数据
ebp-40处为NtRequestWaitReplyPort返回的LPCMESSAGE
kd> dd ebp-40
0006fcb8 00400028 00000002 000000dc 000000d8
0006fcc8 00000024 00000000 00000000 00000000
0006fcd8 00000001 00000010 00000010 fd317e3e
0006fce8 7e24e86d d12503d3 5f7d01a8 7665f528
kd> db ebp-14
0006fce4 3e 7e 31 fd 6d e8 24 7e-d3 03 25 d1 a8 01 7d 5f
(3)将上述"ebp-14"处的0x10字节数据COPY到lsasrv.dll里面的"_LsapDbSysKey"变量。
"_LsapDbSysKey"在不同的机器上面(即使版本相同)都是不一样的。它是怎么产生的?有
幸拜读了***sky的大作后(参考资源[4]),才明白这就是传说中的"SYSKEY"。用***sky
的代码验证一下:
c:/>getsyskey
3e 7e 31 fd 6d e8 24 7e d3 03 25 d1 a8 01 7d 5f
跟踪系统启动过程,可知道"/Security/WxApiPort"是由winlogon.exe进程创建的,然
后lsass进程通过这个LPC PORT从winlogon进程获取SYSKEY,随后winlogon进程会关闭这
个LPC PORT。所以在系统启动完成之后,用"Process Explorer"等工具是看不到这个
LPC PORT存在的,而且在winlogon和LSASS进程空间都搜索不到上述SYSKEY。
(4)从注册表"HKLM/SECURITY/Policy/PolSecretEncryptionKey"中读取出来一段数据,
调用函数_LsapDbDecryptKeyWithSyskey,把它用"_LsapDbSysKey"来解密,
"_LsapDbSecretCipherKey"就在解密完后的数据里面。("LsapDbDecryptKeyWithSyskey"函
数做的其实就是MD5和RC4运算)
了解原理后,我们就可以直接从注册表里面来获取拨号连接中的密码等数据了。但
有几个问题需要解决:
(1)原料。
Q:"HKLM/SECURITY"键只有SYSTEM有权限读写?
A:我们可以把代码插入到SYSTEM进程里面去运行,或者把这个键修改为ADMIN有
权限读,或者提升本进程权限。
(2)催化剂:)
Q: 如何获取"_LsapDbSysKey"?解密用的函数_LsapDbDecryptKeyWithSyskey为非导出函
数,怎么办?
A1: 用***sky的代码来获取SYSKEY,利用公开的MD5和RC4库函数来解密。
A2: 直接从lsass.exe进程里面搜索"_LsapDbSecretCipherKey",它的结构如下,
typedef struct _LSA_BLOB {
DWORD cbData;
DWORD cbMaxData;
BYTE* pbData;
} LSA_BLOB;
pbData指向存储KEY的地址,KEY长度固定为0x10字节,即cbData和cbMaxData都是固定
为0x10。所以从lsass进程的空间里面搜索"/x10/x00/x00/x00/x10/x00/x00/x00"即可找到
正确的KEY。结果可能会有多个,可以把所有搜索到的KEY都试一下,总有一个正确的。
(3)工具
Q: 解密函数LsapCrDecryptValue为非导出函数,怎么办?
A: 或许可以根据特征码来搜索,但总觉得不太可靠。幸好,LsapCrDecryptValue
调用的advapi32!SystemFunction005是导出函数:)。或者直接利用公开的DES库函数,
自己来运算。
x_dialupass2.cpp中的代码演示了直接从注册表中读取数据并解密之的过程,没有
太多实际意义,just for fun!
-=-=-=-=-=-=-=-=-=-= x_dialupass.c -=-=-=-=-=-=-=-=-=-=
/*
演示还原NT平台上拨号连接的密码
可运行于windows 2000/xp/2003
原理基于分析dialupass v2.42
eyas at xfocus.org
http://www.xfocus.net
2004-10-01
FileName: x_dialupass.c
*/
#define WINVER 0x500
#define _WIN32_WINNT 0x0500
#i nclude <windows.h>
#i nclude <stdio.h>
#i nclude <ras.h>
#i nclude <raserror.h>
#i nclude <Ntsecapi.h>
#i nclude <Userenv.h>
#i nclude <Sddl.h>
#pragma comment(lib,"Rasapi32.lib")
#pragma comment(lib,"advapi32.lib")
#pragma comment(lib,"UserEnv.lib")
unsigned char private_data[0x500];
int data_len;
unsigned char * get_real_pass(unsigned char *user, DWORD dwDialParamsUID)
{
int i, j;
unsigned char *p, szDialParamsUID[52], *pass=NULL;
_snprintf(szDialParamsUID, sizeof(szDialParamsUID),
"%d", dwDialParamsUID);
p = private_data;
for(i=0;i<data_len;i++)
{
if(strcmp(&p[i], szDialParamsUID) == 0 )
{
for(j=i;j<data_len;j++)
{
if(strcmp(&p[j], user) == 0 )
{
pass = p + j + strlen(user) + 1;
break;
}
}
break;
}
}
return pass;
}
void main()
{
LPRASENTRYNAME lpRasEntryName;
LPRASDIALPARAMS lpRasDialParams;
DWORD cb, nRet, i, cEntries;
BOOL b;
char szPhoneBook1[512], szPhoneBook2[512],
szUserName[128], szDomainName[128];
DWORD dwSize, dwDialParamsUID, dwTmp;
PSID pSid = NULL;
SID_NAME_USE peUse;
LSA_OBJECT_ATTRIBUTES lsa_object_attr;
LSA_HANDLE lsa_handle;
PLSA_UNICODE_STRING plsa_private_data;
LSA_UNICODE_STRING lsa_keyname;
NTSTATUS status;
int ret;
unsigned char *pass;
WCHAR *sid;
printf("dialup password recover tool for win 2k/xp/2003/n"
"code by eyas at xfocus.org/n"
" http://www.xfocus.net /n"
"2004-10-01/n/n");
//get current user's string sid
dwSize = sizeof(szUserName);
GetUserName(szUserName, &dwSize);
dwSize = 0;
dwTmp = sizeof(szDomainName);
LookupAccountName(NULL, szUserName, pSid, &dwSize, szDomainName,
&dwTmp, &peUse);
if(!dwSize)
{
printf("[-] LookupAccountName failed./n");
return;
}
pSid = (PSID)malloc(dwSize);
LookupAccountName(NULL, szUserName, pSid, &dwSize, szDomainName,
&dwTmp, &peUse);
ConvertSidToStringSidW(pSid, &sid);
memset(&lsa_object_attr, 0, sizeof(lsa_object_attr));
lsa_object_attr.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
LsaOpenPolicy(0, &lsa_object_attr, 0x800, &lsa_handle);
plsa_private_data = (PLSA_UNICODE_STRING)malloc(sizeof(LSA_UNICODE_STRING));
plsa_private_data->Length = 0x500;
plsa_private_data->MaximumLength = 0x500;
plsa_private_data->Buffer = (PWSTR)malloc(0x500);
lsa_keyname.MaximumLength = 0x200;
lsa_keyname.Buffer = (PWSTR)malloc(0x200);
wcscpy(lsa_keyname.Buffer,L"RasDialParams!");
wcscat(lsa_keyname.Buffer, sid);
wcscat(lsa_keyname.Buffer, L"#0");
lsa_keyname.Length = wcslen(lsa_keyname.Buffer) * 2;
//get current user's dialup info
status = LsaRetrievePrivateData(lsa_handle,
&lsa_keyname,
&plsa_private_data);
LsaClose(lsa_handle);
if(status != 0)
{
printf("[-] LsaRetrievePrivateData failed: %d/n",
LsaNtStatusToWinError(status));
return;
}
ret = WideCharToMultiByte(0, 0, plsa_private_data->Buffer,
plsa_private_data->Length,
private_data, sizeof(private_data), 0, 0);
if(ret == 0)
{
printf("[-] WideCharToMultiByte failed:%d/n", GetLastError());
return;
}
data_len = ret;
//get phone book name
GetEnvironmentVariable("ALLUSERSPROFILE", szPhoneBook1,
sizeof(szPhoneBook1)-200);
GetEnvironmentVariable("USERPROFILE", szPhoneBook2,
sizeof(szPhoneBook2)-200);
strcat(szPhoneBook1,
"//Application Data//Microsoft//Network"
"//Connections//pbk//rasphone.pbk");
strcat(szPhoneBook2,
"//Application Data//Microsoft//Network"
"//Connections//pbk//rasphone.pbk");
lpRasEntryName = (LPRASENTRYNAME)GlobalAlloc(GPTR, sizeof(RASENTRYNAME));
lpRasEntryName->dwSize = sizeof(RASENTRYNAME);
cb = sizeof(RASENTRYNAME);
if ((nRet = RasEnumEntries(NULL, NULL, lpRasEntryName, &cb, &cEntries))
== ERROR_BUFFER_TOO_SMALL)
{
lpRasEntryName = (LPRASENTRYNAME)GlobalAlloc(GPTR, cb);
lpRasEntryName->dwSize = sizeof(RASENTRYNAME);
}
// Calling RasEnumEntries to enumerate the phone-book entries
nRet = RasEnumEntries(NULL, NULL, lpRasEntryName, &cb, &cEntries);
if (nRet != ERROR_SUCCESS)
{
printf("[-] RasEnumEntries failed: Error %d/n", nRet);
return;
}
for(i=0;i < cEntries;i++)
{
lpRasDialParams = malloc(sizeof(RASDIALPARAMS));
strcpy(lpRasDialParams->szEntryName, lpRasEntryName->szEntryName);
lpRasDialParams->dwSize = sizeof(RASDIALPARAMS);
RasGetEntryDialParams(0, lpRasDialParams, &b);
dwDialParamsUID = GetPrivateProfileInt(lpRasEntryName->szEntryName,
"DialParamsUID", 0, szPhoneBook1);
if(dwDialParamsUID == 0)
{
dwDialParamsUID = GetPrivateProfileInt(lpRasEntryName->szEntryName,
"DialParamsUID", 0, szPhoneBook2);
if(dwDialParamsUID == 0)
{
printf("[-] Can't get DialParamsUID from PhoneBook./n");
return;
}
}
pass = get_real_pass(lpRasDialParams->szUserName, dwDialParamsUID);
printf(
"-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=/n"
"EntryName : %s/n"
"UserName : %s/n"
"PassWord : %s/n/n",
lpRasEntryName->szEntryName,
lpRasDialParams->szUserName,
pass);
free(lpRasDialParams);
lpRasEntryName++;
}
}
-=-=-=-=-=-=-=-=-=-= code end -=-=-=-=-=-=-=-=-=-=
-=-=-=-=-=-=-=-=-=-= x_dialupass2.cpp -=-=-=-=-=-=-=-=-=-=
/*
演示还原NT平台拨号连接密码
原理:直接从注册表中读取加密后的数据,解密之。
可运行于windows 2000/xp/2003平台,必须有权限读取注册表 "HKLM/SECURITY"。
eyas at xfocus.org
http://www.xfocus.net
2004-10-01
*/
#i nclude <Windows.h>
#i nclude <stdio.h>
#i nclude <Psapi.h>
#pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "psapi.lib")
//抄袭tombkeeper的代码:)
#define FCHK(a) if (!(a)) {printf(#a " failed %d/n", GetLastError()); return
0;}
typedef struct _LSA_BLOB {
DWORD cbData;
DWORD cbMaxData;
BYTE* pbData;
} LSA_BLOB;
typedef int (WINAPI *PSystemFunction005)(
LSA_BLOB* pDataIn,
LSA_BLOB* pDataKey,
LSA_BLOB* pDataOut
);
PSystemFunction005 SystemFunction005;
DWORD dwFlag=0;
//来自lsadump2中的dumplsa.c
int myisprint (int ch)
{
return ((ch >= ' ') && (ch <= '~'));
}
//来自lsadump2中的dumplsa.c
void
dump_bytes (unsigned char *p, size_t sz)
{
char szDumpBuff[256];
if(sz==0)
return;
while (sz > 16) {
_snprintf (szDumpBuff, sizeof (szDumpBuff),
" %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X
%02X %02X %02X %02X %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c/n",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15],
myisprint(p[0]) ? p[0] : '.',
myisprint(p[1]) ? p[1] : '.',
myisprint(p[2]) ? p[2] : '.',
myisprint(p[3]) ? p[3] : '.',
myisprint(p[4]) ? p[4] : '.',
myisprint(p[5]) ? p[5] : '.',
myisprint(p[6]) ? p[6] : '.',
myisprint(p[7]) ? p[7] : '.',
myisprint(p[8]) ? p[8] : '.',
myisprint(p[9]) ? p[9] : '.',
myisprint(p[10]) ? p[10] : '.',
myisprint(p[11]) ? p[11] : '.',
myisprint(p[12]) ? p[12] : '.',
myisprint(p[13]) ? p[13] : '.',
myisprint(p[14]) ? p[14] : '.',
myisprint(p[15]) ? p[15] : '.');
printf ("%s", szDumpBuff);
p+=16;
sz -= 16;
}
if (sz) {
char buf[17];
int i = 0;
int j = 16 - sz;
memset (buf, 0, sizeof (buf));
szDumpBuff[0] = 0;
while (sz--) {
_snprintf (szDumpBuff+strlen (szDumpBuff),
sizeof (szDumpBuff) - strlen (szDumpBuff),
" %02X", *p);
if (myisprint (*p))
buf[i++] = *p;
else
buf[i++] = '.';
p++;
}
_snprintf (szDumpBuff+strlen (szDumpBuff),
sizeof (szDumpBuff)-strlen (szDumpBuff),
"%*s%s/n", j*3 + 2, "", buf);
printf ("%s", szDumpBuff);
}
}
DWORD search_LsapDbSecretCipherKey(BYTE **ppKey, DWORD pid)
{
HANDLE hLsass, hLsasrv;
DWORD dwRead, i, dwAddr;
BYTE *pImage = NULL;
MODULEINFO mod;
BOOL bRet = FALSE;
DWORD dwCount = 0, dwMaxCount=100;
FCHK ( (hLsasrv = LoadLib***y("lsasrv.dll")) );
FCHK ( GetModuleInformation(GetCurrentProcess(), (HMODULE)hLsasrv,
&mod, sizeof(mod)) );
FCHK ( hLsass = OpenProcess(PROCESS_VM_READ, FALSE, pid) );
pImage = (BYTE*)malloc(mod.SizeOfImage);
ReadProcessMemory(hLsass, (BYTE*)hLsasrv,
pImage, mod.SizeOfImage-0x10, &dwRead);
*ppKey = (BYTE*)malloc(dwMaxCount*0x10);
__try
{
for(i=0;i<mod.SizeOfImage;i++)
{
if( memcmp(&pImage[i], "/x10/x00/x00/x00/x10/x00/x00/x00", 8) == 0)
{
dwAddr = *(DWORD *)(&pImage[i+8]);
if( ReadProcessMemory(hLsass, (LPCVOID)dwAddr,
&(*ppKey[dwCount*0x10]), 0x10, &dwRead) )
{
dwCount++;
}
}
}//end of for
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return dwCount;
}
return dwCount;
}
int main(int argc, char **argv)
{
int ret,i,j;
HMODULE hAdvApi32;
HKEY hKeySecrets;
HKEY hKey;
DWORD dwType;
char Data[0x500] = {0};
BYTE *pKey;
DWORD dwSize;
LSA_BLOB LSADataIn;
LSA_BLOB LSADataOut;
LSA_BLOB LSADataKey;
char szSecret[500];
char szSubKey[0x500];
DWORD dwErr, dwCount=0;
if(argc!=2)
{
printf("Usage: %s <pid of lsass.exe>/n", argv[0]);
return 0;
}
FCHK ((hAdvApi32 = LoadLib***y("advapi32.dll")));
FCHK ((SystemFunction005 = (PSystemFunction005)
GetProcAddress (hAdvApi32, "SystemFunction005")) != NULL);
FCHK ((RegOpenKeyEx (HKEY_LOCAL_MACHINE,
"SECURITY//Policy//Secrets",
0, KEY_READ, &hKeySecrets) == ERROR_SUCCESS))
FCHK ( ( dwCount = search_LsapDbSecretCipherKey(&pKey, atoi(argv[1])) ) != 0
);
printf("Search /"LsapDbSecretCipherKey/" return: %d/n", dwCount);
for(j=0;j<dwCount;j++)
{
printf("LsapDbSecretCipherKey [%d]/n", j);
dump_bytes(&pKey[j*0x10], 0x10);
LSADataKey.cbData = LSADataKey.cbMaxData = 0x10;
LSADataKey.pbData = &pKey[j*0x10];
//search our target
for (i=0; TRUE; i++)
{
dwErr = RegEnumKeyA (hKeySecrets, i, szSecret, sizeof (szSecret));
if (dwErr != ERROR_SUCCESS)
//
// No More Secrets
//
break;
printf("/n%s/n", szSecret);
//open it
_snprintf(szSubKey, sizeof(szSubKey),
"SECURITY//Policy//Secrets//%s//CurrVal", szSecret);
if (ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szSubKey,
0,
KEY_READ,
&hKey
) != ERROR_SUCCESS )
continue;
dwSize = sizeof(Data);
FCHK ((ret = RegQueryValueEx(hKey,
"",
NULL,
&dwType,
(LPBYTE)Data,
&dwSize) == ERROR_SUCCESS ))
LSADataIn.pbData = (BYTE *)Data + 0xC; //密文从第0xC位开始
LSADataIn.cbData = dwSize-0xC;
LSADataIn.cbMaxData = LSADataIn.cbData;
//dump_bytes(LSADataIn.pbData, LSADataIn.cbData);
LSADataOut.cbData = 0;
LSADataOut.cbMaxData = 0;
LSADataOut.pbData = NULL;
SystemFunction005(&LSADataIn, &LSADataKey, &LSADataOut);
if (LSADataOut.cbData == 0)
{
printf("null/n");
continue;
}
FCHK ((LSADataOut.pbData = (BYTE*)malloc(LSADataOut.cbData) ) !=
NULL);
LSADataOut.cbMaxData = LSADataOut.cbData;
SystemFunction005(&LSADataIn, &LSADataKey, &LSADataOut);
dump_bytes(LSADataOut.pbData, LSADataOut.cbData);
free(LSADataOut.pbData);
}//end of for
printf("Press any key to use next /"LsapDbSecretCipherKey/", or Ctrl+C
to exit./n");
getchar();
}
if(pKey)
free(pKey);
return 0;
}
-=-=-=-=-=-=-=-=-=-= code end -=-=-=-=-=-=-=-=-=-=
参考资源:
[1] http://www.lwteam.ru/modules/news/article.php?storyid=167
2003-06-24 by Cmeptb
[2] http://www.wasm.ru/forum/index.php?action=vthread&forum=12&topic=4873
2004-01-10 by ??
[3] http://homepage2.nifty.com/spw/software/rtrick/
2004-01-05 by ???
[4] SAM的散列存储加密解密算法以及SYSKEY的计算
http://www.xfocus.net/articles/200306/550.html
Author : eyas
Email : eyas at xfous.org
Date : 2004-11-08
前段时间ADSL密码忘记了,但幸好还保存在拨号连接里面,于是到网上找了些星号密码
显示工具,可惜不起作用。后来找到一种名为dialupass的工具,这家伙不负重望把密码给
我还原出来了。(用的dialupass v2.42,我的系统是windows xp)
看起来dialupass非普通的星号密码显示工具,那它的原理是什么呢?上GOOGLE查了
一翻,没找到相关资料(可能是我用的关键字有问题)。 一生气便操起家伙(windbg)
准备把它大卸八块。郁闷的是,用windbg加载后,密码就还原不出来了,显示是星号。换替
补ollydbg上场,情况依旧。莫非这小工具有Anti-Debug功能?当时只是一丝怀疑,因为实
在不相信这样的小工具作者会花心思来保护。
后来在用s-ice跟踪的过程中,发现有这么一个调用:
GetProcAddress(xx, "IsDebugPresent")。
晕倒,原来真的有Anti-Debug功能,好在比较简单。统计了一下,总共有5处进行了
Anti-Debug检查。
情况查明了,便换回windbg来调试,在windbg里面下这么一个断点便可绕过Anti-Debug
检测:
bp KERNEL32!IsDebuggerPresent "g poi(esp);r eax=0;g"
花了些时间跟踪了一下,把dialupass恢复密码的流程都搞清楚了。这小程序猫腻还
挺多的,总结如下:
1. 关键函数不直接调用,而是用LoadLib***yA和GetProcAddress来获取函数地址
后再CALL。
2. 函数名是经过编码的,反汇编后看字符串是看不到的。
3. 关键地方一概用花指令来迷惑你和反汇编软件。
其实原理很简单,就是用rasapi32.dll里面的一些函数来获取拨号连接的一些信息,
再用 ADVAPI32!LsaRetrievePrivateData 函数来获取密码。
根据dialupasss的原理,写了个类似的工具,源代码参见后面的x_dialupass.c。
后来用"LsaRetrievePrivateData"和"RasDialParams"做关键字,重新在GOOGLE搜索了
一遍,找到一些类似的代码。
参考资源[1]和[2]的是俄罗斯人公布的演示代码,没有对LsaRetrievePrivateData返回
的数据进行拆分用户名和密码。参考资源[3]是日本人公布的完整的应用程序的代码,可惜
在对LsaRetrievePrivateData返回的数据进行拆分处理时存在BUG,导致有些情况下用户名
和密码取的不正确。
后来发现lsadump2 DUMP出来的数据里面包含了"LsaRetrievePrivateData"返回的数
据。lsadump2的原理大致如下:
1)插入一线程到lsass.exe进程
2)打开LSA Policy database
3)从注册表"HKLM/SECURITY/Policy/Secrets"中枚举子键
4)LsarOpenSecret
5)LsarQuerySecret
进一步跟踪后发现,其实ADVAPI32!LsaRetrievePrivateData是通过NdrClientCall2
发送RPC调用到lsass.exe进程,lsass.exe里面再调用LsarOpenSecret、LsarQuerySecret
来完成获取拨号连接信息过程的。(注:LsarOpenSecret里面有权限判断,非ADMIN组用
户是没有权限来调用ADVAPI32!LsaRetrievePrivateData的)
跟踪了一下LsarQuerySecret,发现它返回的数据其实是从注册表中读取。保存拨号
连接信息的注册表键值为:
HKLM/SECURITY/Policy/Secrets/RasDialParams!SID#0/CurrVal
SID对应的是用户的string SID。(“HKLM/SECURITY”这个键只有SYSTEM有权限读
写,连admin都没有权限)
LsarQuerySecret从注册表中读取出来数据后,接着调用LsapCrDecryptValue函数来
解密,对于同一台机器来说,解密时用的KEY始终都是固定的,这个KEY在lsasrv.dll里面
变量名为"_LsapDbSecretCipherKey"。在windows 2003里面,变量名不一样,对应的有两
个,分别为"LsapDbSecretCipherKeyWrite"和"LsapDbSecretCipherKeyRead",但这两个
变量里面的数据是一样的。
LsapCrDecryptValue用的似乎是标准DES算法,解密时主要流程如下:
lsasrv!LsapCrDecryptValue
|_ advapi32!SystemFunction005
|_ advapi32!DecryptDataLength
|_ advapi32!SystemFunction002
|_ advapi32!DES_ECB_LM
|_ advapi32!des
解密后,在"<<"标示处还有一个判断:
.text:785462F0 call _LsapCrDecryptValue@12
.text:785462F5 test eax, eax
.text:785462F7 mov [ebp+var_8], eax
.text:785462FA jl loc_785838E1
.text:78546300
.text:78546300 loc_78546300:
.text:78546300 cmp byte ptr [esi+45h], 0 <<<<<<<<<<<<
.text:78546304 jz short loc_7854632E
......
.text:7854632E loc_7854632E:
.text:7854632E lea eax, [ebp+var_10]
.text:78546331 push eax
.text:78546332 push [ebp+arg_8]
.text:78546335 push [ebp+var_C]
.text:78546338 call _LsapCrEncryptValue@12
假如[esi+45h]为0的话(esi是LsarOpenSecret函数返回的HANDLE),它会把解密后的
数据再进行一次加密,不管是2000还是2003,这时用的KEY始终都是固定为
“SystemLib***yDTC”。
lsadump2里面调用LsarOpenSecret得到的HANDLE,偏移0x45处值为1,所以
LsarQuerySecret函数返回的就是解密后的数据了。
而在调用ADVAPI32!LsaRetrievePrivateData时,LsarOpenSecret返回的HANDLE偏移
0x45处值为0x0,所以LsarQuerySecret返回的是解密后又加密的数据,所以在
ADVAPI32!LsaRetrievePrivateData里面还有一个对应的解密过程。相应的,
LsapCrEncryptValue加密的主要流程如下:
lsasrv!LsapCrEncryptValue
|_ advapi32!SystemFunction004
|_ advapi32!EncryptDataLength
|_ advapi32!SystemFunction001
|_ advapi32!DES_ECB_LM
|_ advapi32!des
开始我以为在同一版本的windows里面,_LsapDbSecretCipherKey是固定的,后来
发现我错了。那么这个_LsapDbSecretCipherKey是如何产生的?流程如下:
(1)调用ntdll!NtConnectPort打开 L"/Security/WxApiPort"
(2)调用ntdll!NtRequestWaitReplyPort得到一些数据
ebp-40处为NtRequestWaitReplyPort返回的LPCMESSAGE
kd> dd ebp-40
0006fcb8 00400028 00000002 000000dc 000000d8
0006fcc8 00000024 00000000 00000000 00000000
0006fcd8 00000001 00000010 00000010 fd317e3e
0006fce8 7e24e86d d12503d3 5f7d01a8 7665f528
kd> db ebp-14
0006fce4 3e 7e 31 fd 6d e8 24 7e-d3 03 25 d1 a8 01 7d 5f
(3)将上述"ebp-14"处的0x10字节数据COPY到lsasrv.dll里面的"_LsapDbSysKey"变量。
"_LsapDbSysKey"在不同的机器上面(即使版本相同)都是不一样的。它是怎么产生的?有
幸拜读了***sky的大作后(参考资源[4]),才明白这就是传说中的"SYSKEY"。用***sky
的代码验证一下:
c:/>getsyskey
3e 7e 31 fd 6d e8 24 7e d3 03 25 d1 a8 01 7d 5f
跟踪系统启动过程,可知道"/Security/WxApiPort"是由winlogon.exe进程创建的,然
后lsass进程通过这个LPC PORT从winlogon进程获取SYSKEY,随后winlogon进程会关闭这
个LPC PORT。所以在系统启动完成之后,用"Process Explorer"等工具是看不到这个
LPC PORT存在的,而且在winlogon和LSASS进程空间都搜索不到上述SYSKEY。
(4)从注册表"HKLM/SECURITY/Policy/PolSecretEncryptionKey"中读取出来一段数据,
调用函数_LsapDbDecryptKeyWithSyskey,把它用"_LsapDbSysKey"来解密,
"_LsapDbSecretCipherKey"就在解密完后的数据里面。("LsapDbDecryptKeyWithSyskey"函
数做的其实就是MD5和RC4运算)
了解原理后,我们就可以直接从注册表里面来获取拨号连接中的密码等数据了。但
有几个问题需要解决:
(1)原料。
Q:"HKLM/SECURITY"键只有SYSTEM有权限读写?
A:我们可以把代码插入到SYSTEM进程里面去运行,或者把这个键修改为ADMIN有
权限读,或者提升本进程权限。
(2)催化剂:)
Q: 如何获取"_LsapDbSysKey"?解密用的函数_LsapDbDecryptKeyWithSyskey为非导出函
数,怎么办?
A1: 用***sky的代码来获取SYSKEY,利用公开的MD5和RC4库函数来解密。
A2: 直接从lsass.exe进程里面搜索"_LsapDbSecretCipherKey",它的结构如下,
typedef struct _LSA_BLOB {
DWORD cbData;
DWORD cbMaxData;
BYTE* pbData;
} LSA_BLOB;
pbData指向存储KEY的地址,KEY长度固定为0x10字节,即cbData和cbMaxData都是固定
为0x10。所以从lsass进程的空间里面搜索"/x10/x00/x00/x00/x10/x00/x00/x00"即可找到
正确的KEY。结果可能会有多个,可以把所有搜索到的KEY都试一下,总有一个正确的。
(3)工具
Q: 解密函数LsapCrDecryptValue为非导出函数,怎么办?
A: 或许可以根据特征码来搜索,但总觉得不太可靠。幸好,LsapCrDecryptValue
调用的advapi32!SystemFunction005是导出函数:)。或者直接利用公开的DES库函数,
自己来运算。
x_dialupass2.cpp中的代码演示了直接从注册表中读取数据并解密之的过程,没有
太多实际意义,just for fun!
-=-=-=-=-=-=-=-=-=-= x_dialupass.c -=-=-=-=-=-=-=-=-=-=
/*
演示还原NT平台上拨号连接的密码
可运行于windows 2000/xp/2003
原理基于分析dialupass v2.42
eyas at xfocus.org
http://www.xfocus.net
2004-10-01
FileName: x_dialupass.c
*/
#define WINVER 0x500
#define _WIN32_WINNT 0x0500
#i nclude <windows.h>
#i nclude <stdio.h>
#i nclude <ras.h>
#i nclude <raserror.h>
#i nclude <Ntsecapi.h>
#i nclude <Userenv.h>
#i nclude <Sddl.h>
#pragma comment(lib,"Rasapi32.lib")
#pragma comment(lib,"advapi32.lib")
#pragma comment(lib,"UserEnv.lib")
unsigned char private_data[0x500];
int data_len;
unsigned char * get_real_pass(unsigned char *user, DWORD dwDialParamsUID)
{
int i, j;
unsigned char *p, szDialParamsUID[52], *pass=NULL;
_snprintf(szDialParamsUID, sizeof(szDialParamsUID),
"%d", dwDialParamsUID);
p = private_data;
for(i=0;i<data_len;i++)
{
if(strcmp(&p[i], szDialParamsUID) == 0 )
{
for(j=i;j<data_len;j++)
{
if(strcmp(&p[j], user) == 0 )
{
pass = p + j + strlen(user) + 1;
break;
}
}
break;
}
}
return pass;
}
void main()
{
LPRASENTRYNAME lpRasEntryName;
LPRASDIALPARAMS lpRasDialParams;
DWORD cb, nRet, i, cEntries;
BOOL b;
char szPhoneBook1[512], szPhoneBook2[512],
szUserName[128], szDomainName[128];
DWORD dwSize, dwDialParamsUID, dwTmp;
PSID pSid = NULL;
SID_NAME_USE peUse;
LSA_OBJECT_ATTRIBUTES lsa_object_attr;
LSA_HANDLE lsa_handle;
PLSA_UNICODE_STRING plsa_private_data;
LSA_UNICODE_STRING lsa_keyname;
NTSTATUS status;
int ret;
unsigned char *pass;
WCHAR *sid;
printf("dialup password recover tool for win 2k/xp/2003/n"
"code by eyas at xfocus.org/n"
" http://www.xfocus.net /n"
"2004-10-01/n/n");
//get current user's string sid
dwSize = sizeof(szUserName);
GetUserName(szUserName, &dwSize);
dwSize = 0;
dwTmp = sizeof(szDomainName);
LookupAccountName(NULL, szUserName, pSid, &dwSize, szDomainName,
&dwTmp, &peUse);
if(!dwSize)
{
printf("[-] LookupAccountName failed./n");
return;
}
pSid = (PSID)malloc(dwSize);
LookupAccountName(NULL, szUserName, pSid, &dwSize, szDomainName,
&dwTmp, &peUse);
ConvertSidToStringSidW(pSid, &sid);
memset(&lsa_object_attr, 0, sizeof(lsa_object_attr));
lsa_object_attr.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
LsaOpenPolicy(0, &lsa_object_attr, 0x800, &lsa_handle);
plsa_private_data = (PLSA_UNICODE_STRING)malloc(sizeof(LSA_UNICODE_STRING));
plsa_private_data->Length = 0x500;
plsa_private_data->MaximumLength = 0x500;
plsa_private_data->Buffer = (PWSTR)malloc(0x500);
lsa_keyname.MaximumLength = 0x200;
lsa_keyname.Buffer = (PWSTR)malloc(0x200);
wcscpy(lsa_keyname.Buffer,L"RasDialParams!");
wcscat(lsa_keyname.Buffer, sid);
wcscat(lsa_keyname.Buffer, L"#0");
lsa_keyname.Length = wcslen(lsa_keyname.Buffer) * 2;
//get current user's dialup info
status = LsaRetrievePrivateData(lsa_handle,
&lsa_keyname,
&plsa_private_data);
LsaClose(lsa_handle);
if(status != 0)
{
printf("[-] LsaRetrievePrivateData failed: %d/n",
LsaNtStatusToWinError(status));
return;
}
ret = WideCharToMultiByte(0, 0, plsa_private_data->Buffer,
plsa_private_data->Length,
private_data, sizeof(private_data), 0, 0);
if(ret == 0)
{
printf("[-] WideCharToMultiByte failed:%d/n", GetLastError());
return;
}
data_len = ret;
//get phone book name
GetEnvironmentVariable("ALLUSERSPROFILE", szPhoneBook1,
sizeof(szPhoneBook1)-200);
GetEnvironmentVariable("USERPROFILE", szPhoneBook2,
sizeof(szPhoneBook2)-200);
strcat(szPhoneBook1,
"//Application Data//Microsoft//Network"
"//Connections//pbk//rasphone.pbk");
strcat(szPhoneBook2,
"//Application Data//Microsoft//Network"
"//Connections//pbk//rasphone.pbk");
lpRasEntryName = (LPRASENTRYNAME)GlobalAlloc(GPTR, sizeof(RASENTRYNAME));
lpRasEntryName->dwSize = sizeof(RASENTRYNAME);
cb = sizeof(RASENTRYNAME);
if ((nRet = RasEnumEntries(NULL, NULL, lpRasEntryName, &cb, &cEntries))
== ERROR_BUFFER_TOO_SMALL)
{
lpRasEntryName = (LPRASENTRYNAME)GlobalAlloc(GPTR, cb);
lpRasEntryName->dwSize = sizeof(RASENTRYNAME);
}
// Calling RasEnumEntries to enumerate the phone-book entries
nRet = RasEnumEntries(NULL, NULL, lpRasEntryName, &cb, &cEntries);
if (nRet != ERROR_SUCCESS)
{
printf("[-] RasEnumEntries failed: Error %d/n", nRet);
return;
}
for(i=0;i < cEntries;i++)
{
lpRasDialParams = malloc(sizeof(RASDIALPARAMS));
strcpy(lpRasDialParams->szEntryName, lpRasEntryName->szEntryName);
lpRasDialParams->dwSize = sizeof(RASDIALPARAMS);
RasGetEntryDialParams(0, lpRasDialParams, &b);
dwDialParamsUID = GetPrivateProfileInt(lpRasEntryName->szEntryName,
"DialParamsUID", 0, szPhoneBook1);
if(dwDialParamsUID == 0)
{
dwDialParamsUID = GetPrivateProfileInt(lpRasEntryName->szEntryName,
"DialParamsUID", 0, szPhoneBook2);
if(dwDialParamsUID == 0)
{
printf("[-] Can't get DialParamsUID from PhoneBook./n");
return;
}
}
pass = get_real_pass(lpRasDialParams->szUserName, dwDialParamsUID);
printf(
"-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=/n"
"EntryName : %s/n"
"UserName : %s/n"
"PassWord : %s/n/n",
lpRasEntryName->szEntryName,
lpRasDialParams->szUserName,
pass);
free(lpRasDialParams);
lpRasEntryName++;
}
}
-=-=-=-=-=-=-=-=-=-= code end -=-=-=-=-=-=-=-=-=-=
-=-=-=-=-=-=-=-=-=-= x_dialupass2.cpp -=-=-=-=-=-=-=-=-=-=
/*
演示还原NT平台拨号连接密码
原理:直接从注册表中读取加密后的数据,解密之。
可运行于windows 2000/xp/2003平台,必须有权限读取注册表 "HKLM/SECURITY"。
eyas at xfocus.org
http://www.xfocus.net
2004-10-01
*/
#i nclude <Windows.h>
#i nclude <stdio.h>
#i nclude <Psapi.h>
#pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "psapi.lib")
//抄袭tombkeeper的代码:)
#define FCHK(a) if (!(a)) {printf(#a " failed %d/n", GetLastError()); return
0;}
typedef struct _LSA_BLOB {
DWORD cbData;
DWORD cbMaxData;
BYTE* pbData;
} LSA_BLOB;
typedef int (WINAPI *PSystemFunction005)(
LSA_BLOB* pDataIn,
LSA_BLOB* pDataKey,
LSA_BLOB* pDataOut
);
PSystemFunction005 SystemFunction005;
DWORD dwFlag=0;
//来自lsadump2中的dumplsa.c
int myisprint (int ch)
{
return ((ch >= ' ') && (ch <= '~'));
}
//来自lsadump2中的dumplsa.c
void
dump_bytes (unsigned char *p, size_t sz)
{
char szDumpBuff[256];
if(sz==0)
return;
while (sz > 16) {
_snprintf (szDumpBuff, sizeof (szDumpBuff),
" %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X
%02X %02X %02X %02X %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c/n",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15],
myisprint(p[0]) ? p[0] : '.',
myisprint(p[1]) ? p[1] : '.',
myisprint(p[2]) ? p[2] : '.',
myisprint(p[3]) ? p[3] : '.',
myisprint(p[4]) ? p[4] : '.',
myisprint(p[5]) ? p[5] : '.',
myisprint(p[6]) ? p[6] : '.',
myisprint(p[7]) ? p[7] : '.',
myisprint(p[8]) ? p[8] : '.',
myisprint(p[9]) ? p[9] : '.',
myisprint(p[10]) ? p[10] : '.',
myisprint(p[11]) ? p[11] : '.',
myisprint(p[12]) ? p[12] : '.',
myisprint(p[13]) ? p[13] : '.',
myisprint(p[14]) ? p[14] : '.',
myisprint(p[15]) ? p[15] : '.');
printf ("%s", szDumpBuff);
p+=16;
sz -= 16;
}
if (sz) {
char buf[17];
int i = 0;
int j = 16 - sz;
memset (buf, 0, sizeof (buf));
szDumpBuff[0] = 0;
while (sz--) {
_snprintf (szDumpBuff+strlen (szDumpBuff),
sizeof (szDumpBuff) - strlen (szDumpBuff),
" %02X", *p);
if (myisprint (*p))
buf[i++] = *p;
else
buf[i++] = '.';
p++;
}
_snprintf (szDumpBuff+strlen (szDumpBuff),
sizeof (szDumpBuff)-strlen (szDumpBuff),
"%*s%s/n", j*3 + 2, "", buf);
printf ("%s", szDumpBuff);
}
}
DWORD search_LsapDbSecretCipherKey(BYTE **ppKey, DWORD pid)
{
HANDLE hLsass, hLsasrv;
DWORD dwRead, i, dwAddr;
BYTE *pImage = NULL;
MODULEINFO mod;
BOOL bRet = FALSE;
DWORD dwCount = 0, dwMaxCount=100;
FCHK ( (hLsasrv = LoadLib***y("lsasrv.dll")) );
FCHK ( GetModuleInformation(GetCurrentProcess(), (HMODULE)hLsasrv,
&mod, sizeof(mod)) );
FCHK ( hLsass = OpenProcess(PROCESS_VM_READ, FALSE, pid) );
pImage = (BYTE*)malloc(mod.SizeOfImage);
ReadProcessMemory(hLsass, (BYTE*)hLsasrv,
pImage, mod.SizeOfImage-0x10, &dwRead);
*ppKey = (BYTE*)malloc(dwMaxCount*0x10);
__try
{
for(i=0;i<mod.SizeOfImage;i++)
{
if( memcmp(&pImage[i], "/x10/x00/x00/x00/x10/x00/x00/x00", 8) == 0)
{
dwAddr = *(DWORD *)(&pImage[i+8]);
if( ReadProcessMemory(hLsass, (LPCVOID)dwAddr,
&(*ppKey[dwCount*0x10]), 0x10, &dwRead) )
{
dwCount++;
}
}
}//end of for
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return dwCount;
}
return dwCount;
}
int main(int argc, char **argv)
{
int ret,i,j;
HMODULE hAdvApi32;
HKEY hKeySecrets;
HKEY hKey;
DWORD dwType;
char Data[0x500] = {0};
BYTE *pKey;
DWORD dwSize;
LSA_BLOB LSADataIn;
LSA_BLOB LSADataOut;
LSA_BLOB LSADataKey;
char szSecret[500];
char szSubKey[0x500];
DWORD dwErr, dwCount=0;
if(argc!=2)
{
printf("Usage: %s <pid of lsass.exe>/n", argv[0]);
return 0;
}
FCHK ((hAdvApi32 = LoadLib***y("advapi32.dll")));
FCHK ((SystemFunction005 = (PSystemFunction005)
GetProcAddress (hAdvApi32, "SystemFunction005")) != NULL);
FCHK ((RegOpenKeyEx (HKEY_LOCAL_MACHINE,
"SECURITY//Policy//Secrets",
0, KEY_READ, &hKeySecrets) == ERROR_SUCCESS))
FCHK ( ( dwCount = search_LsapDbSecretCipherKey(&pKey, atoi(argv[1])) ) != 0
);
printf("Search /"LsapDbSecretCipherKey/" return: %d/n", dwCount);
for(j=0;j<dwCount;j++)
{
printf("LsapDbSecretCipherKey [%d]/n", j);
dump_bytes(&pKey[j*0x10], 0x10);
LSADataKey.cbData = LSADataKey.cbMaxData = 0x10;
LSADataKey.pbData = &pKey[j*0x10];
//search our target
for (i=0; TRUE; i++)
{
dwErr = RegEnumKeyA (hKeySecrets, i, szSecret, sizeof (szSecret));
if (dwErr != ERROR_SUCCESS)
//
// No More Secrets
//
break;
printf("/n%s/n", szSecret);
//open it
_snprintf(szSubKey, sizeof(szSubKey),
"SECURITY//Policy//Secrets//%s//CurrVal", szSecret);
if (ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szSubKey,
0,
KEY_READ,
&hKey
) != ERROR_SUCCESS )
continue;
dwSize = sizeof(Data);
FCHK ((ret = RegQueryValueEx(hKey,
"",
NULL,
&dwType,
(LPBYTE)Data,
&dwSize) == ERROR_SUCCESS ))
LSADataIn.pbData = (BYTE *)Data + 0xC; //密文从第0xC位开始
LSADataIn.cbData = dwSize-0xC;
LSADataIn.cbMaxData = LSADataIn.cbData;
//dump_bytes(LSADataIn.pbData, LSADataIn.cbData);
LSADataOut.cbData = 0;
LSADataOut.cbMaxData = 0;
LSADataOut.pbData = NULL;
SystemFunction005(&LSADataIn, &LSADataKey, &LSADataOut);
if (LSADataOut.cbData == 0)
{
printf("null/n");
continue;
}
FCHK ((LSADataOut.pbData = (BYTE*)malloc(LSADataOut.cbData) ) !=
NULL);
LSADataOut.cbMaxData = LSADataOut.cbData;
SystemFunction005(&LSADataIn, &LSADataKey, &LSADataOut);
dump_bytes(LSADataOut.pbData, LSADataOut.cbData);
free(LSADataOut.pbData);
}//end of for
printf("Press any key to use next /"LsapDbSecretCipherKey/", or Ctrl+C
to exit./n");
getchar();
}
if(pKey)
free(pKey);
return 0;
}
-=-=-=-=-=-=-=-=-=-= code end -=-=-=-=-=-=-=-=-=-=
参考资源:
[1] http://www.lwteam.ru/modules/news/article.php?storyid=167
2003-06-24 by Cmeptb
[2] http://www.wasm.ru/forum/index.php?action=vthread&forum=12&topic=4873
2004-01-10 by ??
[3] http://homepage2.nifty.com/spw/software/rtrick/
2004-01-05 by ???
[4] SAM的散列存储加密解密算法以及SYSKEY的计算
http://www.xfocus.net/articles/200306/550.html