1 /*
2  * chntpw.c - Offline Password Edit Utility for Windows SAM database
3  *
4  * This program uses the "ntreg" library to load and access the registry,
5  * it's main purpose is to reset password based information.
6  * It can also call the registry editor etc
7 
8  * 2011-apr: Command line options added for hive expansion safe mode
9  * 2010-jun: Syskey not visible in menu, but is selectable (2)
10  * 2010-apr: Interactive menu adapts to show most relevant
11  *           selections based on what is loaded
12  * 2008-mar: Minor other tweaks
13  * 2008-mar: Interactive reg ed moved out of this file, into edlib.c
14  * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev
15  *           http://bugs.gentoo.org/show_bug.cgi?id=185411
16  * 2007-sep: Group handling extended, promotion now public
17  * 2007-sep: User edit menu, some changes to user info edit
18  * 2007-apr-may: Get and display users group memberships
19  * 2007-apr: GNU license. Some bugfixes. Cleaned up some output.
20  * 2004-aug: More stuff in regedit. Stringinput bugfixes.
21  * 2004-jan: Changed some of the verbose/debug stuff
22  * 2003-jan: Changed to use more of struct based V + some small stuff
23  * 2003-jan: Support in ntreg for adding keys etc. Editor updated.
24  * 2002-dec: New option: Specify user using RID
25  * 2002-dec: New option: blank the pass (zero hash lengths).
26  * 2001-jul: extra blank password logic (when NT or LANMAN hash missing)
27  * 2001-jan: patched & changed to use OpenSSL. Thanks to Denis Ducamp
28  * 2000-jun: changing passwords regardless of syskey.
29  * 2000-jun: syskey disable works on NT4. Not properly on NT5.
30  * 2000-jan: Attempt to detect and disable syskey
31  * 1999-feb: Now able to browse registry hives. (write support to come)
32  * See HISTORY.txt for more detailed info on history.
33  *
34  *****
35  *
36  * Copyright (c) 1997-2012 Petter Nordahl-Hagen.
37  *
38  * This program is free software; you can redistribute it and/or modify
39  * it under the terms of the GNU General Public License as published by
40  * the Free Software Foundation; version 2 of the License.
41  *
42  * This program is distributed in the hope that it will be useful,
43  * but WITHOUT ANY WARRANTY; without even the implied warranty of
44  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
45  * GNU General Public License for more details.
46  *
47  * See file GPL.txt for the full license.
48  *
49  *****
50  *
51  * Information and ideas taken from pwdump by Jeremy Allison.
52  *
53  * More info from NTCrack by Jonathan Wilkins.
54  *
55  */
56 
57 /* TODO: This is getting ugly. Most likely best to split up into different programs
58  *       and put commun stuff in library
59  */
60 
61 
62 #include <stdio.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <fcntl.h>
66 #include <ctype.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <sys/types.h>
71 #include <inttypes.h>
72 
73 /* Define DOCRYPTO in makefile to include cryptostuff to be able to change passwords to
74  * a new one.
75  * Changing passwords is seems not to be working reliably on XP and newer anyway.
76  * When not defined, only reset (nulling) of passwords available.
77  */
78 
79 #ifdef DOCRYPTO
80 #include <openssl/des.h>
81 #include <openssl/md4.h>
82 #endif
83 
84 #define uchar u_char
85 #define MD4Init MD4_Init
86 #define MD4Update MD4_Update
87 #define MD4Final MD4_Final
88 
89 #include "ntreg.h"
90 #include "sam.h"
91 
92 const char chntpw_version[] = "chntpw version 0.99.6 110511 , (c) Petter N Hagen";
93 
94 extern char *val_types[REG_MAX+1];
95 
96 /* Global verbosity */
97 int gverbose = 0;
98 
99 
100 #define MAX_HIVES 10
101 
102 /* Array of loaded hives */
103 struct hive *hive[MAX_HIVES+1];
104 int no_hives = 0;
105 
106 /* Icky icky... globals used to refer to hives, will be
107  * set when loading, so that hives can be loaded in any order
108  */
109 
110 int H_SAM = -1;
111 int H_SYS = -1;
112 int H_SEC = -1;
113 int H_SOF = -1;
114 
115 int syskeyreset = 0;
116 int dirty = 0;
117 int max_sam_lock = 0;
118 
119 /*
120  * of user with RID 500, because silly MS decided
121  * to localize the bloody admin-username!! AAAGHH!
122  */
123 char admuser[129]="Administrator";
124 
125 /* ============================================================== */
126 
127 
128 #ifdef DOCRYPTO
129 
130 /* Crypto-stuff & support for what we'll do in the V-value */
131 
132 /* Zero out string for lanman passwd, then uppercase
133  * the supplied password and put it in here */
134 
make_lanmpw(char * p,char * lm,int len)135 void make_lanmpw(char *p, char *lm, int len)
136 {
137    int i;
138 
139    for (i=0; i < 15; i++) lm[i] = 0;
140    for (i=0; i < len; i++) lm[i] = toupper(p[i]);
141 }
142 
143 /*
144  * Convert a 7 byte array into an 8 byte des key with odd parity.
145  */
146 
str_to_key(unsigned char * str,unsigned char * key)147 void str_to_key(unsigned char *str,unsigned char *key)
148 {
149 	int i;
150 
151 	key[0] = str[0]>>1;
152 	key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
153 	key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
154 	key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
155 	key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
156 	key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
157 	key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
158 	key[7] = str[6]&0x7F;
159 	for (i=0;i<8;i++) {
160 		key[i] = (key[i]<<1);
161 	}
162 	DES_set_odd_parity((des_cblock *)key);
163 }
164 
165 /*
166  * Function to convert the RID to the first decrypt key.
167  */
168 
sid_to_key1(uint32_t sid,unsigned char deskey[8])169 void sid_to_key1(uint32_t sid,unsigned char deskey[8])
170 {
171 	unsigned char s[7];
172 
173 	s[0] = (unsigned char)(sid & 0xFF);
174 	s[1] = (unsigned char)((sid>>8) & 0xFF);
175 	s[2] = (unsigned char)((sid>>16) & 0xFF);
176 	s[3] = (unsigned char)((sid>>24) & 0xFF);
177 	s[4] = s[0];
178 	s[5] = s[1];
179 	s[6] = s[2];
180 
181 	str_to_key(s,deskey);
182 }
183 
184 /*
185  * Function to convert the RID to the second decrypt key.
186  */
187 
sid_to_key2(uint32_t sid,unsigned char deskey[8])188 void sid_to_key2(uint32_t sid,unsigned char deskey[8])
189 {
190 	unsigned char s[7];
191 
192 	s[0] = (unsigned char)((sid>>24) & 0xFF);
193 	s[1] = (unsigned char)(sid & 0xFF);
194 	s[2] = (unsigned char)((sid>>8) & 0xFF);
195 	s[3] = (unsigned char)((sid>>16) & 0xFF);
196 	s[4] = s[0];
197 	s[5] = s[1];
198 	s[6] = s[2];
199 
200 	str_to_key(s,deskey);
201 }
202 
203 /* DES encrypt, for LANMAN */
204 
E1(uchar * k,uchar * d,uchar * out)205 void E1(uchar *k, uchar *d, uchar *out)
206 {
207   des_key_schedule ks;
208   des_cblock deskey;
209 
210   str_to_key(k,(uchar *)deskey);
211 #ifdef __FreeBSD__
212   des_set_key(&deskey,ks);
213 #else /* __FreeBsd__ */
214   des_set_key((des_cblock *)deskey,ks);
215 #endif /* __FreeBsd__ */
216   des_ecb_encrypt((des_cblock *)d,(des_cblock *)out, ks, DES_ENCRYPT);
217 }
218 
219 #endif   /* DOCRYPTO */
220 
221 
222 /* Get machines SID as binary (raw data)
223  * str = pointer to buffer, first 20 bytes will be filled in
224  * returns true if found, else 0
225  */
226 
get_machine_sid(char * sidbuf)227 int get_machine_sid(char *sidbuf)
228 {
229 
230   struct accountdb_V *v;
231   struct keyval *kv;
232   uint32_t ofs;
233   uint32_t len;
234 
235   if (H_SAM >= 0) {
236 
237     /* Get accoundb V value */
238     kv = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_V_PATH, REG_BINARY, TPF_VK);
239     if (!kv) {
240       fprintf(stderr,"get_machine_sid: Machine SID not found in SAM\n");
241       return(0);
242     }
243 
244     //    hexdump(&(kv->data), 0, kv->len,1);
245 
246     v = (struct accountdb_V *)&kv->data;
247     ofs = v->sid_ofs;
248     len = v->sid_len + 4;
249     ofs += 0x40;
250 
251     if (len != SID_BIN_LEN) {
252       fprintf(stderr,"get_machine_sid: WARNING: SID found, but it has len=%d instead of expected %d bytes\n",len,SID_BIN_LEN);
253     }
254 
255     //    printf("get_machine_sid: adjusted ofs = %x, len = %x (%d)\n",ofs,len,len);
256 
257 
258     memcpy(sidbuf, (char *)v+ofs, len);
259 
260     // hexdump(sidbuf, 0, len, 1);
261 
262      return(1);
263   }
264   return(0);
265 }
266 
267 /* Make string out of SID, in S-1-5 authority (NT authority)
268  * like S-1-5-21-516312364-151943033-2698651
269  * Will allocate return string (which can be of variable lenght)
270  * NOTE: caller must free it
271  * sidbuf = the SID binary data structure with it's type+counter first
272  *
273  * str = string buffer to fill, be sure to have at least space:
274  *       6 chars athority prefix (S-1-5-)
275  *       4 * 10 digits (the 4 32 bit groups)
276  *       3 for the - between the groups
277  *       1 for null termination
278  *      50 chars
279  */
sid_to_string(struct sid_binary * sidbuf)280 char *sid_to_string(struct sid_binary *sidbuf)
281 {
282 
283   int cnt, i;
284   int *array;
285   char *str = NULL;
286 
287   //   hexdump(sidbuf, 0, 24, 1);
288 
289 
290   array = (int *)&sidbuf->array;
291 
292   if (sidbuf->unknown0 != 1) {
293     fprintf(stderr,"sid_to_string: DEBUG: first byte unexpected: %d\n",sidbuf->unknown0);
294   }
295 
296   cnt = sidbuf->sections;
297 
298   // printf("sid_to_string: DEBUG: sections = %d\n",cnt);
299 
300   str = str_dup("S-1-5");
301 
302   for (i = 0; i < cnt; i++) {
303     str = str_catf(str,"-%u",sidbuf->array[i]);
304   }
305 
306   // printf("sid_to_string: returning <%s>\n",str);
307 
308 
309   return(str);
310 }
311 
312 
313 
314 
315 /* Check if hive is SAM, and if it is, extract some
316  * global policy information from it, like lockout counts etc
317  */
318 
check_get_samdata(int show)319 void check_get_samdata(int show)
320 {
321   struct accountdb_F *f;
322   struct keyval *v;
323 
324   if (H_SAM >= 0) {
325 
326     /* Get accoundb F value */
327     v = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_F_PATH, REG_BINARY, TPF_VK);
328     if (!v) {
329       fprintf(stderr,"WARNING: Login counts data not found in SAM\n");
330       return;
331     }
332 
333     f = (struct accountdb_F *)&v->data;
334     max_sam_lock = f->locklimit;
335 
336     if (show) {
337       printf("\n* SAM policy limits:\n");
338       printf("Failed logins before lockout is: %d\n",max_sam_lock);
339       printf("Minimum password length        : %d\n",f->minpwlen);
340       printf("Password history count         : %d\n",f->minpwlen);
341     }
342   }
343 }
344 
345 
346 /* Try to decode and possibly change account lockout etc
347  * This is \SAM\Domains\Account\Users\<RID>\F
348  * It's size seems to always be 0x50.
349  * Params: RID - user ID, mode - 0 silent, 1 silent, 2 edit.
350  * Returns: ACB bits with high bit set if lockout count is >0
351  */
352 
handle_F(int rid,int mode)353 short handle_F(int rid, int mode)
354 {
355 
356   struct user_F *f;
357   char s[200];
358   struct keyval *v;
359   unsigned short acb;
360   int b;
361 
362   if (H_SAM < 0) return(0);
363 
364   /* Get users F value */
365   snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\F",rid);
366   v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
367   if (!v) {
368     printf("Cannot find value <%s>\n",s);
369     return(0);
370   }
371 
372   if (v->len < 0x48) {
373     printf("handle_F: F value is 0x%x bytes, need >= 0x48, unable to check account flags!\n",v->len);
374     FREE(v);
375     return(0);
376   }
377 
378   f = (struct user_F *)&v->data;
379   acb = f->ACB_bits;
380 
381   if (mode == 1) {
382     printf("Account bits: 0x%04x =\n",acb);
383 
384 
385     for (b=0; b < 15; b++) {
386       printf("[%s] %-15.15s | ",
387 	     (acb & (1<<b)) ? "X" : " ", acb_fields[b] );
388       if (b%3 == 2) printf("\n");
389     }
390 
391     printf("\nFailed login count: %u, while max tries is: %u\n",f->failedcnt,max_sam_lock);
392     printf("Total  login count: %u\n",f->logins);
393   }
394 
395   if (mode == 2) {
396     acb |= ACB_PWNOEXP;
397     acb &= ~ACB_DISABLED;
398     acb &= ~ACB_AUTOLOCK;
399     f->ACB_bits = acb;
400     f->failedcnt = 0;
401     put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY,TPF_VK_EXACT);
402     printf("Unlocked!\n");
403   }
404   return (acb | ( (f->failedcnt > 0 && f->failedcnt >= max_sam_lock)<<15 ) | (acb & ACB_AUTOLOCK)<<15 | (acb & ACB_DISABLED)<<15);
405 }
406 
407 
408 /* Stuff SID binary list into more easily handled arrays
409  * sidbuf = binary list buffer (not changed, may point into value structure)
410  * size = number of bytes of raw data
411  * returns pointer to array, terminated with NULL pointer.
412  * Keeps full binary data from each SID
413  * All array space is allocated, call free_sid_array() to free it.
414  */
415 
make_sid_array(struct sid_binary * sidbuf,int size)416 struct sid_array *make_sid_array(struct sid_binary *sidbuf, int size)
417 {
418 
419   int num = 0;
420   int sidlen;
421   struct sid_binary *sb;
422   struct sid_array *array;
423 
424   CREATE(array, struct sid_array, 1);
425   array[0].len = 0;
426   array[0].sidptr = NULL;
427 
428   while (size > 0) {
429 
430     sidlen = sidbuf->sections * 4 + 8;
431 
432     // printf("make_sid_array: sidlen = %d\n",sidlen);
433 
434     ALLOC(sb, 1, sidlen);
435     memcpy(sb, sidbuf, sidlen);
436     array[num].len = sidlen;
437     array[num].sidptr = sb;
438     sidbuf = (void *)sidbuf + sidlen;
439     size -= sidlen;
440     num++;
441 
442     array = realloc(array, (num + 1) * sizeof(struct sid_array));
443     array[num].len = 0;
444     array[num].sidptr = NULL;
445 
446   }
447 
448 
449   return(array);
450 
451 }
452 
453 /* Free the sid array (from the function above) */
454 
free_sid_array(struct sid_array * array)455 void free_sid_array(struct sid_array *array)
456 {
457 
458   int num = 0;
459 
460   while (array[num].sidptr) {
461     free(array[num].sidptr);
462     num++;
463   }
464 
465   free(array);
466 }
467 
468 /* Compare two SIDs, and return like strcmp */
sid_cmp(struct sid_binary * s1,struct sid_binary * s2)469 int sid_cmp(struct sid_binary *s1, struct sid_binary *s2)
470 {
471   int p;
472 
473   if (!s1 && !s2) return(0);
474   if (!s1) return(-1);
475   if (!s2) return(1);
476 
477   if (s1->sections < s2->sections) return(-1); /* s1 has shorter len, always smaller */
478   if (s1->sections > s2->sections) return(1); /* s1 has longer len, always larger */
479   /* Run compare since same length */
480   for (p = 0; p < s1->sections; p++) {
481     if (s1->array[p] < s2->array[p]) return (-1);
482     if (s1->array[p] > s2->array[p]) return (1);
483   }
484   /* At end. Thus equal */
485   return(0);
486 }
487 
488 
489 
490 /* Get list of group members for a group
491  * Will get the SID list (as binary) into a buffer that will be allocated
492  * according to the neccessary size (based on member count)
493  * NOTE: Caller must free the buffer when not needed any more
494  * grp = group ID
495  * sidarray = pointer to pointer to sid array which will be allocated
496  * Returns number of members in the group
497  */
498 
get_grp_members_sid(int grp,struct sid_array ** sarray)499 int get_grp_members_sid(int grp, struct sid_array **sarray)
500 {
501   char g[200];
502   // char groupname[128];
503 
504   struct sid_array *marray;
505   struct keyval *c = NULL;
506   struct group_C *cd;
507   // int grpnamoffs, grpnamlen;
508   int mofs, mlen;
509 
510   snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp);
511   c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT);
512   if (c) {
513     cd = (struct group_C *)&c->data;
514 
515     // grpnamoffs = cd->grpname_ofs + 0x34;
516     // grpnamlen  = cd->grpname_len;
517 
518     // cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
519 
520     // printf("get_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members);
521 
522     mofs = cd->members_ofs;
523     mlen = cd->members_len;
524 
525     //    printf("get_grp_members_sid: mofs = %x, mlen = %x (%d)\n", mofs,mlen,mlen);
526     // printf("get_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen);
527 
528     // hexdump(&c->data, 0, c->len, 1);
529     // hexdump(&cd->data[mofs], 0, mlen, 1);
530 
531     marray = make_sid_array((struct sid_binary *)&cd->data[mofs], mlen);
532 
533     *sarray = marray;
534     // free_sid_array(marray);
535 
536     free(c);
537 
538   } else {
539     printf("Group info for %x not found!\n",grp);
540     *sarray = NULL;
541     return(0);
542   }
543 
544   return(cd->grp_members);
545 
546 }
547 
548 /* Put list of group members back into group C structure
549  * grp = group ID
550  * sidarray = pointer to sid array
551  * Returns true if success
552  */
553 
put_grp_members_sid(int grp,struct sid_array * sarray)554 int put_grp_members_sid(int grp, struct sid_array *sarray)
555 {
556   char g[200];
557   char groupname[128];
558 
559   struct keyval *c = NULL;
560   struct group_C *cd;
561   int grpnamoffs, grpnamlen;
562   int mofs, mlen;
563   int sidlen = 0;
564   void *sidptr;
565   int i;
566   char *str;
567 
568   snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp);
569   c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT);
570   if (c) {
571     cd = (struct group_C *)&c->data;
572 
573     grpnamoffs = cd->grpname_ofs + 0x34;
574     grpnamlen  = cd->grpname_len;
575 
576     cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
577 
578     if (gverbose) printf("put_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members);
579 
580     mofs = cd->members_ofs;
581     mlen = cd->members_len;
582 
583      if (gverbose) printf("put_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen);
584 
585      if (gverbose) hexdump(&c->data, 0, c->len, 1);
586 
587     /* Get total size of new SID data */
588 
589     for (i = 0; sarray[i].sidptr; i++) sidlen += sarray[i].len;
590 
591     if (gverbose) printf("put_grp_members_sid: new count : %d, new sidlen: %x\n",i,sidlen);
592 
593     /* Resize buffer with C structure */
594     c = realloc(c, 4 + mofs + sidlen + 0x34); /* offset of SIDs + sids lenght + pointer list at start */
595     c->len = 0x34 + mofs + sidlen;
596 
597     cd = (struct group_C *)&c->data;
598     mofs = cd->members_ofs;
599     sidptr = &cd->data[mofs];
600 
601     for (i = 0; sarray[i].sidptr; i++) {
602       if (gverbose) printf("  copying : %d len %x, at %x\n",i,sarray[i].len, sidptr);
603       str = sid_to_string(sarray[i].sidptr);
604       if (gverbose) printf("  Member # %d = <%s>\n", i, str);
605       FREE(str);
606       memcpy(sidptr, sarray[i].sidptr, sarray[i].len);
607       sidptr += sarray[i].len;
608     }
609 
610     cd->members_len = sidlen;  /* Update member count in C struct */
611     cd->grp_members = i;
612 
613     if (gverbose) hexdump(&c->data, 0, c->len, 1);
614 
615     if (!put_buf2val(hive[H_SAM], c, 0, g, 0, TPF_VK_EXACT)) {
616       fprintf(stderr,"put_grp_members_sid: could not write back group info in value %s\n",g);
617       free(c);
618       return(0);
619     }
620 
621 
622     free(c);
623 
624   } else {
625     printf("Group info for %x not found!\n",grp);
626     return(0);
627   }
628 
629   return(1);
630 
631 }
632 
633 
634 /* List groups, optionally with members */
635 
list_groups(int listmembers)636 void list_groups(int listmembers) {
637 
638   struct ex_data ex;
639   struct sid_array *sids = NULL;
640   int nkofs;
641   unsigned int grp;
642   int count = 0, countri = 0;
643   struct keyval *c = NULL;
644   struct group_C *cd;
645   int grpnamoffs, grpnamlen, i;
646   char groupname[200];
647   char *str;
648 
649 
650   if (H_SAM < 0) return;
651 
652 
653   nkofs = trav_path(hive[H_SAM], 0,"\\SAM\\Domains\\Builtin\\Aliases",0);
654   if (!nkofs) {
655     printf("list_groups: Cannot find group list in registry! (is this a SAM-hive?)\n");
656     return;
657   }
658 
659   /* Pick up all subkeys here, they are local groups */
660   while ((ex_next_n(hive[H_SAM], nkofs+4, &count, &countri, &ex) > 0)) {
661 
662     // printf("Group ID %s\n",ex.name);
663     sscanf(ex.name,"%x",&grp);
664 
665     /* Groups keys have a C value, get it and pick up the name etc */
666     /* Some other keys also exists (Members, Names at least), but we skip them */
667 
668     c = get_val2buf(hive[H_SAM], NULL, ex.nkoffs+4, "C", 0, TPF_VK_EXACT);
669     if (c) {
670       cd = (struct group_C *)&c->data;
671       grpnamoffs = cd->grpname_ofs + 0x34;
672       grpnamlen  = cd->grpname_len;
673 
674       cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
675 
676       printf("Group #%x named <%s> has %d members\n",grp,groupname,cd->grp_members);
677 
678       if (listmembers) {
679 	get_grp_members_sid(grp, &sids);
680 
681 	for (i = 0; sids[i].sidptr; i++) {
682 	  str = sid_to_string(sids[i].sidptr);
683 	  printf("  Member # %d = <%s>\n", i, str);
684 	  FREE(str);
685 	}
686 	free_sid_array(sids);
687       }
688     }
689 
690   }
691 
692 }
693 
694 /* Get group IDs a user is member of
695  * rid = user ID
696  * returns: since value data is just an array of grp ids (4 bytes each),
697  *          just return the keyval structure (size + data)
698  * caller must free() keyval
699  */
700 
get_user_grpids(int rid)701 struct keyval *get_user_grpids(int rid)
702 {
703   char s[200];
704   struct sid_binary sid;
705   char *sidstr;
706 
707   int nk = 0;
708   struct keyval *m = NULL;
709   int count = 0;
710   int size;
711 
712   if (!rid || (H_SAM < 0)) return(NULL);
713 
714   if (!get_machine_sid((char *)&sid)) {
715     fprintf(stderr,"get_user_grpids: Could not find machine SID\n");
716     return(0);
717   }
718 
719   sidstr = sid_to_string(&sid);
720 
721   /* Get member list for user on this machine */
722   snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s\\%08X",sidstr,rid);
723 
724   free(sidstr);
725 
726   /* Now, the TYPE field is the number of groups the user is member of */
727   /* Don't we just love the inconsistent use of fields!! */
728   nk = trav_path(hive[H_SAM], 0, s, 0);
729   if (!nk) {
730     /* This probably means user is not in any group. Seems to be the case
731        for a couple of XPs built in support / guest users. So just return */
732     if (gverbose) printf("get_user_grpids: Cannot find RID under computer SID <%s>\n",s);
733     return(NULL);
734   }
735   nk += 4;
736   count = get_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT);
737   if (count == -1) {
738     printf("get_user_grpids: Cannot find value <%s\\@>\n",s);
739     return(NULL);
740   }
741 
742   //  printf("get_user_grpids: User is member of %d groups:\n",count);
743 
744   /* This is the data size */
745   size = get_val_len(hive[H_SAM],nk,"@",TPF_VK_EXACT);
746 
747   /* It should be 4 bytes for each group */
748   if (gverbose) printf("Data size %d bytes.\n",size);
749   if (size != count * 4) {
750     printf("get_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n");
751   }
752 
753   m = get_val2buf(hive[H_SAM], NULL, nk, "@", 0, TPF_VK_EXACT);
754   if (!m) {
755     printf("get_user_grpids: Could not get value data! Giving up.\n");
756     return(NULL);
757   }
758 
759   return(m);
760 }
761 
762 /* Put/set group IDs a user is member of
763  * rid = user ID
764  * val = keyval structure of data, actual value data is a list
765  *       of ints, one per group
766  * returns true if successful setting the value
767  */
768 
put_user_grpids(int rid,struct keyval * val)769 int put_user_grpids(int rid, struct keyval *val)
770 {
771   char s[200];
772   struct sid_binary sid;
773   char *sidstr;
774 
775   int newcount = 0;
776   int nk = 0;
777   int count = 0;
778 
779   if (!rid || (H_SAM < 0)) return(0);
780 
781   if (!val || !val->len) return(0);
782 
783   if (!get_machine_sid((char *)&sid)) {
784     fprintf(stderr,"put_user_grpids: Could not find machine SID\n");
785     return(0);
786   }
787 
788   sidstr = sid_to_string(&sid);
789 
790   /* Get member list for user on this machine */
791   snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s\\%08X",sidstr,rid);
792 
793   free(sidstr);
794 
795   /* Now, the TYPE field is the number of groups the user is member of */
796 
797   nk = trav_path(hive[H_SAM], 0, s, 0);
798   if (!nk) {
799     /* This probably means user is not in any group. Seems to be the case
800        for a couple of XPs built in support / guest users. So just return */
801     if (gverbose) printf("put_user_grpids: Cannot find RID under computer SID <%s>\n",s);
802     return(0);
803   }
804 
805   nk += 4;
806 
807   count = get_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT);
808   if (count == -1) {
809     printf("put_user_grpids: Cannot find value <%s\\@>\n",s);
810     return(1);
811   }
812 
813   if (gverbose) printf("put_user_grpids: User was member of %d groups:\n",count);
814 
815   /* This is the data size */
816   /* It should be 4 bytes for each group */
817 
818   newcount = val->len >> 2;
819 
820   if (gverbose) printf("Data size %d bytes.\n",val->len);
821   if (val->len != newcount << 2) {
822     printf("set_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n");
823   }
824 
825   if (gverbose) printf("put_user_grpids: User is NOW member of %d groups:\n",newcount);
826 
827   set_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT,newcount);
828 
829   if (!put_buf2val(hive[H_SAM], val, nk, "@", 0, TPF_VK_EXACT) ) {
830     printf("put_user_grpids: Could not set reg value data!\n");
831     return(0);
832   }
833 
834   return(1);
835 }
836 
837 
838 
839 
840 
841 /* List users membership or check if admin (is in admin group)
842  * rid   - users rid
843  * check - if 1 just check if admin, do not list
844  * returns true if user is admin
845  */
846 
list_user_groups(int rid,int check)847 int list_user_groups(int rid, int check)
848 {
849   char g[200];
850 
851   char groupname[128];
852   struct keyval *m = NULL, *c = NULL;
853   struct group_C *cd;
854   unsigned int *grps;
855   int count = 0, isadmin = 0;
856   int i, grp, grpnamoffs, grpnamlen;
857 
858   if (!rid || (H_SAM < 0)) return(0);
859 
860   m = get_user_grpids(rid);
861 
862   if (!m) return(0);
863 
864   grps = (unsigned int *)&m->data;
865   count = m->len >> 2;
866 
867   for (i = 0; i < count; i++) {
868     grp = grps[i];
869     if (!check) printf("%08x ",grp);
870 
871     if (grp == 0x220) isadmin = 1;
872 
873     if (!check) {
874       snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp);
875       c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT);
876       if (c) {
877 	cd = (struct group_C *)&c->data;
878 	grpnamoffs = cd->grpname_ofs + 0x34;
879 	grpnamlen  = cd->grpname_len;
880 
881 	cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
882 
883 	printf("= %s (which has %d members)\n",groupname,cd->grp_members);
884 
885 	//	get_grp_members_sid(grp, &sidbuf);
886 
887       } else {
888 	printf("Group info for %x not found!\n",grp);
889       }
890     }
891   }
892 
893   free(m);
894 
895   return(isadmin);
896 }
897 
898 
899 
900 
901 /* Add user to a group
902  * rid = user RID
903  * grp = group ID
904  * return true if success
905  */
906 
add_user_to_grp(int rid,int grp)907 int add_user_to_grp(int rid, int grp)
908 {
909   struct keyvala *usrgrplist, *newusrgrplist;
910   struct sid_array *sarray, *narray;
911   struct sid_binary *usid;
912   struct sid_binary msid;
913   int members, newmembers;
914   char *str;
915   int ugcnt;
916   int o,n,hit,c;
917   unsigned int *og, *ng;
918 
919 
920 
921   if (!rid || !grp || (H_SAM < 0)) return(0);
922 
923   /* Build user SID (add RID to machine SID) */
924 
925   if (!get_machine_sid((char *)&msid)) {
926     fprintf(stderr,"get_user_grpids: Could not find machine SID\n");
927     return(0);
928   }
929 
930   /* well, and hope that machine SID is always same size here too */
931   ALLOC(usid, sizeof(struct sid_binary) +4, 1);
932 
933   memcpy(usid, &msid, sizeof(struct sid_binary));
934 
935   usid->array[4] = rid; /* Tack RID on at end */
936   usid->sections = 5;
937 
938   str = sid_to_string(usid);
939 
940   if (gverbose) printf("add_user_to_grp: user SID is <%s>\n", str);
941 
942   free(str);
943 
944   /* With all of the above functions, it should now just be to get
945    * the list of groups the user account has listed under it
946    * and the list of users the group has listed under it
947    */
948 
949   usrgrplist = (struct keyvala *)get_user_grpids(rid);
950 
951   if (!usrgrplist) {
952     printf("add_user_to_grp: user # %x not found!\n",rid);
953     return(0);
954   }
955 
956 
957   members = get_grp_members_sid(grp, &sarray);
958 
959   if (!sarray) {
960     printf("add_user_to_grp: group # %x not found!\n",grp);
961     FREE(usrgrplist);
962     return(0);
963   }
964 
965 
966   /* Add the group to the users list of groups it is member of */
967 
968   ugcnt = usrgrplist->len >> 2;      /* Count of groups already on user */
969 
970   /* Allocate new larger usrgrplist for one more entry */
971 
972   ALLOC(newusrgrplist, usrgrplist->len + 4 + 4, 1);
973   bzero(newusrgrplist, usrgrplist->len + 4 + 4);      /* for sanity.. */
974   newusrgrplist->len = usrgrplist->len + 4;
975 
976   og = (unsigned int *)&usrgrplist->data;
977   ng = (unsigned int *)&newusrgrplist->data;
978 
979   if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len);
980 
981 
982 #if 0   /* If list should be sorted, but seems windows does not do that? */
983 
984   /* Copy over users group list, adding in where needed */
985 
986   hit = 0;
987   for (o = 0, n = 0; o < ugcnt; o++, n++) {
988     printf(":: %d %d : %x\n",o,n,og[o]);
989     if (og[o] == grp) {     /* Was already in there, so just don't increase size.. */
990       newusrgrplist->len = usrgrplist->len;
991       hit = 1;
992     }
993     if (og[o] > grp && !hit) {
994       ng[n++] = grp;     /* Next is higher, so insert out rid */
995       hit = 1;
996       printf("  -- insert\n");
997     }
998     ng[n] = og[o];
999   }
1000   printf("n = %d\n",n);
1001   if (!hit) ng[n] = grp;    /* Insert at end if we run down */
1002 
1003 #endif
1004 
1005   /* Copy over users group list, checking if already there */
1006 
1007   hit = 0;
1008   for (o = 0; o < ugcnt; o++) {
1009     if (gverbose) printf(":: %d : %x\n",o,og[o]);
1010     if (og[o] == grp) {     /* Was already in there, so just don't increase size.. */
1011       newusrgrplist->len = usrgrplist->len;
1012       hit = 1;
1013       if (gverbose) printf("  -- match\n");
1014     }
1015     ng[o] = og[o];
1016   }
1017   if (gverbose) printf(" - end of list at o = %d\n",o);
1018 
1019   if (!hit) ng[o] = grp;  /* Just stuff new group in at end if not already in list */
1020 
1021   if (gverbose) {
1022   for (o = 0; o < (newusrgrplist->len >> 2); o++) {
1023     printf("grp index %d = %08x\n", o, ng[o]);
1024   }
1025   }
1026   /* And then we add the user SID into the groups list of members */
1027 
1028   if (gverbose) {
1029   printf("add_user_to_grp: grp memberlist BEFORE:\n");
1030 
1031   for (o = 0; sarray[o].sidptr; o++) {
1032     str = sid_to_string(sarray[o].sidptr);
1033     printf("  Member # %d = <%s>\n", o, str);
1034     FREE(str);
1035   }
1036   }
1037 
1038   newmembers = members + 1;
1039   ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1);  /* Add one entry size */
1040 
1041   if (gverbose) printf("members = %d\n", members);
1042 
1043   hit = 0;
1044   for (o = 0, n = 0; o <= members; o++, n++) {
1045     c = sid_cmp(sarray[o].sidptr, usid);     /* Compare slot with new SID */
1046     if (gverbose) printf("sid_cmp returns %d\n",c);
1047     if (c == 0) {
1048       newmembers--;                   /* Already there, don't change anything */
1049       hit = 1;
1050     }
1051     if (!hit && ((c > 0) || !sarray[o].sidptr)) {              /* Next is higher, insert new SID */
1052       if (gverbose) printf("  -- add\n");
1053       narray[n].len = usid->sections * 4 + 8;     /* Hmm */
1054       narray[n].sidptr = usid;
1055       n++;
1056       hit = 1;
1057     }
1058     narray[n].len = sarray[o].len;
1059     narray[n].sidptr = sarray[o].sidptr;
1060   }
1061 
1062   if (gverbose) {
1063   printf("add_user_to_grp: grp memberlist AFTER:\n");
1064 
1065 
1066   for (o = 0; narray[o].sidptr; o++) {
1067     str = sid_to_string(narray[o].sidptr);
1068     printf("  Member # %d = <%s>\n", o, str);
1069     FREE(str);
1070   }
1071   }
1072 
1073   /* Write new lists back to registry */
1074   if (!put_user_grpids(rid, (struct keyval *)newusrgrplist)) {
1075     fprintf(stderr, "add_user_to_grp: failed storing users group list\n");
1076   } else if (!put_grp_members_sid(grp, narray)) {
1077     fprintf(stderr,"add_user_to_grp: failed storing groups user list\n");
1078     put_user_grpids(rid, (struct keyval *)usrgrplist);      /* Try to roll back */
1079   }
1080 
1081   FREE(usrgrplist);
1082   FREE(newusrgrplist);
1083   free_sid_array(narray);
1084   FREE(sarray);     /* Pointers was copied to narray, and freed above, just free the array here */
1085 
1086   return(1);
1087 
1088 }
1089 
1090 /* Remove user from a group
1091  * rid = user RID
1092  * grp = group ID
1093  * return true if success
1094  */
1095 
remove_user_from_grp(int rid,int grp)1096 int remove_user_from_grp(int rid, int grp)
1097 {
1098   struct keyvala *usrgrplist, *newusrgrplist;
1099   struct sid_array *sarray, *narray;
1100   struct sid_binary *usid;
1101   struct sid_binary msid;
1102   int members, newmembers;
1103   char *str;
1104   int ugcnt;
1105   int o,n,hit,c;
1106   unsigned int *og, *ng;
1107 
1108 
1109 
1110   if (!rid || !grp || (H_SAM < 0)) return(0);
1111 
1112   /* Build user SID (add RID to machine SID) */
1113 
1114   if (!get_machine_sid((char *)&msid)) {
1115     fprintf(stderr,"get_user_grpids: Could not find machine SID\n");
1116     return(0);
1117   }
1118 
1119   /* well, and hope that machine SID is always same size here too */
1120   ALLOC(usid, sizeof(struct sid_binary) +4, 1);
1121 
1122   memcpy(usid, &msid, sizeof(struct sid_binary));
1123 
1124   usid->array[4] = rid; /* Tack RID on at end */
1125   usid->sections = 5;
1126 
1127   if (gverbose) {
1128     str = sid_to_string(usid);
1129     printf("remove_user_from_grp: user SID is <%s>\n", str);
1130     free(str);
1131   }
1132 
1133   /* With all of the above functions, it should now just be to get
1134    * the list of groups the user account has listed under it
1135    * and the list of users the group has listed under it
1136    */
1137 
1138   usrgrplist = (struct keyvala *)get_user_grpids(rid);
1139 
1140   if (!usrgrplist) {
1141     printf("remove_user_from_grp: user # %x not found!\n",rid);
1142     return(0);
1143   }
1144 
1145 
1146   members = get_grp_members_sid(grp, &sarray);
1147 
1148   if (!sarray) {
1149     printf("remove_user_from_grp: group # %x not found!\n",grp);
1150     FREE(usrgrplist);
1151     return(0);
1152   }
1153 
1154 
1155   /* Add the group to the users list of groups it is member of */
1156 
1157   ugcnt = usrgrplist->len >> 2;      /* Count of groups already on user */
1158 
1159   /* Allocate same size usrgrplist, since we don't know if we are in there and need to be removed */
1160 
1161   ALLOC(newusrgrplist, usrgrplist->len + 4, 1);
1162   bzero(newusrgrplist, usrgrplist->len + 4);      /* for sanity.. */
1163   newusrgrplist->len = usrgrplist->len;
1164 
1165   og = (unsigned int *)&usrgrplist->data;
1166   ng = (unsigned int *)&newusrgrplist->data;
1167 
1168   if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len);
1169 
1170 
1171   /* Copy over users group list, if relevant group found, don't copy it over */
1172 
1173   hit = 0;
1174   for (o = 0; o < ugcnt; o++) {
1175     if (gverbose) printf(":: %d : %x\n",o,og[o]);
1176     if (og[o] == grp) {     /* Group found */
1177       hit = 1;
1178       if (gverbose) printf("  -- match\n");
1179     } else {
1180       ng[o-hit] = og[o];
1181     }
1182   }
1183   if (gverbose) printf(" - end of list at o = %d\n",o);
1184   if (hit) {
1185     newusrgrplist->len -= 4;  /* Decrease size if found */
1186   } else {
1187     fprintf(stderr, "remove_user_from_grp: NOTE: group not in users list of groups, may mean user not member at all. Safe. Continuing.\n");
1188   }
1189 
1190 
1191   if (gverbose) {
1192   for (o = 0; o < (newusrgrplist->len >> 2); o++) {
1193     printf("grp index %d = %08x\n", o, ng[o]);
1194   }
1195 
1196   /* Remove the user SID from the groups list of members */
1197 
1198   printf("remove_user_from_grp: grp memberlist BEFORE:\n");
1199 
1200   for (o = 0; sarray[o].sidptr; o++) {
1201     str = sid_to_string(sarray[o].sidptr);
1202     printf("  Member # %d = <%s>\n", o, str);
1203     FREE(str);
1204   }
1205   }
1206 
1207   newmembers = members;
1208   ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1);
1209 
1210   if (gverbose) printf("members = %d\n", members);
1211 
1212 
1213   hit = 0;
1214   for (o = 0, n = 0; o <= members; o++, n++) {
1215     c = sid_cmp(sarray[o].sidptr, usid);     /* Compare slot with new SID */
1216     if (gverbose) printf("sid_cmp returns %d\n",c);
1217     if (c == 0) {
1218       newmembers--;                   /* Found, skip copy and decrease list size */
1219       hit = 1;
1220       n--;
1221     } else {
1222       narray[n].len = sarray[o].len;        /* Copy entry */
1223       narray[n].sidptr = sarray[o].sidptr;
1224     }
1225   }
1226   if (!hit) fprintf(stderr, "remove_user_from_grp: NOTE: user not in groups list of users, may mean user was not member at all. Does not matter, continuing.\n");
1227 
1228   if (gverbose) {
1229   printf("remove_user_from_grp: grp memberlist AFTER:\n");
1230   for (o = 0; narray[o].sidptr; o++) {
1231     str = sid_to_string(narray[o].sidptr);
1232     printf("  Member # %d = <%s>\n", o, str);
1233     FREE(str);
1234   }
1235   }
1236 
1237   /* Write new lists back to registry */
1238   if (!put_user_grpids(rid, (struct keyval *)newusrgrplist)) {
1239     fprintf(stderr, "remove_user_from_grp: failed storing users group list\n");
1240   } else if (!put_grp_members_sid(grp, narray)) {
1241     fprintf(stderr,"remvoe_user_from_grp: failed storing groups user list\n");
1242     put_user_grpids(rid, (struct keyval *)usrgrplist);      /* Try to roll back */
1243   }
1244 
1245   FREE(usrgrplist);
1246   FREE(newusrgrplist);
1247   free_sid_array(narray);
1248   FREE(sarray);     /* Pointers was copied to narray, and freed above, just free the array here */
1249 
1250   return(1);
1251 
1252 }
1253 
1254 
1255 
1256 
1257 /* Promote user into administrators group (group ID 0x220)
1258  * And remove from all others...
1259  * rid   - users rid
1260  * no returns yet
1261  */
1262 
promote_user(int rid)1263 void promote_user(int rid)
1264 {
1265 
1266   char yn[5];
1267 
1268   if (!rid || (H_SAM < 0)) return;
1269 
1270   printf("\n=== PROMOTE USER\n\n");
1271   printf("Will add the user to the administrator group (0x220)\n"
1272 	 "and to the users group (0x221). That should usually be\n"
1273 	 "what is needed to log in and get administrator rights.\n"
1274 	 "Also, remove the user from the guest group (0x222), since\n"
1275 	 "it may forbid logins.\n\n");
1276   printf("(To add or remove user from other groups, please other menu selections)\n\n");
1277   printf("Note: You may get some errors if the user is already member of some\n"
1278 	 "of these groups, but that is no problem.\n\n");
1279 
1280   fmyinput("Do it? (y/n) [n] : ", yn, 3);
1281 
1282   if (*yn == 'y') {
1283 
1284     printf("* Adding to 0x220 (Administrators) ...\n");
1285     add_user_to_grp(rid, 0x220);
1286     printf("* Adding to 0x221 (Users) ...\n");
1287     add_user_to_grp(rid, 0x221);
1288 
1289     printf("* Removing from 0x222 (Guests) ...\n");
1290     remove_user_from_grp(rid, 0x222);
1291 
1292     printf("\nPromotion DONE!\n");
1293 
1294   } else {
1295     printf("Nothing done, going back..\n");
1296   }
1297 
1298 }
1299 
1300 
interactive_remusrgrp(int rid)1301 void interactive_remusrgrp(int rid)
1302 {
1303   char inp[20];
1304   int grp, l;
1305 
1306   printf("\n=== REMOVE USER FROM A GROUP\n");
1307 
1308   list_user_groups(rid,0);
1309 
1310   printf("\nPlease enter group number (for example 220), or 0 to go back\n");
1311   l = fmyinput("Group number? : ",inp,16);
1312   sscanf(inp, "%x", &grp);
1313 
1314   if (!grp) {
1315     printf("Going back..\n");
1316     return;
1317   }
1318 
1319   printf("Removing user from group 0x%x (%d)\n",grp,grp);
1320   printf("Error messages if the user was not member of the group are harmless\n\n");
1321 
1322   remove_user_from_grp(rid, grp);
1323 
1324   printf("\nFinished removing user from group\n\n");
1325 
1326 }
1327 
1328 
interactive_addusrgrp(int rid)1329 void interactive_addusrgrp(int rid)
1330 {
1331   char inp[20];
1332   int grp, l;
1333 
1334   printf("\n == ADD USER TO A GROUP\n");
1335 
1336   list_groups(0);
1337 
1338   printf("\nPlease enter group number (for example 220), or 0 to go back\n");
1339   l = fmyinput("Group number? : ",inp,16);
1340   sscanf(inp, "%x", &grp);
1341 
1342   if (!grp) {
1343     printf("Going back..\n");
1344     return;
1345   }
1346 
1347   printf("Adding user to group 0x%x (%d)\n",grp,grp);
1348   printf("Error messages if the user was already member of the group are harmless\n\n");
1349 
1350   add_user_to_grp(rid, grp);
1351 
1352   printf("\nFinished adding user to group\n\n");
1353 
1354 
1355 }
1356 
1357 
1358 /* Decode the V-struct, and change the password
1359  * vofs - offset into SAM buffer, start of V struct
1360  * rid - the users RID, required for the DES decrypt stage
1361  *
1362  * Some of this is ripped & modified from pwdump by Jeremy Allison
1363  *
1364  */
change_pw(char * buf,int rid,int vlen,int stat)1365 char *change_pw(char *buf, int rid, int vlen, int stat)
1366 {
1367 
1368    int pl;
1369    char *vp;
1370    static char username[128],fullname[128];
1371    char comment[128], homedir[128], newp[20];
1372    int username_offset,username_len;
1373    int fullname_offset,fullname_len;
1374    int comment_offset,comment_len;
1375    int homedir_offset,homedir_len;
1376    int ntpw_len,lmpw_len,ntpw_offs,lmpw_offs;
1377    int dontchange = 0;
1378    unsigned short acb;
1379    struct user_V *v;
1380 
1381 #ifdef DOCRYPT
1382    int i;
1383    char md4[32],lanman[32];
1384    char newunipw[34], despw[20], newlanpw[16], newlandes[20];
1385    des_key_schedule ks1, ks2;
1386    des_cblock deskey1, deskey2;
1387    MD4_CTX context;
1388    unsigned char digest[16];
1389    uchar x1[] = {0x4B,0x47,0x53,0x21,0x40,0x23,0x24,0x25};
1390 #endif
1391 
1392 
1393    v = (struct user_V *)buf;
1394    vp = buf;
1395 
1396    username_offset = v->username_ofs;
1397    username_len    = v->username_len;
1398    fullname_offset = v->fullname_ofs;
1399    fullname_len    = v->fullname_len;
1400    comment_offset  = v->comment_ofs;
1401    comment_len     = v->comment_len;
1402    homedir_offset  = v->homedir_ofs;
1403    homedir_len     = v->homedir_len;
1404    lmpw_offs       = v->lmpw_ofs;
1405    lmpw_len        = v->lmpw_len;
1406    ntpw_offs       = v->ntpw_ofs;
1407    ntpw_len        = v->ntpw_len;
1408 
1409    if (!rid) {
1410      printf("No RID given. Unable to change passwords..\n");
1411      return(0);
1412    }
1413 
1414    if (gverbose) {
1415      printf("lmpw_offs: 0x%x, lmpw_len: %d (0x%x)\n",lmpw_offs,lmpw_len,lmpw_len);
1416      printf("ntpw_offs: 0x%x, ntpw_len: %d (0x%x)\n",ntpw_offs,ntpw_len,ntpw_len);
1417    }
1418 
1419    *username = 0;
1420    *fullname = 0;
1421    *comment = 0;
1422    *homedir = 0;
1423 
1424    if(username_len <= 0 || username_len > vlen ||
1425       username_offset <= 0 || username_offset >= vlen ||
1426       comment_len < 0 || comment_len > vlen   ||
1427       fullname_len < 0 || fullname_len > vlen ||
1428       homedir_offset < 0 || homedir_offset >= vlen ||
1429       comment_offset < 0 || comment_offset >= vlen ||
1430       lmpw_offs < 0 || lmpw_offs >= vlen)
1431      {
1432 	if (stat != 1) printf("change_pw: Not a legal V struct? (negative struct lengths)\n");
1433 	return(NULL);
1434      }
1435 
1436    /* Offsets in top of struct is relative to end of pointers, adjust */
1437    username_offset += 0xCC;
1438    fullname_offset += 0xCC;
1439    comment_offset += 0xCC;
1440    homedir_offset += 0xCC;
1441    ntpw_offs += 0xCC;
1442    lmpw_offs += 0xCC;
1443 
1444    cheap_uni2ascii(vp + username_offset,username,username_len);
1445    cheap_uni2ascii(vp + fullname_offset,fullname,fullname_len);
1446    cheap_uni2ascii(vp + comment_offset,comment,comment_len);
1447    cheap_uni2ascii(vp + homedir_offset,homedir,homedir_len);
1448 
1449 #if 0
1450    /* Reset hash-lengths to 16 if syskey has been reset */
1451    if (syskeyreset && ntpw_len > 16 && !stat) {
1452      ntpw_len = 16;
1453      lmpw_len = 16;
1454      ntpw_offs -= 4;
1455      (unsigned int)*(vp+0xa8) = ntpw_offs - 0xcc;
1456      *(vp + 0xa0) = 16;
1457      *(vp + 0xac) = 16;
1458    }
1459 #endif
1460 
1461    printf("\nRID     : %04d [%04x]\n",rid,rid);
1462    printf("Username: %s\n",username);
1463    printf("fullname: %s\n",fullname);
1464    printf("comment : %s\n",comment);
1465    printf("homedir : %s\n\n",homedir);
1466 
1467    list_user_groups(rid,0);
1468    printf("\n");
1469 
1470    acb = handle_F(rid,1);
1471 
1472    if (lmpw_len < 16 && gverbose) {
1473       printf("** LANMAN password not set. User MAY have a blank password.\n** Usually safe to continue. Normal in Vista\n");
1474    }
1475 
1476    if (ntpw_len < 16) {
1477       printf("** No NT MD4 hash found. This user probably has a BLANK password!\n");
1478       if (lmpw_len < 16) {
1479 	printf("** No LANMAN hash found either. Try login with no password!\n");
1480 	dontchange = 1;
1481       } else {
1482 	printf("** LANMAN password IS however set. Will now install new password as NT pass instead.\n");
1483 	printf("** NOTE: Continue at own risk!\n");
1484 	ntpw_offs = lmpw_offs;
1485 	*(vp+0xa8) = ntpw_offs - 0xcc;
1486 	ntpw_len = 16;
1487 	lmpw_len = 0;
1488       }
1489    }
1490 
1491    if (gverbose) {
1492      hexprnt("Crypted NT pw: ",(unsigned char *)(vp+ntpw_offs),16);
1493      hexprnt("Crypted LM pw: ",(unsigned char *)(vp+lmpw_offs),16);
1494    }
1495 
1496 #ifdef DOCRYPTO
1497    /* Get the two decrpt keys. */
1498    sid_to_key1(rid,(unsigned char *)deskey1);
1499    des_set_key((des_cblock *)deskey1,ks1);
1500    sid_to_key2(rid,(unsigned char *)deskey2);
1501    des_set_key((des_cblock *)deskey2,ks2);
1502 
1503    /* Decrypt the NT md4 password hash as two 8 byte blocks. */
1504    des_ecb_encrypt((des_cblock *)(vp+ntpw_offs ),
1505 		   (des_cblock *)md4, ks1, DES_DECRYPT);
1506    des_ecb_encrypt((des_cblock *)(vp+ntpw_offs + 8),
1507 		   (des_cblock *)&md4[8], ks2, DES_DECRYPT);
1508 
1509    /* Decrypt the lanman password hash as two 8 byte blocks. */
1510    des_ecb_encrypt((des_cblock *)(vp+lmpw_offs),
1511 		   (des_cblock *)lanman, ks1, DES_DECRYPT);
1512    des_ecb_encrypt((des_cblock *)(vp+lmpw_offs + 8),
1513 		   (des_cblock *)&lanman[8], ks2, DES_DECRYPT);
1514 
1515    if (gverbose) {
1516      hexprnt("MD4 hash     : ",(unsigned char *)md4,16);
1517      hexprnt("LANMAN hash  : ",(unsigned char *)lanman,16);
1518    }
1519 #endif  /* DOCRYPTO */
1520 
1521 
1522    printf("\n- - - - User Edit Menu:\n");
1523    printf(" 1 - Clear (blank) user password\n");
1524    printf("%s2 - Unlock and enable user account%s\n", (acb & 0x8000) ? " " : "(",
1525 	  (acb & 0x8000) ? " [probably locked now]" : ") [seems unlocked already]");
1526    printf(" 3 - Promote user (make user an administrator)\n");
1527    printf(" 4 - Add user to a group\n");
1528    printf(" 5 - Remove user from a group\n");
1529 #ifdef DOCRYPTO
1530    printf(" 9 - Edit (set new) user password (careful with this on XP or Vista)\n");
1531 #endif
1532    printf(" q - Quit editing user, back to user select\n");
1533 
1534    pl = fmyinput("Select: [q] > ",newp,16);
1535 
1536    if ( (pl < 1) || (*newp == 'q') || (*newp == 'Q')) return(0);
1537 
1538 
1539    if (*newp == '2') {
1540      acb = handle_F(rid,2);
1541      return(username);
1542    }
1543 
1544    if (*newp == '3') {
1545      promote_user(rid);
1546      return(username);
1547    }
1548 
1549    if (*newp == '4') {
1550      interactive_addusrgrp(rid);
1551      return(username);
1552    }
1553 
1554    if (*newp == '5') {
1555      interactive_remusrgrp(rid);
1556      return(username);
1557    }
1558 
1559 
1560 #ifdef DOCRYPT
1561    if (*newp == '9') {   /* Set new password */
1562 
1563      if (dontchange) {
1564        printf("Sorry, unable to edit since password seems blank already (thus no space for it)\n");
1565        return(0);
1566      }
1567 
1568      pl = fmyinput("New Password: ",newp,16);
1569 
1570      if (pl < 1) {
1571        printf("No change.\n");
1572        return(0);
1573      }
1574 
1575      cheap_ascii2uni(newp,newunipw,pl);
1576 
1577      make_lanmpw(newp,newlanpw,pl);
1578 
1579      /*   printf("Ucase Lanman: %s\n",newlanpw); */
1580 
1581      MD4Init (&context);
1582      MD4Update (&context, newunipw, pl<<1);
1583      MD4Final (digest, &context);
1584 
1585      if (gverbose) hexprnt("\nNEW MD4 hash    : ",digest,16);
1586 
1587      E1((uchar *)newlanpw,   x1, (uchar *)lanman);
1588      E1((uchar *)newlanpw+7, x1, (uchar *)lanman+8);
1589 
1590      if (gverbose) hexprnt("NEW LANMAN hash : ",(unsigned char *)lanman,16);
1591 
1592      /* Encrypt the NT md4 password hash as two 8 byte blocks. */
1593      des_ecb_encrypt((des_cblock *)digest,
1594 		     (des_cblock *)despw, ks1, DES_ENCRYPT);
1595      des_ecb_encrypt((des_cblock *)(digest+8),
1596 		     (des_cblock *)&despw[8], ks2, DES_ENCRYPT);
1597 
1598      des_ecb_encrypt((des_cblock *)lanman,
1599 		     (des_cblock *)newlandes, ks1, DES_ENCRYPT);
1600      des_ecb_encrypt((des_cblock *)(lanman+8),
1601 		     (des_cblock *)&newlandes[8], ks2, DES_ENCRYPT);
1602 
1603      if (gverbose) {
1604        hexprnt("NEW DES crypt   : ",(unsigned char *)despw,16);
1605        hexprnt("NEW LANMAN crypt: ",(unsigned char *)newlandes,16);
1606      }
1607 
1608      /* Reset hash length to 16 if syskey enabled, this will cause
1609       * a conversion to syskey-hashes upon next boot */
1610      if (syskeyreset && ntpw_len > 16) {
1611        ntpw_len = 16;
1612        lmpw_len = 16;
1613        ntpw_offs -= 4;
1614        *(vp+0xa8) = (unsigned int)(ntpw_offs - 0xcc);
1615        *(vp + 0xa0) = 16;
1616        *(vp + 0xac) = 16;
1617      }
1618 
1619      for (i = 0; i < 16; i++) {
1620        *(vp+ntpw_offs+i) = (unsigned char)despw[i];
1621        if (lmpw_len >= 16) *(vp+lmpw_offs+i) = (unsigned char)newlandes[i];
1622      }
1623 
1624      printf("Password changed!\n");
1625 
1626 
1627    } /* new password */
1628 #endif /* DOCRYPT */
1629 
1630    if (pl == 1 && *newp == '1') {
1631      /* Setting hash lengths to zero seems to make NT think it is blank
1632       * However, since we cant cut the previous hash bytes out of the V value
1633       * due to missing resize-support of values, it may leak about 40 bytes
1634       * each time we do this.
1635       */
1636      v->ntpw_len = 0;
1637      v->lmpw_len = 0;
1638 
1639      printf("Password cleared!\n");
1640    }
1641 
1642 #if 0
1643    hexprnt("Pw in buffer: ",(vp+ntpw_offs),16);
1644    hexprnt("Lm in buffer: ",(vp+lmpw_offs),16);
1645 #endif
1646    dirty = 1;
1647    return(username);
1648 }
1649 
1650 
1651 /* Registry edit wrapper */
1652 
mainloop(void)1653 void mainloop(void)
1654 {
1655   regedit_interactive(hive, no_hives);
1656 }
1657 
1658 
1659 /* Iterate over users in SAM file, and do things with it
1660  * automode - if null, just list, else handle auto change
1661  *            f = reset first user that is in admin group
1662  *            a = reset all users in admin group
1663  *            0x1f4 (built-in administrator account) will only be reset
1664  *            if no other users are found to be admin group
1665  */
1666 
list_users(int readable)1667 int list_users(int readable)
1668 {
1669   char s[200];
1670   struct keyval *v;
1671   int nkofs /* ,vkofs */ ;
1672   int rid;
1673   int count = 0, countri = 0;
1674   int ntpw_len;
1675 
1676   unsigned short acb;
1677 
1678   struct user_V *vpwd;
1679   struct ex_data ex;
1680 
1681   if (H_SAM < 0) return(1);
1682   nkofs = trav_path(hive[H_SAM], 0,"\\SAM\\Domains\\Account\\Users\\Names\\",0);
1683   if (!nkofs) {
1684     printf("list_users: Cannot find usernames in registry! (is this a SAM-hive?)\n");
1685     return(1);
1686   }
1687 
1688   if (readable) printf("| RID -|---------- Username ------------| Admin? |- Lock? --|\n");
1689 
1690   while ((ex_next_n(hive[H_SAM], nkofs+4, &count, &countri, &ex) > 0)) {
1691 
1692     /* Extract the value out of the username-key, value is RID  */
1693     snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",ex.name);
1694     rid = get_dword(hive[H_SAM], 0, s, TPF_VK_EXACT|TPF_VK_SHORT);
1695     if (rid == 500) strncpy(admuser,ex.name,128); /* Copy out admin-name */
1696 
1697     /* Now that we have the RID, build the path to, and get the V-value */
1698     snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
1699     v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
1700     if (!v) {
1701       printf("Cannot find value <%s>\n",s);
1702       return(1);
1703     }
1704 
1705     if (v->len < 0xcc) {
1706       printf("list_users: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
1707 	     s, v->len);
1708     } else {
1709 
1710       vpwd = (struct user_V *)&(v->data);
1711       ntpw_len = vpwd->ntpw_len;
1712 
1713       acb = handle_F(rid,0);
1714       if (readable) {
1715 	printf("| %04x | %-30.30s | %-6s | %-8s |\n",
1716 	       rid, ex.name, (list_user_groups(rid,1) ? "ADMIN" : "") , (  acb & 0x8000 ? "dis/lock" : (ntpw_len < 16) ? "*BLANK*" : "")  );
1717       } else {
1718 	printf("%04x:%s:%d:%x:%x\n",
1719 	       rid, ex.name, list_user_groups(rid,1) , acb, ntpw_len );
1720       }
1721 
1722 
1723       //      change_pw( (char *)&v->data , rid, v->len, (*automode == 'l') ? 2 : 1);
1724 
1725     }
1726     FREE(v);
1727     FREE(ex.name);
1728   }
1729   return(0);
1730 }
1731 
1732 
1733 /* Find a username in the SAM registry, then get it's V-value,
1734  * and feed it to the password changer.
1735  */
1736 
find_n_change(char * username)1737 void find_n_change(char *username)
1738 {
1739   char s[200];
1740   struct keyval *v;
1741   int rid = 0;
1742 
1743   if ((H_SAM < 0) || (!username)) return;
1744   if (*username == '0' && *(username+1) == 'x') sscanf(username,"%i",&rid);
1745 
1746   if (!rid) { /* Look up username */
1747     /* Extract the unnamed value out of the username-key, value is RID  */
1748     snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",username);
1749     rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT);
1750     if (rid == -1) {
1751       printf("Cannot find value <%s>\n",s);
1752       return;
1753     }
1754   }
1755 
1756   /*
1757   printf("Username: %s, RID = %d (0x%0x)\n",username,rid,rid);
1758   */
1759 
1760   /* Now that we have the RID, build the path to, and get the V-value */
1761   snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
1762   v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
1763   if (!v) {
1764     printf("Cannot find value <%s>\n",s);
1765     return;
1766   }
1767 
1768   if (v->len < 0xcc) {
1769     printf("Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
1770 	   s, v->len);
1771   } else {
1772     change_pw( (char *)&v->data , rid, v->len, 0);
1773     if (dirty) {
1774       if (!(put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY, TPF_VK_EXACT))) {
1775 	printf("Failed to write updated <%s> to registry! Password change not completed!\n",s);
1776       }
1777     }
1778   }
1779   FREE(v);
1780 }
1781 
1782 /* Check for presence of syskey and possibly disable it if
1783  * user wants it.
1784  * This is tricky, and extremely undocumented!
1785  * See docs for more info on what's going on when syskey is installed
1786  */
1787 
1788 #undef LSADATA
1789 
handle_syskey(void)1790 void handle_syskey(void)
1791 {
1792 
1793   /* This is \SAM\Domains\Account\F */
1794   struct samkeyf {
1795     char unknown[0x50];       /* 0x0000 - Unknown. May be machine SID */
1796     char unknown2[0x14];
1797     char syskeymode;          /* 0x0064 - Type/mode of syskey in use     */
1798     char syskeyflags1[0xb];   /* 0x0065 - More flags/settings            */
1799     char syskeyobf[0x30];     /* 0x0070 - This may very well be the obfuscated syskey */
1800   };    /* There may be more, usually 8 null-bytes? */
1801 
1802   /* Security\Policy\SecretEncryptionKey\@, only on NT5 */
1803   /* Probably contains some keyinfo for syskey. Second DWORD seems to be syskeymode */
1804   struct secpoldata {
1805     int  unknown1;             /* Some kind of flag? usually 1 */
1806     int  syskeymode;           /* Is this what we're looking for? */
1807     int  unknown2;             /* Usually 0? */
1808     char keydata[0x40];        /* Some kind of scrambled keydata? */
1809   };
1810 
1811 #ifdef LSADATA
1812   /* SYSTEM\CurrentControlSet\Control\Lsa\Data, only on NT5?? */
1813   /* Probably contains some keyinfo for syskey. Byte 0x34 seems to be mode */
1814   struct lsadata {
1815     char keydata[0x34];        /* Key information */
1816     int  syskeymode;           /* Is this what we're looking for? */
1817   };
1818 #endif
1819 
1820   /* void *fdata; */
1821   struct samkeyf *ff = NULL;
1822   struct secpoldata *sf = NULL;
1823   /* struct lsadata *ld = NULL; */
1824   int /* len, */ i,secboot, samfmode, secmode /* , ldmode */ ;
1825   struct keyval *samf, *secpol /* , *lsad */ ;
1826   char *syskeytypes[4] = { "off", "key-in-registry", "enter-passphrase", "key-on-floppy" };
1827   char yn[5];
1828 
1829   printf("\n---------------------> SYSKEY CHECK <-----------------------\n");
1830 
1831 
1832   if (H_SAM < 0) {
1833     printf("ERROR: SAM hive not loaded!\n");
1834     return;
1835   }
1836   samf = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT);
1837 
1838   if (samf && samf->len > 0x70 ) {
1839     ff = (struct samkeyf *)&samf->data;
1840     samfmode = ff->syskeymode;
1841   } else {
1842     samfmode = -1;
1843   }
1844 
1845   secboot = -1;
1846   if (H_SYS >= 0) {
1847     secboot = get_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT );
1848   }
1849 
1850   secmode = -1;
1851   if (H_SEC >=0) {
1852     secpol = get_val2buf(hive[H_SEC], NULL, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_NONE, TPF_VK_EXACT);
1853     if (secpol) {     /* Will not be found in NT 4, take care of that */
1854       sf = (struct secpoldata *)&secpol->data;
1855       secmode = sf->syskeymode;
1856     }
1857   }
1858 
1859 #ifdef LSADATA
1860   lsad = get_val2buf(hive[H_SYS], NULL, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT);
1861 
1862   if (lsad && lsad->len >= 0x38) {
1863     ld = (struct lsadata *)&lsad->data;
1864     ldmode = ld->syskeymode;
1865   } else {
1866     ldmode = -1;
1867   }
1868 #endif
1869 
1870   printf("SYSTEM   SecureBoot            : %d -> %s\n", secboot,
1871 	 (secboot < 0 || secboot > 3) ? "Not Set (not installed, good!)" : syskeytypes[secboot]);
1872   printf("SAM      Account\\F             : %d -> %s\n", samfmode,
1873 	 (samfmode < 0 || samfmode > 3) ? "Not Set" : syskeytypes[samfmode]);
1874   printf("SECURITY PolSecretEncryptionKey: %d -> %s\n", secmode,
1875 	 (secmode < 0 || secmode > 3) ? "Not Set (OK if this is NT4)" : syskeytypes[secmode]);
1876 
1877 #ifdef LSADATA
1878   printf("SYSTEM   LsaData               : %d -> %s\n\n", ldmode,
1879 	 (ldmode < 0 || ldmode > 3) ? "Not Set (strange?)" : syskeytypes[ldmode]);
1880 #endif
1881 
1882   if (secboot != samfmode && secboot != -1) {
1883     printf("WARNING: Mismatch in syskey settings in SAM and SYSTEM!\n");
1884     printf("WARNING: It may be dangerous to continue (however, resetting syskey\n");
1885     printf("         may very well fix the problem)\n");
1886   }
1887 
1888   if (secboot > 0 || samfmode > 0) {
1889     printf("\n***************** SYSKEY IS ENABLED! **************\n");
1890     printf("This installation very likely has the syskey passwordhash-obfuscator installed\n");
1891     printf("It's currently in mode = %d, %s-mode\n",secboot,
1892 	   (secboot < 0 || secboot > 3) ? "Unknown" : syskeytypes[secboot]);
1893 
1894     if (no_hives < 2) {
1895       printf("\nSYSTEM (and possibly SECURITY) hives not loaded, unable to disable syskey!\n");
1896       printf("Please start the program with at least SAM & SYSTEM-hive filenames as arguments!\n\n");
1897       return;
1898     }
1899     printf("SYSKEY is on! However, DO NOT DISABLE IT UNLESS YOU HAVE TO!\n");
1900     printf("This program can change passwords even if syskey is on, however\n");
1901     printf("if you have lost the key-floppy or passphrase you can turn it off,\n");
1902     printf("but please read the docs first!!!\n");
1903     printf("\n** IF YOU DON'T KNOW WHAT SYSKEY IS YOU DO NOT NEED TO SWITCH IT OFF!**\n");
1904     printf("NOTE: On WINDOWS 2000 and XP it will not be possible\n");
1905     printf("to turn it on again! (and other problems may also show..)\n\n");
1906     printf("NOTE: Disabling syskey will invalidate ALL\n");
1907     printf("passwords, requiring them to be reset. You should at least reset the\n");
1908     printf("administrator password using this program, then the rest ought to be\n");
1909     printf("done from NT.\n");
1910     printf("\nEXTREME WARNING: Do not try this on Vista or Win 7, it will go into endless re-boots\n\n");
1911 
1912     fmyinput("\nDo you really wish to disable SYSKEY? (y/n) [n] ",yn,2);
1913     if (*yn == 'y') {
1914       /* Reset SAM syskey infostruct, fill with zeroes */
1915       if (ff) {
1916 	ff->syskeymode = 0;
1917 
1918 	for (i = 0; i < 0x3b; i++) {
1919 	  ff->syskeyflags1[i] = 0;
1920 	}
1921 
1922 	put_buf2val(hive[H_SAM], samf, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT);
1923 
1924       }
1925       /* Reset SECURITY infostruct (if any) */
1926       if (sf) {
1927 	memset(sf, 0, secpol->len);
1928 	sf->syskeymode = 0;
1929 
1930 	put_buf2val(hive[H_SEC], secpol, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_BINARY, TPF_VK_EXACT);
1931 
1932       }
1933 
1934 #if LSADATA
1935       if (ld) {
1936 
1937 	ld->syskeymode = 0;
1938 
1939 	put_buf2val(hive[H_SYS], lsad, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT);
1940 
1941       }
1942 #endif
1943 
1944       /* And SYSTEM SecureBoot parameter */
1945 
1946       put_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT, 0);
1947 
1948       dirty = 1;
1949       syskeyreset = 1;
1950       printf("Updating passwordhash-lengths..\n");
1951       list_users(1);
1952       printf("* SYSKEY RESET!\nNow please set new administrator password!\n");
1953     } else {
1954 
1955       syskeyreset = 1;
1956     }
1957   } else {
1958     printf("Syskey not installed!\n");
1959     return;
1960   }
1961 
1962 }
1963 
1964 
1965 /* Interactive user edit */
useredit(void)1966 void useredit(void)
1967 {
1968   char iwho[100];
1969   int il;
1970 
1971   printf("\n\n===== chntpw Edit User Info & Passwords ====\n\n");
1972 
1973   if (H_SAM < 0) {
1974     printf("ERROR: SAM registry file (which contains user data) is not loaded!\n\n");
1975     return;
1976   }
1977 
1978 
1979   list_users(1);
1980 
1981   while (1) {
1982     printf("\nSelect: ! - quit, . - list users, 0x<RID> - User with RID (hex)\n");
1983     printf("or simply enter the username to change: [%s] ",admuser);
1984     il = fmyinput("",iwho,32);
1985     if (il == 1 && *iwho == '.') { printf("\n"); list_users(1); continue; }
1986     if (il == 1 && *iwho == '!') return;
1987     if (il == 0) strcpy(iwho,admuser);
1988     find_n_change(iwho);
1989   }
1990 
1991 }
1992 
1993 
recoveryconsole()1994 void recoveryconsole()
1995 {
1996 
1997   int cmd = 0;
1998   int sec = 0;
1999   static char *scpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SetCommand";
2000   static char *slpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SecurityLevel";
2001   char yn[5];
2002 
2003   if (H_SOF < 0) {
2004     printf("\nSOFTWARE-hive not loaded, and there's where RecoveryConsole settings are..\n");
2005     return;
2006   }
2007 
2008   cmd = get_dword(hive[H_SOF],0,scpath,TPF_VK_EXACT);
2009   sec = get_dword(hive[H_SOF],0,slpath,TPF_VK_EXACT);
2010 
2011   if (cmd == -1 && sec == -1) {
2012     printf("\nDid not find registry entries for RecoveryConsole.\n(RecoveryConsole is only in Windows 2000 and XP)\n");
2013     return;
2014   }
2015 
2016   printf("\nRecoveryConsole:\n- Extended SET command is \t%s\n", cmd>0 ? "ENABLED (1)" : "DISABLED (0)");
2017   printf("- Administrator password login: %s\n", sec>0 ? "SKIPPED (1)" : "ENFORCED (0)");
2018 
2019   fmyinput("\nDo you want to change it? (y/n) [n] ",yn,2);
2020   if (*yn == 'y') {
2021     cmd ^= 1;
2022     sec ^= 1;
2023     if (!put_dword(hive[0], 0, scpath, TPF_VK_EXACT, cmd)) printf("Update of SET level failed registry edit\n");
2024     if (!put_dword(hive[0], 0, slpath, TPF_VK_EXACT, sec)) printf("Update of login level failed registry edit\n");
2025     printf("Done!\n");
2026   }
2027 
2028 }
2029 
2030 
2031 /* Interactive menu system */
2032 
interactive(void)2033 void interactive(void)
2034 {
2035   int il;
2036   char inbuf[20];
2037 
2038   while(1) {
2039     printf("\n\n<>========<> chntpw Main Interactive Menu <>========<>\n\n"
2040 	   "Loaded hives:");
2041     for (il = 0; il < no_hives; il++) {
2042       printf(" <%s>",hive[il]->filename);
2043     }
2044 
2045     printf("\n\n");
2046 
2047     /* Make menu selection depending on what is loaded
2048        but it is still possible to select even if not shown */
2049 
2050     if (H_SAM >= 0) {
2051       printf("  1 - Edit user data and passwords\n");
2052       printf("  2 - List groups\n");
2053     }
2054     if (H_SOF >= 0) {
2055       printf("  3 - RecoveryConsole settings\n");
2056       printf("  4 - Show product key (DigitalProductID)\n");
2057     }
2058 #if 0
2059     if (H_SAM >= 0 && H_SYS >= 0 && H_SEC >= 0) {
2060       printf("  8 - Syskey status & change\n");
2061     }
2062 #endif
2063 
2064     printf("      - - -\n"
2065 	   "  9 - Registry editor, now with full write support!\n"
2066 	   "  q - Quit (you will be asked if there is something to save)\n"
2067 	   "\n\n");
2068 
2069     il = fmyinput("What to do? [1] -> ", inbuf, 10);
2070 
2071     if (!il) useredit();
2072     if (il) {
2073       switch(inbuf[0]) {
2074       case '1': useredit(); break;
2075       case '2': list_groups(0); break;
2076       case '3': recoveryconsole(); break;
2077       case '4': cat_dpi(hive[H_SOF],0,"\\Microsoft\\Windows NT\\CurrentVersion\\DigitalProductId"); break;
2078       case '8': handle_syskey(); break;
2079       case '9': mainloop(); break;
2080       case 'q': return; break;
2081       }
2082     }
2083   }
2084 }
2085 
2086 
cmd_usrgrp(char * user,char * grp,int what)2087 int cmd_usrgrp(char *user, char *grp, int what)
2088 {
2089   int numgrp;
2090   int rid = 0;
2091   char s[200];
2092 
2093   numgrp = strtol(grp, NULL, 0);
2094 
2095   printf("numgrp = %d (0x%x)\n", numgrp, numgrp);
2096 
2097   if ((H_SAM < 0) || (!user)) return(1);
2098   if (*user == '0' && *(user+1) == 'x') sscanf(user,"%i",&rid);
2099 
2100   if (!rid) { /* Look up username */
2101     /* Extract the unnamed value out of the username-key, value is RID  */
2102     snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",user);
2103     rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT);
2104     if (rid == -1) {
2105       printf("User <%s> not found\n",user);
2106       return(1);
2107     }
2108   }
2109 
2110   printf("Username: %s, RID = %d (0x%0x)\n",user,rid,rid);
2111 
2112   switch (what) {
2113   case 0: return(add_user_to_grp(rid, numgrp)); break;
2114   case 1: return(remove_user_from_grp(rid, numgrp)); break;
2115   }
2116 
2117 
2118   return(0);
2119 
2120 }
2121 
2122 
do_automode(char * automode,char * who,char * grp)2123 void do_automode(char *automode, char *who, char *grp)
2124 {
2125   struct sid_binary sid;
2126   char *sidstr;
2127 
2128 #if 1
2129   printf("DEBUG: do_automode start\n");
2130   printf("automode = %s\n",automode);
2131   printf("who = %s\n",who);
2132   printf("grp = %s\n",grp);
2133 #endif
2134 
2135   switch (*automode) {
2136   case 'l': list_users(0); break;
2137   case 's': if(get_machine_sid((char *)&sid)) { sidstr = sid_to_string(&sid); puts(sidstr); FREE(sidstr);} break;
2138   case 'g': list_groups(1); break;
2139   case 'a': cmd_usrgrp(who, grp, 0); break;  /* Add user to group */
2140   case 'r': cmd_usrgrp(who, grp, 1); break;  /* Remove user from group */
2141 
2142   }
2143 
2144 
2145   //  printf("DEBUG: do_automode end\n");
2146 
2147 }
2148 
2149 
usage(void)2150 void usage(void) {
2151    printf("chntpw: change password of a user in a Windows SAM file,\n"
2152 	  "or invoke registry editor. Should handle both 32 and 64 bit windows and\n"
2153 	  "all version from NT3.x to Win7\n"
2154 	  "chntpw [OPTIONS] <samfile> [systemfile] [securityfile] [otherreghive] [...]\n"
2155 	  " -h          This message\n"
2156 	  " -u <user>   Username or RID to change, Administrator is default\n"
2157 	  " -l          list all users in SAM file\n"
2158 	  " -i          Interactive Menu system\n"
2159 	  " -e          Registry editor. Now with full write support!\n"
2160 	  " -d          Enter buffer debugger instead (hex editor), \n"
2161           " -v          Be a little more verbose (for debuging)\n"
2162 	  " -L          For scripts, write names of changed files to /tmp/changed\n"
2163 	  " -N          No allocation mode. Only same length overwrites possible (very safe mode)\n"
2164 	  " -E          No expand mode, do not expand hive file (safe mode)\n"
2165 	  " -A <subcommand> Auto / Noninteractive. Do stuff without asking\n"
2166 	  " -A F   reset on first admin user (lowest RID)\n"
2167 	  " -A A   reset all admins\n"
2168 	  " -A l   list all users, output it parseable\n"
2169 	  " -A g   list groups\n"
2170 	  " -A s   show machine SID\n"
2171 	  " -A a -u <username|RID> -u <grpid>  Add user to group\n"
2172 	  " -A r -u <username|RID> -g <grpid>  Remove user from group\n"
2173 
2174 	  "\nUsernames can be given as name or RID (in hex with 0x first)\n"
2175 	  "<grpid> group IDs must be given as number, can be hex with 0x first\n"
2176 	  "Example: chntpw -A a -u 0x3aa -g 1000 # Adds user with RID hex 0x3aa to group decimal 1000\n"
2177           "\nSee readme file on how to get to the registry files, and what they are.\n"
2178           "Source/binary freely distributable under GPL v2 license. See README for details.\n"
2179           "NOTE: This program is somewhat hackish! You are on your own!\n"
2180 	  );
2181 }
2182 
2183 
main(int argc,char ** argv)2184 int main(int argc, char **argv)
2185 {
2186 
2187   int dodebug = 0, list = 0, inter = 0,edit = 0,il,d = 0, dd = 0, logchange = 0;
2188   int mode = HMODE_INFO;
2189   extern int /* opterr, */ optind;
2190   extern char* optarg;
2191   char *filename,c;
2192   char *who = "Administrator";
2193   char *grp = NULL;
2194   char iwho[100];
2195   char *automode = "";
2196   FILE *ch;     /* Write out names of touched files to this */
2197 
2198   char *options = "A:LENidehlvu:g:";
2199 
2200   while((c=getopt(argc,argv,options)) > 0) {
2201     switch(c) {
2202     case 'd': dodebug = 1; break;
2203     case 'e': edit = 1; break;
2204     case 'L': logchange = 1; break;
2205     case 'N': mode |= HMODE_NOALLOC; break;
2206     case 'E': mode |= HMODE_NOEXPAND; break;
2207     case 'l': list = 1; break;
2208     case 'v': mode |= HMODE_VERBOSE; gverbose = 1; break;
2209     case 'i': inter = 1; break;
2210     case 'u': who = optarg; break;
2211     case 'g': grp = optarg; break;
2212     case 'A': automode = optarg; mode &= ~HMODE_INFO; break;
2213     case 'h': usage(); exit(0); break;
2214     default: usage(); exit(1); break;
2215     }
2216   }
2217 
2218   if (!*automode) printf("%s\n",chntpw_version);
2219 
2220   filename=argv[optind];
2221   if (!filename || !*filename) {
2222     usage(); exit(1);
2223   }
2224   do {
2225     if (!(hive[no_hives] = openHive(filename,
2226 				    HMODE_RW|mode))) {
2227       fprintf(stderr,"%s: Unable to open/read a hive, exiting..\n",argv[0]);
2228       exit(1);
2229     }
2230     switch(hive[no_hives]->type) {
2231     case HTYPE_SAM:      H_SAM = no_hives; break;
2232     case HTYPE_SOFTWARE: H_SOF = no_hives; break;
2233     case HTYPE_SYSTEM:   H_SYS = no_hives; break;
2234     case HTYPE_SECURITY: H_SEC = no_hives; break;
2235     }
2236     no_hives++;
2237     filename = argv[optind+no_hives];
2238   } while (filename && *filename && no_hives < MAX_HIVES);
2239 
2240 #if 0
2241   printf("user = %s\n",who);
2242   printf("automode = %s\n",automode);
2243 #endif
2244 
2245   if (dodebug) {
2246     debugit(hive[0]->buffer,hive[0]->size);
2247   } else if (*automode) {
2248     check_get_samdata(0);
2249     do_automode(automode, who, grp);
2250 
2251   } else if (inter) {
2252     check_get_samdata(1);
2253     interactive();
2254   } else if (edit) {
2255     check_get_samdata(1);
2256      mainloop();
2257   } else if (list) {
2258     check_get_samdata(1);
2259     list_users(1);
2260   } else if (who) {
2261     check_get_samdata(1);
2262     find_n_change(who);
2263   }
2264 
2265 
2266 
2267 
2268 
2269   if (list != 1) {
2270     if (!*automode) printf("\nHives that have changed:\n #  Name\n");
2271     for (il = 0; il < no_hives; il++) {
2272       if (hive[il]->state & HMODE_DIRTY) {
2273 	if (!logchange && !*automode) printf("%2d  <%s>",il,hive[il]->filename);
2274 	if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n");
2275 	if (!*automode) printf("\n");
2276 
2277 	d = 1;
2278       }
2279     }
2280     if (d) {
2281       /* Only prompt user if logging of changed files has not been set */
2282       /* Thus we assume confirmations are done externally if they ask for a list of changes */
2283       if (!logchange && !*automode) fmyinput("Write hive files? (y/n) [n] : ",iwho,3);
2284       if (*iwho == 'y' || logchange || *automode) {
2285 	if (logchange) {
2286 	  ch = fopen("/tmp/changed","w");
2287 	}
2288 	for (il = 0; il < no_hives; il++) {
2289 	  if (hive[il]->state & HMODE_DIRTY) {
2290 	    if (!*automode) printf("%2d  <%s> - ",il,hive[il]->filename);
2291 	    if (!writeHive(hive[il])) {
2292 	      if (!*automode) printf("OK");
2293 	      if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n");
2294 	      if (!*automode) printf("\n");
2295 	      if (logchange) fprintf(ch,"%s ",hive[il]->filename);
2296 	      dd = 2;
2297 	    }
2298 	  }
2299 	}
2300 	if (logchange) {
2301 	  fprintf(ch,"\n");
2302 	  fclose(ch);
2303 	}
2304       } else {
2305 	printf("Not written!\n\n");
2306       }
2307     } else {
2308       if (!*automode) printf("None!\n\n");
2309     }
2310   } /* list only check */
2311   return(dd);
2312 }
2313