chntpw-1.0/0000755000175000017500000000000012370507714011011 5ustar jfsjfschntpw-1.0/samusrgrp.c0000644000175000017500000002051712273223316013200 0ustar jfsjfs/* * samusrgrp.c - SAM database, add or remove user in a group * * Command line utility, non-inreractive to add or remove a user to/from * a local group in the SAM database, list groups with memberships etc * * When run as: * samusrtogrp - add user to group * samusrfromgrp - remove user from a group * or as any other name, option of what to do must be specified * * Changes: * 2013 - aug: cleaned up for release, still some debug & strangeness left * 2013 - apr-may: add, remove, list working (more or less) * 2012 - oct: First version, never released * ***** * * Copyright (c) 1997-2014 Petter Nordahl-Hagen. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See file GPL.txt for the full license. * ***** */ #include #include #include #include #include "ntreg.h" #include "sam.h" const char samusrgrp_version[] = "samusrgrp version 0.2 140201, (c) Petter N Hagen"; /* Global verbosity flag */ int gverbose = 0; /* Array of loaded hives */ #define MAX_HIVES 10 struct hive *hive[MAX_HIVES+1]; int no_hives = 0; int H_SAM = -1; /* Print machine SID. Put into here since no better place for now */ void cmd_machinesid(void) { struct sid_binary sid; char *sidstr; if (sam_get_machine_sid(hive[H_SAM], (char *)&sid)) { sidstr = sam_sid_to_string(&sid); puts(sidstr); FREE(sidstr); } } /* Get and parse parameters for group changes */ int cmd_usrgrp(char *user, char *grp, int what, int human) { int numgrp; int rid = 0; char *resolveduser = NULL; char *resolvedgroup = NULL; char s[200]; numgrp = strtol(grp, NULL, 0); if ((H_SAM < 0) || (!user)) return(1); if (*user == '0' && *(user+1) == 'x') sscanf(user,"%i",&rid); if (!rid) { /* Look up username */ /* Extract the unnamed value out of the username-key, value is RID */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",user); rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT); if (rid == -1) { printf("User <%s> not found\n",user); return(1); } } /* At this point we have a RID, so get the real username it maps to just to show it */ resolveduser = sam_get_username(hive[H_SAM], rid); if (!resolveduser) return(1); /* Fails if RID does not exists */ resolvedgroup = sam_get_groupname(hive[H_SAM], numgrp); if (!resolvedgroup) return(1); /* Fails if GID does not exists */ if (human) printf("%s user <%s> with RID = %d (0x%0x) %s group <%s> with GID = %d (0x%x)\n", (what == 1 ? "Add" : "Remove"), resolveduser, rid, rid, (what == 1 ? "to" : "from"), resolvedgroup, numgrp, numgrp ); FREE(resolveduser); FREE(resolvedgroup); switch (what) { case 1: return(!sam_add_user_to_grp(hive[H_SAM] ,rid, numgrp)); break; case 2: return(!sam_remove_user_from_grp(hive[H_SAM] ,rid, numgrp)); break; } return(0); } void usage(void) { printf(" [-a|-r] -u -g \n" "Add or remove a (local) user to/from a group\n" "Mode:" " -a = add user to group\n" " -r = remove user from group\n" " -l = list groups\n" " -L = list groups and also their members\n" " -s = Print machine SID\n" "Parameters:\n" " can be given as a username or a RID in hex with 0x in front\n" " is the group number, in hex with 0x in front\n" " Example:\n" " -a -u theboss -g 0x220 -> add user named 'theboss' group hex 220 (administrators)\n" " -a -u 0x3ea -g 0x221 -> add user with RID (hex) 3ea group hex 221 (users)\n" " -r -u 0x3ff -g 0x220 -> remove user RID 0x3ff from grp 0x220\n" " Usernames with international characters usually fails to be found,\n" " please use RID number instead\n" " If success, there will be no output, and exit code is 0\n" " Also, success if user already in (or not in if -r) the group\n" "Options:\n" " -H : Human readable output, else parsable\n" " -N : No allocate mode, only allow edit of existing values with same size\n" " -E : No expand mode, do not expand hive file (safe mode)\n" " -t : Debug trace of allocated blocks\n" " -v : Some more verbose messages/debug\n" "Multi call binary, if program is named:\n" " samusrtogrp -- Assume -a mode: Add a user into a group\n" " samusrfromgrp -- Assume -r mode: Remove user from a group\n" ); } int main(int argc, char **argv) { extern int optind; extern char* optarg; int what = 0; int add = 0; int rem = 0; int mode = 0; int m = 0; int list = 0; int human = 0; int ret, wret, il; char *hivename; char c; char *usr = NULL; char *grp = NULL; char *options = "aru:g:vNEthlLHs"; if (!strcmp(argv[0],"samusrtogrp")) what = 1; if (!strcmp(argv[0],"samusrfromgrp")) what = 2; if (!strcmp(argv[0],"samgrplist")) what = 3; while((c=getopt(argc,argv,options)) > 0) { switch(c) { case 'a': add = 1; m++; break; /* Add user */ case 'r': rem = 2; m++; break; /* Remove user */ case 'l': list = 1; m++; break; /* List groups */ case 'L': list = 2; m++; break; /* List groups + members */ case 's': list = 3; m++; break; /* Print machine sid */ case 'u': usr = optarg; break; case 'g': grp = optarg; break; case 'H': human = 1; break; case 'v': mode |= HMODE_VERBOSE; gverbose = 1; break; case 'N': mode |= HMODE_NOALLOC; break; case 'E': mode |= HMODE_NOEXPAND; break; case 't': mode |= HMODE_TRACE; break; case 'h': printf("%s\n%s ",samusrgrp_version,argv[0]); usage(); exit(0); break; default: printf("%s\n%s ",samusrgrp_version,argv[0]); usage(); exit(1); break; } } if (m == 0) { fprintf(stderr,"%s: ERROR: One of mode -a -r -l -L must be specified\n",argv[0]); printf("%s\n%s ",samusrgrp_version,argv[0]); usage(); exit(1); } if (m > 1) { fprintf(stderr,"%s: ERROR: Please select only one of modes -a -r -l -L\n",argv[0]); exit(1); } if (!list && (!usr || !grp || !*usr || !*grp) ) { fprintf(stderr,"%s: ERROR: Both -u and -g must be specified.\n",argv[0]); exit(1); } /* Implicit mode parameter overrides mode based on binary name */ if (add) what = 1; if (rem) what = 2; if (list) what = 3; // printf("add = %d, rem = %d, list = %d, what = %d\n",add,rem,list,what); /* Load hives. Only first SAM hive will be used however */ hivename = argv[optind+no_hives]; if (!hivename || !*hivename) { fprintf(stderr,"%s: ERROR: You must specify a SAM registry hive filename.\n",argv[0]); exit(1); } do { if (!(hive[no_hives] = openHive(hivename, HMODE_RW|mode))) { fprintf(stderr,"%s: ERROR: Unable to open/read registry hive, cannot continue\n",argv[0]); exit(1); } switch(hive[no_hives]->type) { case HTYPE_SAM: H_SAM = no_hives; break; // case HTYPE_SOFTWARE: H_SOF = no_hives; break; // case HTYPE_SYSTEM: H_SYS = no_hives; break; // case HTYPE_SECURITY: H_SEC = no_hives; break; } no_hives++; hivename = argv[optind+no_hives]; } while (hivename && *hivename && no_hives < MAX_HIVES); if (H_SAM == -1) { fprintf(stderr,"%s: WARNING: Registry hive does not look like SAM!\n" "%s: WARNING: Continuing anyway, may lead to strange messages/failures!\n",argv[0],argv[0]); H_SAM = 0; } /* Do logic */ if (list == 3) { cmd_machinesid(); ret = 0; } else { if (list) { sam_list_groups(hive[H_SAM], list - 1, human); ret = 0; } else { ret = cmd_usrgrp(usr, grp, what, human); if (!ret && human) printf("Success!\n"); } } /* write registry hive (if needed) */ wret = 0; for (il = 0; il < no_hives; il++) { wret |= writeHive(hive[il]); if (hive[il]->state & HMODE_DIDEXPAND) fprintf(stderr," WARNING: Registry file %s was expanded! Experimental! Use at own risk!\n",hive[il]->filename); while (no_hives > 0) closeHive(hive[--no_hives]); } return(ret | wret); } chntpw-1.0/COPYING.txt0000644000175000017500000004313312273223316012661 0ustar jfsjfs GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. chntpw-1.0/hash.c0000644000175000017500000000531312273223316012075 0ustar jfsjfs/* Mickeysoft hashroutine in XP 'lh' key index lists */ #include #include #include static const unsigned char charset2upper[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */ }; int main(void) { char str[500]; long hash = 0; int i; FILE *f; f = fdopen(0,"r"); fgets(str, 499, f); str[strlen(str)-1] = 0; for (i = 0; i < strlen(str); i++) { hash *= 37; hash += charset2upper[(unsigned char)str[i]]; } printf("hash = %08x\n",hash); } chntpw-1.0/ntreg.c0000600000175000017500000035613012273223316012267 0ustar jfsjfs/* * ntreg.c - Windows (NT and up) Registry Hive access library * should be able to handle most basic functions: * iterate, add&delete keys and values, read stuff, change stuff etc * no rename of keys or values yet.. * also contains some minor utility functions (string handling etc) for now * * 2014-jan: openhive() now more compatible with build on non-unix? * 2013-aug: Enter buil-in buffer debugger only if in trace mode, else return error or abort() * 2013-may-aug: Fixed critical bug in del_value which could * thrash the hive when removing value in bottom of key. * And a pointer not reinitialized when buffer reallocated in some cases, fixed. * Thanks to Jacky To for reporting those two. * Some minor adjustments for compiler. A few more utility functions. * 2012-oct: Added set_val_type. some minor changes. * 2011-may: Seems like large values >16k or something like that is split * into several blocks (db), have tried to implement that. * Vista seems to accept it. Not tested on others yet. * 2011-may: Expansion now seems to be working, have lot of test accepted by * vista and win7. But no warranties.. * 2011-may: Found a couple of nasty bugs inn add_key(), making Vista and newer * reject (remove) keys on hive load. * May have been there for a long time according to reports. * 2011-apr: Fixed some problems with the allocator when at the end of a hbin. * 2011-apr: .reg file import. Ugly one, but it seems to work. Found * quite a lot of bugs in other places while testing it. * String handling when international characters or wide (UTF-16) * is a pain, and very ugly. May not work in some cases. * Will keep wide (16 bit) characters in strings when importing from * .reg file that has it, like what regedit.exe generates for example. * 2011-apr: Added routines for hive expansion. Will add new hbin at end of file * when needed. If using library, please read ugly warnings in "alloc_block()". * 2010-jun: Patches from Frediano Ziglio adding support for wide characters * and some bugfixes. Thank you! * 2008-mar: Type QWORD (XP/Vista and newer) now recognized * 2008-mar: Most functions accepting a path now also have a parameter specifying if * the search should be exact or on first match basis * 2008-mar: Fixed bug which skipped first indirect index table when deleting keys, * usually leading to endless loop when recursive deleting. * 2008-mar: Export to .reg file by Leo von Klenze, expanded a bit by me. * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev * http://bugs.gentoo.org/show_bug.cgi?id=185411 * 2007-sep: Verbosity/debug messages minor changes * 2007-apr: LGPL license. * 2004-aug: Deep indirect index support. NT351 support. Recursive delete. * Debugged a lot in allocation routines. Still no expansion. * 2004-jan: Verbosity updates * 2003-jan: Allocation of new data, supports adding/deleting keys & stuff. * Missing is expanding the file. * 2003-jan: Seems there may be garbage pages at end of file, not zero pages * now stops enumerating at first non 'hbin' page. * * NOTE: The API is not frozen. It can and will change every release. * ***** * * NTREG - Window registry file reader / writer library * Copyright (c) 1997-2014 Petter Nordahl-Hagen. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * See file LGPL.txt for the full license. * */ #include #include #include #include #include #include #include #include #include #include #include #include "ntreg.h" /* Set to abort() and debug on more critical errors */ #define DOCORE 1 #define ZEROFILL 1 /* Fill blocks with zeroes when allocating and deallocating */ #define ZEROFILLONLOAD 0 /* Fill blocks marked as unused/deallocated with zeroes on load. FOR DEBUG */ const char ntreg_version[] = "ntreg lib routines, v0.95 140201, (c) Petter N Hagen"; const char *val_types[REG_MAX+1] = { "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", /* 0 - 4 */ "REG_DWORD_BIG_ENDIAN", "REG_LINK", /* 5 - 6 */ "REG_MULTI_SZ", "REG_RESOUCE_LIST", "REG_FULL_RES_DESC", "REG_RES_REQ", /* 7 - 10 */ "REG_QWORD", /* 11 */ }; static char * string_prog2regw(void *string, int len, int* out_len); /* Utility routines */ /* toupper() table for registry hashing functions, so we don't have to * dependent upon external locale lib files */ static const unsigned char reg_touppertable[] = { /* ISO 8859-1 is probably not the one.. */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */ }; /* Use table above in strcasecmp else add_key may put names in wrong order and windows actually verifies that on hive load!! or at least it finds out in some cases.. */ int strn_casecmp(const char *s1, const char *s2, size_t n) { char r; while ( *s1 && *s2 && n ) { r = (unsigned char)reg_touppertable[(unsigned char)*s1] - (unsigned char)reg_touppertable[(unsigned char)*s2]; if (r) return(r); n--; s1++; s2++; } if ( (!*s1 && !*s2) || !n) return(0); if ( !*s1 ) return(-1); return(1); } char *str_dup( const char *str ) { char *str_new; if (!str) return(0); CREATE( str_new, char, strlen(str) + 1 ); strcpy( str_new, str ); return str_new; } char *str_cat(char *str, char *add) { if (!add) return(str); str = (char *) realloc(str, strlen(str) + strlen(add) + 3); strcat (str, add); return (str); } char *str_catf(char *str, const char *format, ... ) { va_list ap; char add[640] ; va_start(ap, format) ; vsprintf(add, format, ap) ; va_end(ap) ; str = (char *) realloc(str, strlen(str) + strlen(add) + 3); strcat (str, add ); return(str); } /* Copy non-terminated string to buffer we allocate and null terminate it * Uses length only, does not check for nulls */ char *mem_str( const char *str, int len ) { char *str_new; if (!str) return 0 ; CREATE( str_new, char, len + 1 ); memcpy( str_new, str, len); *(str_new+len) = 0; return str_new; } int fmyinput(char *prmpt, char *ibuf, int maxlen) { printf("%s",prmpt); fgets(ibuf,maxlen+1,stdin); ibuf[strlen(ibuf)-1] = 0; return(strlen(ibuf)); } /* Print len number of hexbytes */ void hexprnt(char *s, unsigned char *bytes, int len) { int i; printf("%s",s); for (i = 0; i < len; i++) { printf("%02x ",bytes[i]); } printf("\n"); } /* HexDump all or a part of some buffer */ void hexdump(char *hbuf, int start, int stop, int ascii) { char c; int diff,i; while (start < stop ) { diff = stop - start; if (diff > 16) diff = 16; printf(":%05X ",start); for (i = 0; i < diff; i++) { printf("%02X ",(unsigned char)*(hbuf+start+i)); } if (ascii) { for (i = diff; i < 16; i++) printf(" "); for (i = 0; i < diff; i++) { c = *(hbuf+start+i); printf("%c", isprint(c) ? c : '.'); } } printf("\n"); start += 16; } } /* General search routine, find something in something else */ int find_in_buf(char *buf, char *what, int sz, int len, int start) { int i; for (; start < sz; start++) { for (i = 0; i < len; i++) { if (*(buf+start+i) != *(what+i)) break; } if (i == len) return(start); } return(0); } /* Get INTEGER from memory. This is probably low-endian specific? */ int get_int( char *array ) { return ((array[0]&0xff) + ((array[1]<<8)&0xff00) + ((array[2]<<16)&0xff0000) + ((array[3]<<24)&0xff000000)); } /* Quick and dirty UNICODE to std. ascii */ void cheap_uni2ascii(char *src, char *dest, int l) { for (; l > 0; l -=2) { *dest = *src; dest++; src +=2; } *dest = 0; } /* Quick and dirty ascii to unicode */ void cheap_ascii2uni(char *src, char *dest, int l) { for (; l > 0; l--) { *dest++ = *src++; *dest++ = 0; } } void skipspace(char **c) { while( **c == ' ' ) (*c)++; } int gethex(char **c) { int value; skipspace(c); if (!(**c)) return(0); sscanf(*c,"%x",&value); while( **c != ' ' && (**c)) (*c)++; return(value); } /* Get a string of HEX bytes (space separated), * or if first char is ' get an ASCII string instead. */ int gethexorstr(char **c, char *wb) { int l = 0; skipspace(c); if ( **c == '\'') { (*c)++; while ( **c ) { *(wb++) = *((*c)++); l++; } } else { do { *(wb++) = gethex(c); l++; skipspace(c); } while ( **c ); } return(l); } /* Simple buffer debugger, returns 1 if buffer dirty/edited */ int debugit(char *buf, int sz) { char inbuf[100],whatbuf[100],*bp; int dirty=0,to,from,l,i,j,wlen,cofs = 0; printf("Buffer debugger. '?' for help.\n"); while (1) { l = fmyinput(".",inbuf,90); bp = inbuf; skipspace(&bp); if (l > 0 && *bp) { switch(*bp) { case 'd' : bp++; if (*bp) { from = gethex(&bp); to = gethex(&bp); } else { from = cofs; to = 0; } if (to == 0) to = from + 0x100; if (to > sz) to = sz; hexdump(buf,from,to,1); cofs = to; break; case 'a' : bp++; if (*bp) { from = gethex(&bp); to = gethex(&bp); } else { from = cofs; to = 0; } if (to == 0) to = from + 0x100; if (to > sz) to = sz; hexdump(buf,from,to,0); cofs = to; break; case 'q': return(0); break; case 's': if (!dirty) printf("Buffer has not changed, no need to write..\n"); return(dirty); break; case 'h': bp++; if (*bp == 'a') { from = 0; to = sz; bp++; } else { from = gethex(&bp); to = gethex(&bp); } wlen = gethexorstr(&bp,whatbuf); if (to > sz) to = sz; printf("from: %x, to: %x, wlen: %d\n",from,to,wlen); for (i = from; i < to; i++) { for (j = 0; j < wlen; j++) { if ( *(buf+i+j) != *(whatbuf+j)) break; } if (j == wlen) printf("%06x ",i); } printf("\n"); break; case ':': bp++; if (!*bp) break; from = gethex(&bp); wlen = gethexorstr(&bp,whatbuf); printf("from: %x, wlen: %d\n",from,wlen); memcpy(buf+from,whatbuf,wlen); dirty = 1; break; case '?': printf("d [] [] - dump buffer within range\n"); printf("a [] [] - same as d, but without ascii-part (for cut'n'paste)\n"); printf(": [ ...] - change bytes\n"); printf("h [ ...] - hunt (search) for bytes\n"); printf("ha [ etc. you may give 'string to enter/search a string\n"); break; default: printf("?\n"); break; } } } } /* Utility function to copy and append two values into a new one * Will allocate new buffer, but not touch the input ones */ struct keyval *reg_valcat(struct keyval *a, struct keyval *b) { int newsize = 0; int asize = 0; int bsize = 0; struct keyval *result = NULL; if (!a && !b) return(NULL); if (a) asize = a->len; if (b) bsize = b->len; newsize = asize + bsize; // printf("asize = %d, bsize = %d, newsize = %d\n",asize,bsize,newsize); ALLOC(result, sizeof(struct keyval) + newsize, 1); if (asize) memcpy(&result->data, &a->data, asize); if (bsize) memcpy(&result->data + asize / sizeof(int), &b->data, bsize); result->len = newsize; //printf("reg_valcat done\n"); return(result); } /* ========================================================================= */ /* The following routines are mostly for debugging, I used it * much during discovery. the -t command line option uses it, * also the 'st' and 's' from the editor & hexdebugger. * All offsets shown in these are unadjusted (ie you must add * headerpage (most often 0x1000) to get file offset) */ /* Parse the nk datablock * vofs = offset into struct (after size linkage) */ void parse_nk(struct hive *hdesc, int vofs, int blen) { struct nk_key *key; int i; printf("== nk at offset %0x\n",vofs); /* #define D_OFFS2(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */ #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) key = (struct nk_key *)(hdesc->buffer + vofs); printf("%04x type = 0x%02x %s\n", D_OFFS(type) ,key->type, (key->type == KEY_ROOT ? "ROOT_KEY" : "") ); printf("%04x timestamp skipped\n", D_OFFS(timestamp) ); printf("%04x parent key offset = 0x%0x\n", D_OFFS(ofs_parent) ,key->ofs_parent + 0x1000); printf("%04x number of subkeys = %d\n", D_OFFS(no_subkeys),key->no_subkeys); printf("%04x lf-record offset = 0x%0x\n",D_OFFS(ofs_lf),key->ofs_lf + 0x1000); printf("%04x number of values = %d\n", D_OFFS(no_values),key->no_values); printf("%04x val-list offset = 0x%0x\n",D_OFFS(ofs_vallist),key->ofs_vallist + 0x1000); printf("%04x sk-record offset = 0x%0x\n",D_OFFS(ofs_sk),key->ofs_sk + 0x1000); printf("%04x classname offset = 0x%0x\n",D_OFFS(ofs_classnam),key->ofs_classnam + 0x1000); printf("%04x dummy3 = 0x%0x (%d)\n",D_OFFS(dummy3),key->dummy3,key->dummy3); printf("%04x dummy4 = 0x%0x (%d)\n",D_OFFS(dummy4),key->dummy4,key->dummy4); printf("%04x dummy5 = 0x%0x (%d)\n",D_OFFS(dummy5),key->dummy5,key->dummy5); printf("%04x dummy6 = 0x%0x (%d)\n",D_OFFS(dummy6),key->dummy6,key->dummy6); printf("%04x dummy7 = 0x%0x (%d)\n",D_OFFS(dummy7),key->dummy7,key->dummy7); printf("%04x name length = %d\n", D_OFFS(len_name),key->len_name); printf("%04x classname length = %d\n", D_OFFS(len_classnam),key->len_classnam); printf("%04x Key name: <",D_OFFS(keyname) ); for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]); printf(">\n== End of key info.\n"); } /* Parse the vk datablock * vofs = offset into struct (after size linkage) */ void parse_vk(struct hive *hdesc, int vofs, int blen) { struct vk_key *key; int i; printf("== vk at offset %0x\n",vofs); key = (struct vk_key *)(hdesc->buffer + vofs); printf("%04x name length = %d (0x%0x)\n", D_OFFS(len_name), key->len_name, key->len_name ); printf("%04x length of data = %d (0x%0x)\n", D_OFFS(len_data), key->len_data, key->len_data ); printf("%04x data offset = 0x%0x\n",D_OFFS(ofs_data),key->ofs_data + 0x1000); printf("%04x value type = 0x%0x %s\n", D_OFFS(val_type), key->val_type, (key->val_type <= REG_MAX ? val_types[key->val_type] : "(unknown)") ) ; printf("%04x flag = 0x%0x\n",D_OFFS(flag),key->flag); printf("%04x *unused?* = 0x%0x\n",D_OFFS(dummy1),key->dummy1); printf("%04x Key name: <",D_OFFS(keyname) ); for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]); printf(">\n== End of key info.\n"); } /* Parse the sk datablock * Gee, this is the security info. Who cares? *evil grin* * vofs = offset into struct (after size linkage) */ void parse_sk(struct hive *hdesc, int vofs, int blen) { struct sk_key *key; /* int i; */ printf("== sk at offset %0x\n",vofs); key = (struct sk_key *)(hdesc->buffer + vofs); printf("%04x *unused?* = %d\n" , D_OFFS(dummy1), key->dummy1 ); printf("%04x Offset to prev sk = 0x%0x\n", D_OFFS(ofs_prevsk), key->ofs_prevsk + 0x1000); printf("%04x Offset to next sk = 0x%0x\n", D_OFFS(ofs_nextsk), key->ofs_nextsk + 0x1000); printf("%04x Usage counter = %d (0x%0x)\n", D_OFFS(no_usage), key->no_usage,key->no_usage); printf("%04x Security data len = %d (0x%0x)\n", D_OFFS(len_sk), key->len_sk,key->len_sk); printf("== End of key info.\n"); } /* Parse the lf datablock (>4.0 'nk' offsets lookuptable) * vofs = offset into struct (after size linkage) */ void parse_lf(struct hive *hdesc, int vofs, int blen) { struct lf_key *key; int i; printf("== lf at offset %0x\n",vofs); key = (struct lf_key *)(hdesc->buffer + vofs); printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys ); for(i = 0; i < key->no_keys; i++) { printf("%04x %3d Offset: 0x%0x - <%c%c%c%c>\n", D_OFFS(hash[i].ofs_nk), i, key->hash[i].ofs_nk + 0x1000, key->hash[i].name[0], key->hash[i].name[1], key->hash[i].name[2], key->hash[i].name[3] ); } printf("== End of key info.\n"); } /* Parse the lh datablock (WinXP offsets lookuptable) * vofs = offset into struct (after size linkage) * The hash is most likely a base 37 conversion of the name string */ void parse_lh(struct hive *hdesc, int vofs, int blen) { struct lf_key *key; int i; printf("== lh at offset %0x\n",vofs); key = (struct lf_key *)(hdesc->buffer + vofs); printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys ); for(i = 0; i < key->no_keys; i++) { printf("%04x %3d Offset: 0x%0x - \n", D_OFFS(lh_hash[i].ofs_nk), i, key->lh_hash[i].ofs_nk + 0x1000, key->lh_hash[i].hash ); } printf("== End of key info.\n"); } /* Parse the li datablock (3.x 'nk' offsets list) * vofs = offset into struct (after size linkage) */ void parse_li(struct hive *hdesc, int vofs, int blen) { struct li_key *key; int i; printf("== li at offset %0x\n",vofs); /* #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */ key = (struct li_key *)(hdesc->buffer + vofs); printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys ); for(i = 0; i < key->no_keys; i++) { printf("%04x %3d Offset: 0x%0x\n", D_OFFS(hash[i].ofs_nk), i, key->hash[i].ofs_nk + 0x1000); } printf("== End of key info.\n"); } /* Parse the ri subindex-datablock * (Used to list li/lf/lh's when ~>500keys) * vofs = offset into struct (after size linkage) */ void parse_ri(struct hive *hdesc, int vofs, int blen) { struct ri_key *key; int i; printf("== ri at offset %0x\n",vofs); /* #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */ key = (struct ri_key *)(hdesc->buffer + vofs); printf("%04x number of subindices = %d\n", D_OFFS(no_lis), key->no_lis ); for(i = 0; i < key->no_lis; i++) { printf("%04x %3d Offset: 0x%0x\n", D_OFFS(hash[i].ofs_li), i, key->hash[i].ofs_li + 0x1000); } printf("== End of key info.\n"); } /* Parse the db block (used when value data >4k or something) * vofs = offset into struct (after size linkage) */ void parse_db(struct hive *hdesc, int vofs, int blen) { struct db_key *key; printf("== db at offset %0x\n",vofs); key = (struct db_key *)(hdesc->buffer + vofs); printf("%04x number of parts = %d\n", D_OFFS(no_part), key->no_part ); printf("%04x Data list at offset: 0x%0x\n", D_OFFS(ofs_data), key->ofs_data + 0x1000); printf("== End of key info.\n"); } /* Parse the datablock * vofs = offset into struct (after size linkage) */ int parse_block(struct hive *hdesc, int vofs,int verbose) { unsigned short id; int seglen; seglen = get_int(hdesc->buffer+vofs); // if (vofs > 0xaef000) verbose = 1; #if 0 if (verbose || seglen == 0) { printf("** Block at offset %0x\n",vofs); printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen); } #endif if (seglen == 0) { printf("parse_block: FATAL! Zero data block size! (not registry or corrupt file?)\n"); if (verbose) debugit(hdesc->buffer,hdesc->size); return(0); } if (seglen < 0) { seglen = -seglen; hdesc->usetot += seglen; hdesc->useblk++; if (verbose) { printf("USED BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen); /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ } } else { hdesc->unusetot += seglen; hdesc->unuseblk++; /* Useful to zero blocks we think are empty when debugging.. */ #if ZEROFILLONLOAD bzero(hdesc->buffer+vofs+4,seglen-4); #endif if (verbose) { printf("FREE BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen); /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ } } vofs += 4; id = (*(hdesc->buffer + vofs)<<8) + *(hdesc->buffer+vofs+1); if (verbose > 1) { switch (id) { case 0x6e6b: /* nk */ parse_nk(hdesc, vofs, seglen); break; case 0x766b: /* vk */ parse_vk(hdesc, vofs, seglen); break; case 0x6c66: /* lf */ parse_lf(hdesc, vofs, seglen); break; case 0x6c68: /* lh */ parse_lh(hdesc, vofs, seglen); break; case 0x6c69: /* li */ parse_li(hdesc, vofs, seglen); break; case 0x736b: /* sk */ parse_sk(hdesc, vofs, seglen); break; case 0x7269: /* ri */ parse_ri(hdesc, vofs, seglen); break; case 0x6462: /* db */ parse_db(hdesc, vofs, seglen); break; default: printf("value data, or not handeled yet!\n"); break; } } return(seglen); } /* ================================================================ */ /* Scan and allocation routines */ /* Find start of page given a current pointer into the buffer * hdesc = hive * vofs = offset pointer into buffer * returns: offset to start of page (and page header) */ int find_page_start(struct hive *hdesc, int vofs) { int r,prev; struct hbin_page *h; /* Again, assume start at 0x1000 */ r = 0x1000; while (r < hdesc->size) { prev = r; h = (struct hbin_page *)(hdesc->buffer + r); if (h->id != 0x6E696268) return(0); if (h->ofs_next == 0) { printf("find_page_start: zero len or ofs_next found in page at 0x%x\n",r); return(0); } r += h->ofs_next; if (r > vofs) return (prev); } return(0); } /* Find free space in page * size = requested size in bytes * pofs = offset to start of actual page header * returns: offset to free block, or 0 for error */ #define FB_DEBUG 0 int find_free_blk(struct hive *hdesc, int pofs, int size) { int vofs = pofs + 0x20; int seglen; struct hbin_page *p; p = (struct hbin_page *)(hdesc->buffer + pofs); while (vofs-pofs < (p->ofs_next - HBIN_ENDFILL)) { seglen = get_int(hdesc->buffer+vofs); #if FB_DEBUG if (vofs > 0x400000) { printf("** Block at offset %0x\n",vofs); printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen); } #endif if (seglen == 0) { printf("find_free_blk: FATAL! Zero data block size! (not registry or corrupt file?)\n"); printf(" : Block at offset %0x\n",vofs); if ( (vofs - pofs) == (p->ofs_next - 4) ) { printf("find_free_blk: at exact end of hbin, do not care..\n"); return(0); } if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size); else abort(); return(0); } if (seglen < 0) { seglen = -seglen; #if FB_DEBUG if (vofs >0x400000) printf("USED BLOCK: %d, 0x%0x\n",seglen,seglen); #endif /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ } else { #if FB_DEBUG if (vofs >0x400000) printf("FREE BLOCK!\n"); #endif /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ if (seglen >= size) { #if FB_DEBUG if (vofs >0x400000) printf("find_free_blk: found size %d block at 0x%x\n",seglen,vofs); #endif #if 0 if (vofs == 0x19fb8) { printf("find_free_blk: vofs = %x, seglen = %x\n",vofs,seglen); debugit(hdesc->buffer,hdesc->size); abort(); } #endif return(vofs); } } vofs += seglen; } return(0); } #undef FB_DEBUG /* Search pages from start to find free block * hdesc - hive * size - space requested, in bytes * returns: offset to free block, 0 if not found or error */ int find_free(struct hive *hdesc, int size) { int r,blk; struct hbin_page *h; /* Align to 8 byte boundary */ if (size & 7) size += (8 - (size & 7)); /* Again, assume start at 0x1000 */ r = 0x1000; while (r < hdesc->endofs) { h = (struct hbin_page *)(hdesc->buffer + r); if (h->id != 0x6E696268) return(0); if (h->ofs_next == 0) { printf("find_free: zero len or ofs_next found in page at 0x%x\n",r); return(0); } blk = find_free_blk(hdesc,r,size); if (blk) return (blk); r += h->ofs_next; } return(0); } /* Add new hbin to end of file. If file contains data at end * that is not in a hbin, include that too * hdesc - hive as usual * size - minimum size (will be rounded up to next 0x1000 alignment) * returns offset to first block in new hbin */ #define ADDBIN_DEBUG int add_bin(struct hive *hdesc, int size) { int r,newsize,newbinofs; struct hbin_page *newbin; struct regf_header *hdr; if (hdesc->state & HMODE_NOEXPAND) { fprintf(stderr,"ERROR: Registry hive <%s> need to be expanded,\n" "but that is not allowed according to selected options. Operations will fail.\n", hdesc->filename); return(0); } r = ((size + 0x20 + 4) & ~0xfff) + HBIN_PAGESIZE; /* Add header and link, round up to page boundary, usually 0x1000 */ newbinofs = hdesc->endofs; #ifdef ADDBIN_DEBUG printf("add_bin: request size = %d [%x], rounded to %d [%x]\n",size,size,r,r); printf("add_bin: old buffer size = %d [%x]\n",hdesc->size,hdesc->size); printf("add_bin: firs nonbin off = %d [%x]\n",newbinofs,newbinofs); printf("add_bin: free at end = %d [%x]\n",hdesc->size-newbinofs,hdesc->size-newbinofs); #endif if ( (newbinofs + r) >= hdesc->size) { /* We must allocate more buffer */ newsize = ( (newbinofs + r) & ~(REGF_FILEDIVISOR-1) ) + REGF_FILEDIVISOR; /* File normally multiple of 0x40000 bytes */ #ifdef ADDBIN_DEBUG printf("add_bin: new buffer size = %d [%x]\n",newsize,newsize); #endif hdesc->buffer = realloc(hdesc->buffer, newsize); if (!hdesc->buffer) { perror("add_bin : realloc() "); abort(); } hdesc->size = newsize; } /* At this point, we have large enough space at end of file */ newbin = (struct hbin_page *)(hdesc->buffer + newbinofs); bzero((void *)newbin, r); /* zero out new hbin, easier to debug too */ newbin->id = 0x6E696268; /* 'hbin' */ newbin->ofs_self = newbinofs - 0x1000; /* Point to ourselves minus regf. Seem to be that.. */ newbin->ofs_next = r; /* size of this new bin */ /* Wonder if anything else in the hbin header matters? */ /* Set whole hbin to be one contious unused block */ newbin->firstlink = (r - 0x20 - 0); /* Positive linkage = unused */ /* Update REGF header */ hdr = (struct regf_header *) hdesc->buffer; hdr->filesize = newbinofs + r - 0x1000; /* Point header to new end of data */ #ifdef ADDBIN_DEBUG printf("add_bin: adjusting size field in REGF: %d [%x]\n",hdr->filesize,hdr->filesize); #endif /* Update state */ hdesc->state |= HMODE_DIDEXPAND | HMODE_DIRTY; hdesc->lastbin = newbinofs; /* Last bin */ hdesc->endofs = newbinofs + r; /* New data end */ return(newbinofs + 0x20); } /* Allocate a block of requested size if possible * hdesc - hive * pofs - If >0 will try this page first (ptr may be inside page) * size - number of bytes to allocate * returns: 0 - failed, else pointer to allocated block. * WARNING: Will realloc() buffer if it has to be expanded! * ALL POINTERS TO BUFFER IS INVALID AFTER THAT. (offsets are still correct) * Guess I'd better switch to mmap() one day.. * This function WILL CHANGE THE HIVE (change block linkage) if it * succeeds. */ int alloc_block(struct hive *hdesc, int ofs, int size) { int pofs = 0; int blk = 0; int newbin; int trail, trailsize, oldsz; if (hdesc->state & HMODE_NOALLOC) { printf("\nERROR: alloc_block: Hive <%s> is in no allocation safe mode," "new space not allocated. Operation will fail!\n", hdesc->filename); return(0); } size += 4; /* Add linkage */ if (size & 7) size += (8 - (size & 7)); /* Check current page first */ if (ofs) { pofs = find_page_start(hdesc,ofs); blk = find_free_blk(hdesc,pofs,size); } /* Then check whole hive */ if (!blk) { blk = find_free(hdesc,size); } if (blk) { /* Got the space */ oldsz = get_int(hdesc->buffer+blk); #if 0 printf("Block at : %x\n",blk); printf("Old block size is: %x\n",oldsz); printf("New block size is: %x\n",size); #endif trailsize = oldsz - size; if (trailsize == 4) { trailsize = 0; size += 4; } #if 1 if (trailsize & 7) { /* Trail must be 8 aligned */ trailsize -= (8 - (trailsize & 7)); size += (8 - (trailsize & 7)); } if (trailsize == 4) { trailsize = 0; size += 4; } #endif #if 0 printf("trail after comp: %x\n",trailsize); printf("size after comp: %x\n",size); #endif /* Now change pointers on this to reflect new size */ *(int *)((hdesc->buffer)+blk) = -(size); /* If the fit was exact (unused block was same size as wee need) * there is no need for more, else make free block after end * of newly allocated one */ hdesc->useblk++; hdesc->unuseblk--; hdesc->usetot += size; hdesc->unusetot -= size; if (trailsize) { trail = blk + size; *(int *)((hdesc->buffer)+trail) = (int)trailsize; hdesc->useblk++; /* This will keep blockcount */ hdesc->unuseblk--; hdesc->usetot += 4; /* But account for more linkage bytes */ hdesc->unusetot -= 4; } /* Clear the block data, makes it easier to debug */ #if ZEROFILL bzero( (void *)(hdesc->buffer+blk+4), size-4); #endif hdesc->state |= HMODE_DIRTY; #if 0 printf("alloc_block: returning %x\n",blk); #endif return(blk); } else { printf("alloc_block: failed to alloc %d bytes, trying to expand hive..\n",size); newbin = add_bin(hdesc,size); if (newbin) return(alloc_block(hdesc,newbin,size)); /* Nasty... recall ourselves. */ /* Fallthrough to fail if add_bin fails */ } return(0); } /* Free a block in registry * hdesc - hive * blk - offset of block, MUST POINT TO THE LINKAGE! * returns bytes freed (incl linkage bytes) or 0 if fail * Will CHANGE HIVE IF SUCCESSFUL (changes linkage) */ #define FB_DEBUG 0 int free_block(struct hive *hdesc, int blk) { int pofs,vofs,seglen,prev,next,nextsz,prevsz,size; struct hbin_page *p; if (hdesc->state & HMODE_NOALLOC) { printf("free_block: ERROR: Hive %s is in no allocation safe mode," "space not freed. Operation will fail!\n", hdesc->filename); return(0); } size = get_int(hdesc->buffer+blk); if (size >= 0) { printf("free_block: trying to free already free block!\n"); #ifdef DOCORE printf("blk = %x\n",blk); if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size); abort(); #endif return(0); } size = -size; /* So, we must find start of the block BEFORE us */ pofs = find_page_start(hdesc,blk); if (!pofs) return(0); p = (struct hbin_page *)(hdesc->buffer + pofs); vofs = pofs + 0x20; prevsz = -32; if (vofs != blk) { /* Block is not at start of page? */ while (vofs-pofs < (p->ofs_next - HBIN_ENDFILL) ) { seglen = get_int(hdesc->buffer+vofs); if (seglen == 0) { printf("free_block: EEEK! Zero data block size! (not registry or corrupt file?)\n"); debugit(hdesc->buffer,hdesc->size); return(0); } if (seglen < 0) { seglen = -seglen; /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ } prev = vofs; vofs += seglen; if (vofs == blk) break; } if (vofs != blk) { printf("free_block: ran off end of page!?!? Error in chains?\n"); #ifdef DOCORE printf("vofs = %x, pofs = %x, blk = %x\n",vofs,pofs,blk); if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size); abort(); #endif return(0); } prevsz = get_int(hdesc->buffer+prev); } /* We also need details on next block (unless at end of page) */ next = blk + size; nextsz = 0; if (next-pofs < (p->ofs_next - HBIN_ENDFILL) ) nextsz = get_int(hdesc->buffer+next); #if 0 printf("offset prev : %x , blk: %x , next: %x\n",prev,blk,next); printf("size prev : %x , blk: %x , next: %x\n",prevsz,size,nextsz); #endif /* Now check if next block is free, if so merge it with the one to be freed */ if ( nextsz > 0) { #if 0 printf("Swallow next\n"); #endif size += nextsz; /* Swallow it in current block */ hdesc->useblk--; hdesc->usetot -= 4; hdesc->unusetot -= 4; /* FIXME !??!?? */ } /* Now free the block (possibly with ajusted size as above) */ #if ZEROFILL bzero( (void *)(hdesc->buffer+blk), size); #endif *(int *)((hdesc->buffer)+blk) = (int)size; hdesc->usetot -= size; hdesc->unusetot -= size; /* FIXME !?!? */ hdesc->unuseblk--; hdesc->state |= HMODE_DIRTY; /* Check if previous block is also free, if so, merge.. */ if (prevsz > 0) { #if 0 printf("Swallow prev\n"); #endif hdesc->usetot -= prevsz; hdesc->unusetot += prevsz; prevsz += size; /* And swallow current.. */ #if ZEROFILL bzero( (void *)(hdesc->buffer+prev), prevsz); #endif *(int *)((hdesc->buffer)+prev) = (int)prevsz; hdesc->useblk--; return(prevsz); } return(size); } /* ================================================================ */ /* ** Registry manipulation routines ** */ /* "directory scan", return next name/pointer of a subkey on each call * nkofs = offset to directory to scan * lfofs = pointer to int to hold the current scan position, * set position to 0 to start. * sptr = pointer to struct to hold a single result * returns: -1 = error. 0 = end of key. 1 = more subkeys to scan * NOTE: caller must free the name-buffer (struct ex_data *name) */ int ex_next_n(struct hive *hdesc, int nkofs, int *count, int *countri, struct ex_data *sptr) { struct nk_key *key, *newnkkey; int newnkofs; struct lf_key *lfkey; struct li_key *likey; struct ri_key *rikey; if (!nkofs) return(-1); key = (struct nk_key *)(hdesc->buffer + nkofs); if (key->id != 0x6b6e) { printf("ex_next error: Not a 'nk' node at 0x%0x\n",nkofs); return(-1); } #define EXNDEBUG 0 lfkey = (struct lf_key *)(hdesc->buffer + key->ofs_lf + 0x1004); rikey = (struct ri_key *)(hdesc->buffer + key->ofs_lf + 0x1004); if (rikey->id == 0x6972) { /* Is it extended 'ri'-block? */ #if EXNDEBUG printf("%d , %d\n",*countri,*count); #endif if (*countri < 0 || *countri >= rikey->no_lis) { /* End of ri's? */ return(0); } /* Get the li of lf-struct that's current based on countri */ likey = (struct li_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ; if (likey->id == 0x696c) { newnkofs = likey->hash[*count].ofs_nk + 0x1000; } else { lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ; newnkofs = lfkey->hash[*count].ofs_nk + 0x1000; } /* Check if current li/lf is exhausted */ #if EXNDEBUG printf("likey->no_keys = %d\n",likey->no_keys); #endif if (*count >= likey->no_keys-1) { /* Last legal entry in li list? */ (*countri)++; /* Bump up ri count so we take next ri entry next time */ (*count) = -1; /* Reset li traverse counter for next round, not used later here */ } } else { /* Plain handler */ if (key->no_subkeys <= 0 || *count >= key->no_subkeys) { return(0); } if (lfkey->id == 0x696c) { /* Is it 3.x 'li' instead? */ likey = (struct li_key *)(hdesc->buffer + key->ofs_lf + 0x1004); newnkofs = likey->hash[*count].ofs_nk + 0x1000; } else { newnkofs = lfkey->hash[*count].ofs_nk + 0x1000; } } sptr->nkoffs = newnkofs; newnkkey = (struct nk_key *)(hdesc->buffer + newnkofs + 4); sptr->nk = newnkkey; if (newnkkey->id != 0x6b6e) { printf("ex_next: ERROR: not 'nk' node at 0x%0x\n",newnkofs); return(-1); } else { if (newnkkey->len_name <= 0) { printf("ex_next: nk at 0x%0x has no name!\n",newnkofs); } else if (newnkkey->type & 0x20) { #if 0 printf("dummy1 %x\n", *((int*)newnkkey->dummy1)); printf("dummy2 %x\n", *((int*)newnkkey->dummy2)); printf("type %x\n", newnkkey->type); printf("timestamp+8 %x\n", *((int*)(newnkkey->timestamp+8))); printf("dummy3+0 %x\n", *((int*)(newnkkey->dummy3+0))); printf("dummy3+4 %x\n", *((int*)(newnkkey->dummy3+4))); printf("dummy3+8 %x\n", *((int*)(newnkkey->dummy3+8))); printf("dummy3+12 %x\n", *((int*)(newnkkey->dummy3+12))); printf("dummy4 %x\n", *((int*)&newnkkey->dummy4)); printf("len %d\n", newnkkey->len_name); printf("len class %d\n", newnkkey->len_classnam); fflush(stdout); #endif sptr->name = mem_str(newnkkey->keyname,newnkkey->len_name); // sptr->name = string_rega2prog(newnkkey->keyname, newnkkey->len_name); } else { sptr->name = string_regw2prog(newnkkey->keyname, newnkkey->len_name); } } /* if */ (*count)++; return(1); /* return( *count <= key->no_subkeys); */ } /* "directory scan" for VALUES, return next name/pointer of a value on each call * nkofs = offset to directory to scan * lfofs = pointer to int to hold the current scan position, * set position to 0 to start. * sptr = pointer to struct to hold a single result * returns: -1 = error. 0 = end of key. 1 = more values to scan * NOTE: caller must free the name-buffer (struct vex_data *name) */ int ex_next_v(struct hive *hdesc, int nkofs, int *count, struct vex_data *sptr) { struct nk_key *key /* , *newnkkey */ ; int vkofs,vlistofs; int *vlistkey; struct vk_key *vkkey; if (!nkofs) return(-1); key = (struct nk_key *)(hdesc->buffer + nkofs); if (key->id != 0x6b6e) { printf("ex_next_v error: Not a 'nk' node at 0x%0x\n",nkofs); return(-1); } if (key->no_values <= 0 || *count >= key->no_values) { return(0); } vlistofs = key->ofs_vallist + 0x1004; vlistkey = (int *)(hdesc->buffer + vlistofs); vkofs = vlistkey[*count] + 0x1004; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); if (vkkey->id != 0x6b76) { printf("ex_next_v: hit non valuekey (vk) node during scan at offs 0x%0x\n",vkofs); return(-1); } /* parse_vk(hdesc, vkofs, 4); */ sptr->vk = vkkey; sptr->vkoffs = vkofs; sptr->name = 0; sptr->size = (vkkey->len_data & 0x7fffffff); if (vkkey->len_name >0) { if (vkkey->flag & 1) { sptr->name = mem_str(vkkey->keyname, vkkey->len_name); // sptr->name = string_rega2prog(vkkey->keyname, vkkey->len_name); } else { sptr->name = string_regw2prog(vkkey->keyname, vkkey->len_name); } } else { sptr->name = str_dup(""); } sptr->type = vkkey->val_type; if (sptr->size) { if (vkkey->val_type == REG_DWORD) { if (vkkey->len_data & 0x80000000) { sptr->val = (int)(vkkey->ofs_data); } } } #if 0 else if (vkkey->len_data == 0x80000000) { /* Data SIZE is 0, high bit set: special inline case, data is DWORD and in TYPE field!! */ /* Used a lot in SAM, and maybe in SECURITY I think */ sptr->val = (int)(vkkey->val_type); sptr->size = 4; sptr->type = REG_DWORD; } else { sptr->val = 0; sptr->size = 0; } #endif (*count)++; return( *count <= key->no_values ); } /* traceback - trace nk's back to root, * building path string as we go. * nkofs = offset to nk-node * path = pointer to pathstring-buffer * maxlen = max length of path-buffer * return: length of path string */ int get_abs_path(struct hive *hdesc, int nkofs, char *path, int maxlen) { /* int newnkofs; */ struct nk_key *key; char tmp[ABSPATHLEN+1]; char *keyname; int len_name; maxlen = (maxlen < ABSPATHLEN ? maxlen : ABSPATHLEN); key = (struct nk_key *)(hdesc->buffer + nkofs); if (key->id != 0x6b6e) { printf("get_abs_path: Not a 'nk' node!\n"); return(0); } if (key->type == KEY_ROOT) { /* We're at the root */ return(strlen(path)); } strncpy(tmp,path,ABSPATHLEN-1); if (key->type & 0x20) keyname = mem_str(key->keyname, key->len_name); // keyname = string_rega2prog(key->keyname, key->len_name); else keyname = string_regw2prog(key->keyname, key->len_name); len_name = strlen(keyname); if ( (strlen(path) + len_name) >= maxlen-6) { free(keyname); snprintf(path,maxlen,"(...)%s",tmp); return(strlen(path)); /* Stop trace when string exhausted */ } *path = '\\'; memcpy(path+1,keyname,len_name); free(keyname); strncpy(path+len_name+1,tmp,maxlen-6-len_name); return(get_abs_path(hdesc, key->ofs_parent+0x1004, path, maxlen)); /* go back one more */ } /* Value index table lookup * hdesc - hive as usual * vlistofs - offset of table * name - value name to look for * returns index into table or -1 if err */ int vlist_find(struct hive *hdesc, int vlistofs, int numval, char *name, int type) { struct vk_key *vkkey; int i,vkofs,len; int32_t *vlistkey; int approx = -1; len = strlen(name); vlistkey = (int32_t *)(hdesc->buffer + vlistofs); // printf("vlist_find: <%s> len = %d\n",name,len); for (i = 0; i < numval; i++) { vkofs = vlistkey[i] + 0x1004; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); if (vkkey->len_name == 0 && *name == '@' && len == 1) { /* @ is alias for nameless value */ return(i); } // printf("vlist_find: matching against: <%s> len = %d\n",vkkey->keyname,vkkey->len_name); if ( (type & TPF_EXACT) && vkkey->len_name != len ) continue; /* Skip if exact match and not exact size */ if ( vkkey->len_name >= len ) { /* Only check for names that are longer or equal than we seek */ if ( !strncmp(name, vkkey->keyname, len) ) { /* Name match */ if (vkkey->len_name == len) return(i); /* Exact match always best, returns */ if (approx == -1) approx = i; /* Else remember first partial match */ } } } return(approx); } /* Recursevely follow 'nk'-nodes based on a path-string, * returning offset of last 'nk' or 'vk' * vofs - offset to start node * path - null-terminated pathname (relative to vofs, \ is separator) * type - type to return TPF_??, see ntreg.h * return: offset to nk or vk (or NULL if not found) */ int trav_path(struct hive *hdesc, int vofs, char *path, int type) { struct nk_key *key, *newnkkey; struct lf_key *lfkey; struct li_key *likey; struct ri_key *rikey; int32_t *vlistkey; int newnkofs, plen, i, lfofs, vlistofs, adjust, r, ricnt, subs; char *buf; char part[ABSPATHLEN+1]; char *partptr; if (!hdesc) return(0); buf = hdesc->buffer; // printf("trav_path: called with vofs = %x, path = <%s>, type = %x\n",vofs, path, type); if (!vofs) vofs = hdesc->rootofs+4; /* No current key given , so start at root */ if ( !(type & TPF_ABS) && *path == '\\' && *(path+1) != '\\') { /* Start from root if path starts with \ */ path++; vofs = hdesc->rootofs+4; } key = (struct nk_key *)(buf + vofs); // printf("check of nk at offset: 0x%0x\n",vofs); if (key->id != 0x6b6e) { printf("trav_path: Error: Not a 'nk' node!\n"); return(0); } if ( !(type & TPF_ABS)) { /* Only traverse path if not absolute literal value name passed */ /* TODO: Need to rethink this.. */ /* Find \ delimiter or end of string, copying to name part buffer as we go, rewriting double \\s */ partptr = part; for(plen = 0; path[plen] && (path[plen] != '\\' || path[plen+1] == '\\'); plen++) { if (path[plen] == '\\' && path[plen+1] == '\\') plen++; /* Skip one if double */ *partptr++ = path[plen]; } *partptr = '\0'; #if 0 printf("Name part: <%s>\n",part); printf("Name path: <%s>\n",path); #endif adjust = (path[plen] == '\\' ) ? 1 : 0; // printf("Checking for <%s> with len %d\n",path,plen); if (!plen) return(vofs-4); /* Path has no lenght - we're there! */ if ( (plen == 1) && (*(path+1) && *path == '.') && !(type & TPF_EXACT)) { /* Handle '.' current dir */ // printf("** handle current\n"); return(trav_path(hdesc,vofs,path+plen+adjust,type)); } if ( !(type & TPF_EXACT) && (plen == 2) && !strncmp("..",path,2) ) { /* Get parent key */ newnkofs = key->ofs_parent + 0x1004; /* Return parent (or only root if at the root) */ return(trav_path(hdesc, (key->type == KEY_ROOT ? vofs : newnkofs), path+plen+adjust, type)); } } /* at last name of path, and we want vk, and the nk has values */ if ((type & TPF_VK_ABS) || (!path[plen] && (type & TPF_VK) && key->no_values) ) { // if ( (!path[plen] && (type & TPF_VK) && key->no_values) ) { if (type & TPF_ABS) { strcpy(part, path); plen = de_escape(part,0); partptr = part + plen; } // printf("VK namematch for <%s>, type = %d\n",part,type); vlistofs = key->ofs_vallist + 0x1004; vlistkey = (int32_t *)(buf + vlistofs); i = vlist_find(hdesc, vlistofs, key->no_values, part, type); if (i != -1) { return(vlistkey[i] + 0x1000); } } if (key->no_subkeys > 0) { /* If it has subkeys, loop through the hash */ char *partw = NULL; int partw_len, part_len; // printf("trav_path: subkey loop: path = %s, part = %s\n",path,part); lfofs = key->ofs_lf + 0x1004; /* lf (hash) record */ lfkey = (struct lf_key *)(buf + lfofs); if (lfkey->id == 0x6972) { /* ri struct need special parsing */ /* Prime loop state */ rikey = (struct ri_key *)lfkey; ricnt = rikey->no_lis; r = 0; likey = (struct li_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ; subs = likey->no_keys; if (likey->id != 0x696c) { /* Bwah, not li anyway, XP uses lh usually which is actually smarter */ lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ; likey = NULL; } } else { if (lfkey->id == 0x696c) { /* li? */ likey = (struct li_key *)(buf + lfofs); } else { likey = NULL; } rikey = NULL; ricnt = 0; r = 0; subs = key->no_subkeys; } partw = string_prog2regw(part, partptr-part, &partw_len); // string_prog2rega(part, partptr-part); part_len = strlen(part); do { for(i = 0; i < subs; i++) { if (likey) newnkofs = likey->hash[i].ofs_nk + 0x1004; else newnkofs = lfkey->hash[i].ofs_nk + 0x1004; newnkkey = (struct nk_key *)(buf + newnkofs); if (newnkkey->id != 0x6b6e) { printf("ERROR: not 'nk' node! (strange?)\n"); } else { if (newnkkey->len_name <= 0) { printf("[No name]\n"); } else if ( ( ( part_len <= newnkkey->len_name ) && !(type & TPF_EXACT) ) || ( ( part_len == newnkkey->len_name ) && (type & TPF_EXACT) ) ) { /* Can't match if name is shorter than we look for */ int cmp; // printf("trav_path: part = <%s>, part_len = %d\n",part,part_len); if (newnkkey->type & 0x20) cmp = strncmp(part,newnkkey->keyname,part_len); else cmp = memcmp(partw, newnkkey->keyname, partw_len); if (!cmp) { // printf("Key at 0x%0x matches! recursing! new path = %s\n",newnkofs,path+plen+adjust); free(partw); return(trav_path(hdesc, newnkofs, path+plen+adjust, type)); } } } /* if id OK */ } /* hash loop */ r++; if (ricnt && r < ricnt) { newnkofs = rikey->hash[r].ofs_li; likey = (struct li_key *)( hdesc->buffer + newnkofs + 0x1004 ) ; subs = likey->no_keys; if (likey->id != 0x696c) { /* Bwah, not li anyway, XP uses lh usually which is actually smarter */ lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ; likey = NULL; } } } while (r < ricnt && ricnt); free(partw); } /* if subkeys */ /* Not found */ return(0); } /* ls - list a 'nk' nodes subkeys and values * vofs - offset to start of data (skipping block linkage) * type - 0 = full, 1 = keys only. 2 = values only */ void nk_ls(struct hive *hdesc, char *path, int vofs, int type) { struct nk_key *key; int nkofs; struct ex_data ex; struct vex_data vex; int count = 0, countri = 0; nkofs = trav_path(hdesc, vofs, path, 0); if(!nkofs) { printf("nk_ls: Key <%s> not found\n",path); return; } nkofs += 4; key = (struct nk_key *)(hdesc->buffer + nkofs); VERBF(hdesc,"ls of node at offset 0x%0x\n",nkofs); if (key->id != 0x6b6e) { printf("Error: Not a 'nk' node at offset %x!\n",nkofs); if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size); } printf("Node has %d subkeys and %d values",key->no_subkeys,key->no_values); if (key->len_classnam) printf(", and class-data of %d bytes",key->len_classnam); printf("\n"); if (key->no_subkeys) { printf(" key name\n"); while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) { if (!(hdesc->state & HMODE_VERBOSE)) printf("%c <%s>\n", (ex.nk->len_classnam)?'*':' ',ex.name); else printf("[%6x] %c <%s>\n", ex.nkoffs, (ex.nk->len_classnam)?'*':' ',ex.name); FREE(ex.name); } } count = 0; if (key->no_values) { printf(" size type value name [value if type DWORD]\n"); while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0)) { if (hdesc->state & HMODE_VERBOSE) printf("[%6x] %6d %x %-16s <%s>", vex.vkoffs - 4, vex.size, vex.type, (vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name); else printf("%6d %x %-16s <%s>", vex.size, vex.type, (vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name); if (vex.type == REG_DWORD) printf(" %*d [0x%x]",25-(int)strlen(vex.name),vex.val , vex.val); printf("\n"); FREE(vex.name); } } } /* Get the type of a value */ int get_val_type(struct hive *hdesc, int vofs, char *path, int exact) { struct vk_key *vkkey; int vkofs; vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK); if (!vkofs) { return -1; } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); return(vkkey->val_type); } /* Set/change the type of a value. Strangely we need this in some situation in SAM * and delete + add value is a bit overkill */ int set_val_type(struct hive *hdesc, int vofs, char *path, int exact, int type) { struct vk_key *vkkey; int vkofs; vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK); if (!vkofs) { return -1; } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); vkkey->val_type = type; return(vkkey->val_type); } /* Get len of a value, given current key + path */ int get_val_len(struct hive *hdesc, int vofs, char *path, int exact) { struct vk_key *vkkey; int vkofs; int len; vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK); if (!vkofs) { return -1; } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); len = vkkey->len_data & 0x7fffffff; if ( vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) { /* Special inline case, return size of 4 (dword) */ len = 4; } return(len); } /* Get void-pointer to value-data, also if inline. * If val_type != 0 a check for correct value type is done * Caller must keep track of value's length (call function above to get it) */ void *get_val_data(struct hive *hdesc, int vofs, char *path, int val_type, int exact) { struct vk_key *vkkey; int vkofs; // printf("get_val_data: path = %s\n",path); vkofs = trav_path(hdesc,vofs,path,exact | TPF_VK); if (!vkofs) { printf("get_val_data: %s not found\n",path); abort(); return NULL; } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); if (vkkey->len_data == 0) { return NULL; } if (vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) { /* Special inline case (len = 0x80000000) */ return(&vkkey->val_type); /* Data (4 bytes?) in type field */ } if (val_type && vkkey->val_type && (vkkey->val_type) != val_type) { printf("Value <%s> is not of correct type!\n",path); #if DOCORE abort(); #endif return NULL; } /* Negative len is inline, return ptr to offset-field which in * this case contains the data itself */ if (vkkey->len_data & 0x80000000) return(&vkkey->ofs_data); /* Normal return, return data pointer */ return(hdesc->buffer + vkkey->ofs_data + 0x1004); } /* Get and copy key data (if any) to buffer * if kv==NULL will allocate needed return struct & buffer * else will use buffer allocated for it (if it fits) * return len+data or NULL if not found (or other error) * NOTE: caller must deallocate buffer! a simple free(keyval) will suffice. */ struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv, int vofs, char *path, int type, int exact ) { int l,i,parts,list,blockofs,blocksize,point,copylen,restlen; struct keyval *kr; void *keydataptr; struct db_key *db; void *addr; l = get_val_len(hdesc, vofs, path, exact); if (l == -1) return(NULL); /* error */ if (kv && (kv->len < l)) return(NULL); /* Check for overflow of supplied buffer */ keydataptr = get_val_data(hdesc, vofs, path, type, exact); // if (!keydataptr) return(NULL); /* Allocate space for data + header, or use supplied buffer */ if (kv) { kr = kv; } else { ALLOC(kr,1,l*sizeof(int)+4); } kr->len = l; // printf("get_val2buf: keydataprtr = %x, l = %x\n",keydataptr,l); if (l > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */ db = (struct db_key *)keydataptr; if (db->id != 0x6264) abort(); parts = db->no_part; list = db->ofs_data + 0x1004; printf("get_val2buf: Long value: parts = %d, list = %x\n",parts,list); point = 0; restlen = l; for (i = 0; i < parts; i++) { blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000; blocksize = -get_int(hdesc->buffer + blockofs) - 8; /* Copy this part, up to size of block or rest lenght in last block */ copylen = (blocksize > restlen) ? restlen : blocksize; printf("get_val2buf: Datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize); printf(" : Point = %x, restlen = %x, copylen = %x\n",point,restlen,copylen); addr = (void *)&(kr->data) + point; memcpy( addr, hdesc->buffer + blockofs + 4, copylen); // debugit((char *)&(kr->data), l); point += copylen; restlen -= copylen; } } else { if (l && kr && keydataptr) memcpy(&(kr->data), keydataptr, l); } return(kr); } /* DWORDs are so common that I make a small function to get it easily */ int get_dword(struct hive *hdesc, int vofs, char *path, int exact) { struct keyval *v; int dword; v = get_val2buf(hdesc, NULL, vofs, path, REG_DWORD, exact | TPF_VK); if (!v) return(-1); /* well... -1 COULD BE THE STORED VALUE TOO */ dword = (int)v->data; FREE(v); return(dword); } /* Sanity checker when transferring data into a block * ofs = offset to data block, point to start of actual datablock linkage * data = data to copy * size = size of data to copy */ int fill_block(struct hive *hdesc, int ofs, void *data, int size) { int blksize; blksize = get_int(hdesc->buffer + ofs); blksize = -blksize; #if 0 printf("fill_block: ofs = %x - %x, size = %x, blksize = %x\n",ofs,ofs+size,size,blksize); #endif /* if (blksize < size || ( (ofs & 0xfffff000) != ((ofs+size) & 0xfffff000) )) { */ if (blksize < size) { printf("fill_block: ERROR: block to small for data: ofs = %x, size = %x, blksize = %x\n",ofs,size,blksize); if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size); abort(); } memcpy(hdesc->buffer + ofs + 4, data, size); return(0); } /* Free actual data of a value, and update value descriptor * hdesc - hive * vofs - current value offset */ int free_val_data(struct hive *hdesc, int vkofs) { struct vk_key *vkkey; struct db_key *db; int len,i,blockofs,blocksize,parts,list; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); len = vkkey->len_data; if (!(len & 0x80000000)) { /* Check for inline, if so, skip it, nothing to do */ if (len > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */ db = (struct db_key *)(hdesc->buffer + vkkey->ofs_data + 0x1004); if (db->id != 0x6264) abort(); parts = db->no_part; list = db->ofs_data + 0x1004; printf("free_val_data: Long value: parts = %d, list = %x\n",parts,list); for (i = 0; i < parts; i++) { blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000; blocksize = -get_int(hdesc->buffer + blockofs); printf("free_val_data: Freeing long datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize); free_block(hdesc, blockofs); } printf("free_val_data: Freeing indirect list at %x\n", list-4); free_block(hdesc, list - 4); printf("free_val_data: Freeing db structure at %x\n", vkkey->ofs_data + 0x1000); } /* Fall through to regular which deallocs data or db block ofs_data point to */ if (len) free_block(hdesc, vkkey->ofs_data + 0x1000); } /* inline check */ vkkey->len_data = 0; vkkey->ofs_data = 0; return(vkofs); } /* Allocate data for value. Frees old data (if any) which will be destroyed * hdesc - hive * vofs - current key * path - path to value * size - size of data * Returns: 0 - error, >0 pointer to actual dataspace */ int alloc_val_data(struct hive *hdesc, int vofs, char *path, int size,int exact) { struct vk_key *vkkey; struct db_key *db; int vkofs,dbofs,listofs,blockofs,blocksize,parts; int datablk,i; int *ptr; vkofs = trav_path(hdesc,vofs,path,exact); if (!vkofs) { return (0); } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); free_val_data(hdesc, vkofs); /* Get rid of old data if any */ /* Allocate space for new data */ if (size > 4) { if (size > VAL_DIRECT_LIMIT) { /* We must allocate indirect stuff *sigh* */ parts = size / VAL_DIRECT_LIMIT + 1; printf("alloc_val_data: doing large key: size = %x (%d), parts = %d\n",size,size,parts); dbofs = alloc_block(hdesc, vkofs, sizeof(struct db_key)); /* Alloc db structure */ db = (struct db_key *)(hdesc->buffer + dbofs + 4); db->id = 0x6264; db->no_part = parts; listofs = alloc_block(hdesc, vkofs, 4 * parts); /* block offset list */ db = (struct db_key *)(hdesc->buffer + dbofs + 4); db->ofs_data = listofs - 0x1000; printf("alloc_val_data: dbofs = %x, listofs = %x\n",dbofs,listofs); for (i = 0; i < parts; i++) { blocksize = VAL_DIRECT_LIMIT; /* Windows seem to alway allocate the whole block */ blockofs = alloc_block(hdesc, vkofs, blocksize); printf("alloc_val_data: block # %d, blockofs = %x\n",i,blockofs); ptr = (int *)(hdesc->buffer + listofs + 4 + (i << 2)); *ptr = blockofs - 0x1000; } datablk = dbofs; } else { /* Regular size < 16 k direct alloc */ datablk = alloc_block(hdesc, vkofs, size); } } else { /* 4 bytes or less are inlined */ datablk = vkofs + (int32_t)&(vkkey->ofs_data) - (int32_t)vkkey; size |= 0x80000000; } if (!datablk) return(0); vkkey = (struct vk_key *)(hdesc->buffer + vkofs); /* alloc_block may move pointer, realloc() buf */ // printf("alloc_val_data: datablk = %x, size = %x, vkkey->len_data = %x\n",datablk, size, vkkey->len_data); /* Link in new datablock */ if ( !(size & 0x80000000)) vkkey->ofs_data = datablk - 0x1000; vkkey->len_data = size; return(datablk + 4); } /* Add a value to a key. * Just add the metadata (empty value), to put data into it, use * put_buf2val afterwards * hdesc - hive * nkofs - current key * name - name of value * type - type of value * returns: 0 err, >0 offset to value metadata */ struct vk_key *add_value(struct hive *hdesc, int nkofs, char *name, int type) { struct nk_key *nk; int oldvlist = 0, newvlist, newvkofs; struct vk_key *newvkkey; char *blank=""; if (!name || !*name) return(NULL); nk = (struct nk_key *)(hdesc->buffer + nkofs); if (nk->id != 0x6b6e) { printf("add_value: Key pointer not to 'nk' node!\n"); return(NULL); } if (vlist_find(hdesc, nk->ofs_vallist + 0x1004, nk->no_values, name, TPF_EXACT) != -1) { printf("add_value: value %s already exists\n",name); return(NULL); } if (!strcmp(name,"@")) name = blank; if (nk->no_values) oldvlist = nk->ofs_vallist; newvlist = alloc_block(hdesc, nkofs, nk->no_values * 4 + 4); if (!newvlist) { printf("add_value: failed to allocate new value list!\n"); return(NULL); } nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved.. */ if (oldvlist) { /* Copy old data if any */ memcpy(hdesc->buffer + newvlist + 4, hdesc->buffer + oldvlist + 0x1004, nk->no_values * 4 + 4); } /* Allocate value descriptor including its name */ newvkofs = alloc_block(hdesc, newvlist, sizeof(struct vk_key) + strlen(name)); if (!newvkofs) { printf("add_value: failed to allocate value descriptor\n"); free_block(hdesc, newvlist); return(NULL); } nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved.. */ /* Success, now fill in the metadata */ newvkkey = (struct vk_key *)(hdesc->buffer + newvkofs + 4); /* Add pointer in value list */ *(int *)(hdesc->buffer + newvlist + 4 + (nk->no_values * 4)) = newvkofs - 0x1000; /* Fill in vk struct */ newvkkey->id = 0x6b76; newvkkey->len_name = strlen(name); if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) { newvkkey->len_data = 0x80000004; /* Prime the DWORD inline stuff */ } else { newvkkey->len_data = 0x80000000; /* Default inline zero size */ } newvkkey->ofs_data = 0; newvkkey->val_type = type; newvkkey->flag = newvkkey->len_name ? 1 : 0; /* Seems to be 1, but 0 for no name default value */ newvkkey->dummy1 = 0; memcpy((char *)&newvkkey->keyname, name, newvkkey->len_name); /* And copy name */ /* Finally update the key and free the old valuelist */ nk->no_values++; nk->ofs_vallist = newvlist - 0x1000; if (oldvlist) free_block(hdesc,oldvlist + 0x1000); return(newvkkey); } /* Remove a vk-struct incl dataspace if any * Mostly for use by higher level stuff * hdesc - hive * vkofs - offset to vk */ void del_vk(struct hive *hdesc, int vkofs) { struct vk_key *vk; vk = (struct vk_key *)(hdesc->buffer + vkofs); if (vk->id != 0x6b76) { printf("del_vk: Key pointer not to 'vk' node!\n"); return; } if ( !(vk->len_data & 0x80000000) && vk->ofs_data) { free_val_data(hdesc, vkofs); } free_block(hdesc, vkofs - 4); } /* Delete all values from key (used in recursive delete) * hdesc - yer usual hive * nkofs - current keyoffset */ void del_allvalues(struct hive *hdesc, int nkofs) { int vlistofs, o, vkofs; int32_t *vlistkey; struct nk_key *nk; nk = (struct nk_key *)(hdesc->buffer + nkofs); if (nk->id != 0x6b6e) { printf("del_allvalues: Key pointer not to 'nk' node!\n"); return; } if (!nk->no_values) { /* printf("del_avalues: Key has no values!\n"); */ return; } vlistofs = nk->ofs_vallist + 0x1004; vlistkey = (int32_t *)(hdesc->buffer + vlistofs); /* Loop through index and delete all vk's */ for (o = 0; o < nk->no_values; o++) { vkofs = vlistkey[o] + 0x1004; del_vk(hdesc, vkofs); } /* Then zap the index, and update nk */ free_block(hdesc, vlistofs-4); nk->ofs_vallist = -1; nk->no_values = 0; } /* Delete single value from key * hdesc - yer usual hive * nkofs - current keyoffset * name - name of value to delete * exact - NKF_EXACT to do exact match, else first match * returns: 0 - ok, 1 - failed */ int del_value(struct hive *hdesc, int nkofs, char *name, int exact) { int vlistofs, slot, o, n, vkofs, newlistofs; int32_t *vlistkey, *tmplist, *newlistkey; struct nk_key *nk; char *blank=""; if (!name || !*name) return(1); if (!strcmp(name,"@")) name = blank; nk = (struct nk_key *)(hdesc->buffer + nkofs); if (nk->id != 0x6b6e) { printf("del_value: Key pointer not to 'nk' node!\n"); return(1); } if (!nk->no_values) { printf("del_value: Key has no values!\n"); return(1); } vlistofs = nk->ofs_vallist + 0x1004; vlistkey = (int32_t *)(hdesc->buffer + vlistofs); slot = vlist_find(hdesc, vlistofs, nk->no_values, name, TPF_VK); if (slot == -1) { printf("del_value: value %s not found!\n",name); return(1); } /* Delete vk and data */ vkofs = vlistkey[slot] + 0x1004; del_vk(hdesc, vkofs); /* Copy out old index list */ CREATE(tmplist,int32_t,nk->no_values); memcpy(tmplist, vlistkey, nk->no_values * sizeof(int32_t)); free_block(hdesc,vlistofs-4); /* Get rid of old list */ nk->no_values--; if (nk->no_values) { newlistofs = alloc_block(hdesc, vlistofs, nk->no_values * sizeof(int32_t)); if (!newlistofs) { printf("del_value: FATAL: Was not able to alloc new index list\n"); abort(); } nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved */ /* Now copy over, omitting deleted entry */ newlistkey = (int32_t *)(hdesc->buffer + newlistofs + 4); for (n = 0, o = 0; n < nk->no_values; o++, n++) { if (o == slot) o++; newlistkey[n] = tmplist[o]; } nk->ofs_vallist = newlistofs - 0x1000; } else { nk->ofs_vallist = -1; } return(0); } /* Add a subkey to a key * hdesc - usual.. * nkofs - offset of current nk * name - name of key to add * return: ptr to new keystruct, or NULL */ #undef AKDEBUG struct nk_key *add_key(struct hive *hdesc, int nkofs, char *name) { int slot, newlfofs = 0, oldlfofs = 0, newliofs = 0; int oldliofs = 0; int o, n, i, onkofs, newnkofs, cmp; int rimax, rislot, riofs, namlen; struct ri_key *ri = NULL; struct lf_key *newlf = NULL, *oldlf; struct li_key *newli = NULL, *oldli; struct nk_key *key, *newnk, *onk; int32_t hash; key = (struct nk_key *)(hdesc->buffer + nkofs); if (key->id != 0x6b6e) { printf("add_key: current ptr not 'nk'\n"); return(NULL); } namlen = strlen(name); slot = -1; if (key->no_subkeys) { /* It already has subkeys */ oldlfofs = key->ofs_lf; oldliofs = key->ofs_lf; oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); if (oldlf->id != 0x666c && oldlf->id != 0x686c && oldlf->id != 0x696c && oldlf->id != 0x6972) { printf("add_key: index type not supported: 0x%04x\n",oldlf->id); return(NULL); } rimax = 0; ri = NULL; riofs = 0; rislot = -1; if (oldlf->id == 0x6972) { /* Indirect index 'ri', init loop */ riofs = key->ofs_lf; ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004); rimax = ri->no_lis-1; #ifdef AKDEBUG printf("add_key: entering 'ri' traverse, rimax = %d\n",rimax); #endif oldliofs = ri->hash[rislot+1].ofs_li; oldlfofs = ri->hash[rislot+1].ofs_li; } do { /* 'ri' loop, at least run once if no 'ri' deep index */ if (ri) { /* Do next 'ri' slot */ rislot++; oldliofs = ri->hash[rislot].ofs_li; oldlfofs = ri->hash[rislot].ofs_li; oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); } oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); #ifdef AKDEBUG printf("add_key: top of ri-loop: rislot = %d, rimax = %d\n",rislot,rimax); #endif slot = -1; if (oldli->id == 0x696c) { /* li */ #ifdef AKDEBUG printf("add_key: li slot allocate\n"); #endif FREE(newli); ALLOC(newli, 8 + 4*oldli->no_keys + 4, 1); newli->no_keys = oldli->no_keys; newli->id = oldli->id; /* Now copy old, checking where to insert (alphabetically) */ for (o = 0, n = 0; o < oldli->no_keys; o++,n++) { onkofs = oldli->hash[o].ofs_nk; onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004); if (slot == -1) { #if 1 printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname); #endif cmp = strn_casecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name); if (!cmp) { printf("add_key: key %s already exists!\n",name); FREE(newli); return(NULL); } if ( cmp < 0) { slot = o; rimax = rislot; /* Cause end of 'ri' search, too */ n++; #ifdef AKDEBUG printf("add_key: li-match: slot = %d\n",o); #endif } } newli->hash[n].ofs_nk = oldli->hash[o].ofs_nk; } if (slot == -1) slot = oldli->no_keys; } else { /* lf or lh */ oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); FREE(newlf); ALLOC(newlf, 8 + 8*oldlf->no_keys + 8, 1); newlf->no_keys = oldlf->no_keys; newlf->id = oldlf->id; #ifdef AKDEBUG printf("add_key: new lf/lh no_keys: %d\n",newlf->no_keys); #endif /* Now copy old, checking where to insert (alphabetically) */ for (o = 0, n = 0; o < oldlf->no_keys; o++,n++) { onkofs = oldlf->hash[o].ofs_nk; onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004); if (slot == -1) { #if 0 printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname); #endif cmp = strn_casecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name); if (!cmp) { printf("add_key: key %s already exists!\n",name); FREE(newlf); return(NULL); } if ( cmp < 0 ) { slot = o; rimax = rislot; /* Cause end of 'ri' search, too */ n++; #ifdef AKDEBUG printf("add_key: lf-match: slot = %d\n",o); #endif } } newlf->hash[n].ofs_nk = oldlf->hash[o].ofs_nk; newlf->hash[n].name[0] = oldlf->hash[o].name[0]; newlf->hash[n].name[1] = oldlf->hash[o].name[1]; newlf->hash[n].name[2] = oldlf->hash[o].name[2]; newlf->hash[n].name[3] = oldlf->hash[o].name[3]; } if (slot == -1) slot = oldlf->no_keys; } /* li else check */ } while ( (rislot < rimax) && (rimax > 0)); /* 'ri' wrapper loop */ } else { /* Parent was empty, make new index block */ #ifdef AKDEBUG printf("add_key: new index!\n"); #endif ALLOC(newlf, 8 + 8, 1); newlf->no_keys = 0 ; /* Will increment to 1 when filling in the offset later */ /* Use ID (lf, lh or li) we fetched from root node, so we use same as rest of hive */ newlf->id = hdesc->nkindextype; slot = 0; } /* if has keys before */ /* Make and fill in new nk */ newnkofs = alloc_block(hdesc, nkofs, sizeof(struct nk_key) + strlen(name)); if (!newnkofs) { printf("add_key: unable to allocate space for new key descriptor for %s!\n",name); FREE(newlf); FREE(newli); return(NULL); } key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */ newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4); newnk->id = 0x6b6e; newnk->type = KEY_NORMAL; /* Some versions use 0x1020 a lot.. */ newnk->ofs_parent = nkofs - 0x1004; newnk->no_subkeys = 0; newnk->ofs_lf = -1; newnk->no_values = 0; newnk->ofs_vallist = -1; newnk->ofs_sk = key->ofs_sk; /* Get parents for now. 0 or -1 here crashes XP */ newnk->ofs_classnam = -1; newnk->len_name = strlen(name); newnk->len_classnam = 0; memcpy(newnk->keyname, name, newnk->len_name); if (newli) { /* Handle li */ #if AKDEBUG printf("add_key: li fill at slot: %d\n",slot); #endif /* And put its offset into parents index list */ newli->hash[slot].ofs_nk = newnkofs - 0x1000; newli->no_keys++; /* Allocate space for our new li list and copy it into reg */ newliofs = alloc_block(hdesc, nkofs, 8 + 4*newli->no_keys); if (!newliofs) { printf("add_key: unable to allocate space for new index table for %s!\n",name); FREE(newli); free_block(hdesc,newnkofs); return(NULL); } key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */ newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4); /* memcpy(hdesc->buffer + newliofs + 4, newli, 8 + 4*newli->no_keys); */ fill_block(hdesc, newliofs, newli, 8 + 4*newli->no_keys); } else { /* lh or lf */ #ifdef AKDEBUG printf("add_key: lf/lh fill at slot: %d, rislot: %d\n",slot,rislot); #endif /* And put its offset into parents index list */ newlf->hash[slot].ofs_nk = newnkofs - 0x1000; newlf->no_keys++; if (newlf->id == 0x666c) { /* lf hash */ newlf->hash[slot].name[0] = 0; newlf->hash[slot].name[1] = 0; newlf->hash[slot].name[2] = 0; newlf->hash[slot].name[3] = 0; strncpy(newlf->hash[slot].name, name, 4); } else if (newlf->id == 0x686c) { /* lh. XP uses this. hashes whole name */ for (i = 0,hash = 0; i < strlen(name); i++) { hash *= 37; hash += reg_touppertable[(unsigned char)name[i]]; } newlf->lh_hash[slot].hash = hash; } /* Allocate space for our new lf list and copy it into reg */ newlfofs = alloc_block(hdesc, nkofs, 8 + 8*newlf->no_keys); if (!newlfofs) { printf("add_key: unable to allocate space for new index table for %s!\n",name); FREE(newlf); free_block(hdesc,newnkofs); return(NULL); } key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */ newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4); /* memcpy(hdesc->buffer + newlfofs + 4, newlf, 8 + 8*newlf->no_keys); */ fill_block(hdesc, newlfofs, newlf, 8 + 8*newlf->no_keys); } /* li else */ /* Update parent, and free old lf list */ key->no_subkeys++; if (ri) { /* ri index */ ri->hash[rislot].ofs_li = (newlf ? newlfofs : newliofs) - 0x1000; } else { /* Parent key */ key->ofs_lf = (newlf ? newlfofs : newliofs) - 0x1000; } if (newlf && oldlfofs) free_block(hdesc,oldlfofs + 0x1000); if (newli && oldliofs) free_block(hdesc,oldliofs + 0x1000); FREE(newlf); FREE(newli); return(newnk); } /* Delete a subkey from a key * hdesc - usual.. * nkofs - offset of current nk * name - name of key to delete (must match exactly, also case) * return: 1 - err, 0 - ok */ #undef DKDEBUG int del_key(struct hive *hdesc, int nkofs, char *name) { int slot = 0, newlfofs = 0, oldlfofs = 0, o, n, onkofs, delnkofs; int oldliofs = 0, no_keys = 0, newriofs = 0; int namlen; int rimax, riofs, rislot; struct ri_key *ri, *newri = NULL; struct lf_key *newlf = NULL, *oldlf = NULL; struct li_key *newli = NULL, *oldli = NULL; struct nk_key *key, *onk, *delnk; char fullpath[501]; key = (struct nk_key *)(hdesc->buffer + nkofs); namlen = strlen(name); #ifdef DKDEBUG printf("del_key: deleting: <%s>\n",name); #endif if (key->id != 0x6b6e) { printf("del_key: current ptr not nk\n"); return(1); } slot = -1; if (!key->no_subkeys) { printf("del_key: key has no subkeys!\n"); return(1); } oldlfofs = key->ofs_lf; oldliofs = key->ofs_lf; oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); if (oldlf->id != 0x666c && oldlf->id != 0x686c && oldlf->id != 0x696c && oldlf->id != 0x6972) { printf("del_key: index other than 'lf', 'li' or 'lh' not supported yet. 0x%04x\n",oldlf->id); return(1); } rimax = 0; ri = NULL; riofs = 0; rislot = 0; if (oldlf->id == 0x6972) { /* Indirect index 'ri', init loop */ riofs = key->ofs_lf; ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004); rimax = ri->no_lis-1; #ifdef DKDEBUG printf("del_key: entering 'ri' traverse, rimax = %d\n",rimax); #endif rislot = -1; /* Starts at slot 0 below */ } do { /* 'ri' loop, at least run once if no 'ri' deep index */ if (ri) { /* Do next 'ri' slot */ rislot++; oldliofs = ri->hash[rislot].ofs_li; oldlfofs = ri->hash[rislot].ofs_li; } oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); #ifdef DKDEBUG printf("del_key: top of ri-loop: rislot = %d\n",rislot); #endif slot = -1; if (oldlf->id == 0x696c) { /* 'li' handler */ #ifdef DKDEBUG printf("del_key: li handler\n"); #endif FREE(newli); ALLOC(newli, 8 + 4*oldli->no_keys - 4, 1); newli->no_keys = oldli->no_keys - 1; no_keys = newli->no_keys; newli->id = oldli->id; /* Now copy old, checking where to delete */ for (o = 0, n = 0; o < oldli->no_keys; o++,n++) { onkofs = oldli->hash[o].ofs_nk; onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004); if (slot == -1 && onk->len_name == namlen && !strncmp(name, onk->keyname, (onk->len_name > namlen) ? onk->len_name : namlen)) { slot = o; delnkofs = onkofs; delnk = onk; rimax = rislot; o++; } newli->hash[n].ofs_nk = oldli->hash[o].ofs_nk; } } else { /* 'lf' or 'lh' are similar */ #ifdef DKDEBUG printf("del_key: lf or lh handler\n"); #endif FREE(newlf); ALLOC(newlf, 8 + 8*oldlf->no_keys - 8, 1); #ifdef DKDEBUG printf("alloc newlf: %x\n",newlf); #endif newlf->no_keys = oldlf->no_keys - 1; no_keys = newlf->no_keys; newlf->id = oldlf->id; /* Now copy old, checking where to delete */ for (o = 0, n = 0; o < oldlf->no_keys; o++,n++) { onkofs = oldlf->hash[o].ofs_nk; onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004); if (slot == -1 && (onk->len_name == namlen) && !strncmp(name, onk->keyname, onk->len_name)) { slot = o; delnkofs = onkofs; delnk = onk; rimax = rislot; o++; } if (n < newlf->no_keys) { /* Only store if not last index in old */ #ifdef DKDEBUG printf("del_key: n = %d, o = %d\n",n,o); #endif newlf->hash[n].ofs_nk = oldlf->hash[o].ofs_nk; newlf->hash[n].name[0] = oldlf->hash[o].name[0]; newlf->hash[n].name[1] = oldlf->hash[o].name[1]; newlf->hash[n].name[2] = oldlf->hash[o].name[2]; newlf->hash[n].name[3] = oldlf->hash[o].name[3]; } } } /* else lh or lf */ } while (rislot < rimax); /* ri traverse loop */ if (slot == -1) { printf("del_key: subkey %s not found!\n",name); FREE(newlf); FREE(newli); return(1); } #ifdef DKDEBUG printf("del_key: key found at slot %d\n",slot); #endif if (delnk->no_values || delnk->no_subkeys) { printf("del_key: subkey %s has subkeys or values. Not deleted.\n",name); FREE(newlf); FREE(newli); return(1); } /* Allocate space for our new lf list and copy it into reg */ if ( no_keys && (newlf || newli) ) { newlfofs = alloc_block(hdesc, nkofs, 8 + (newlf ? 8 : 4) * no_keys); /* alloc_block may invalidate pointers if hive expanded. Recalculate this one. * Thanks to Jacky To for reporting it here, and suggesting a fix * (better would of course be for me to redesign stuff :) */ if (delnkofs) delnk = (struct nk_key *)(delnkofs + hdesc->buffer + 0x1004); #ifdef DKDEBUG printf("del_key: alloc_block for index returns: %x\n",newlfofs); #endif if (!newlfofs) { printf("del_key: WARNING: unable to allocate space for new key descriptor for %s! Not deleted\n",name); FREE(newlf); return(1); } key = (struct nk_key *)(hdesc->buffer + nkofs); oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); /* memcpy(hdesc->buffer + newlfofs + 4, ((void *)newlf ? (void *)newlf : (void *)newli), 8 + (newlf ? 8 : 4) * no_keys); */ fill_block(hdesc, newlfofs, ((void *)newlf ? (void *)newlf : (void *)newli), 8 + (newlf ? 8 : 4) * no_keys); } else { /* Last deleted, will throw away index */ newlfofs = 0xfff; /* We subtract 0x1000 later */ } if (newlfofs < 0xfff) { printf("del_key: ERROR: newlfofs = %x\n",newlfofs); #if DOCORE if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size); abort(); #endif } /* Check for CLASS data, if so, deallocate it too */ if (delnk->len_classnam) { free_block(hdesc, delnk->ofs_classnam + 0x1000); } /* Now it's safe to zap the nk */ free_block(hdesc, delnkofs + 0x1000); /* And the old index list */ free_block(hdesc, (oldlfofs ? oldlfofs : oldliofs) + 0x1000); /* Update parent */ key->no_subkeys--; key = (struct nk_key *)(hdesc->buffer + nkofs); oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); if (ri) { ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004); /* In case realloc */ if (newlfofs == 0xfff) { *fullpath = 0; get_abs_path(hdesc, nkofs, fullpath, 480); VERBF(hdesc,"del_key: need to delete ri-slot %d for %x - %s\n", rislot,nkofs,fullpath ); if (ri->no_lis > 1) { /* We have subindiceblocks left? */ /* Delete from array */ ALLOC(newri, 8 + 4*ri->no_lis - 4, 1); newri->no_lis = ri->no_lis - 1; newri->id = ri->id; for (o = 0, n = 0; o < ri->no_lis; o++,n++) { if (n == rislot) o++; newri->hash[n].ofs_li = ri->hash[o].ofs_li; } newriofs = alloc_block(hdesc, nkofs, 8 + newri->no_lis*4 ); if (!newriofs) { printf("del_key: WARNING: unable to allocate space for ri-index for %s! Not deleted\n",name); FREE(newlf); FREE(newri); return(1); } key = (struct nk_key *)(hdesc->buffer + nkofs); oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); fill_block(hdesc, newriofs, newri, 8 + newri->no_lis * 4); free_block(hdesc, riofs + 0x1000); key->ofs_lf = newriofs - 0x1000; FREE(newri); } else { /* Last entry in ri was deleted, get rid of it, key is empty */ VERB(hdesc,"del_key: .. and that was the last one. key now empty!\n"); free_block(hdesc, riofs + 0x1000); key->ofs_lf = -1; } } else { ri->hash[rislot].ofs_li = newlfofs - 0x1000; } } else { key->ofs_lf = newlfofs - 0x1000; } FREE(newlf); return(0); } /* Recursive delete keys * hdesc - usual.. * nkofs - offset of current nk * name - name of key to delete * return: 0 - ok, 1 fail */ void rdel_keys(struct hive *hdesc, char *path, int vofs) { struct nk_key *key; int nkofs; struct ex_data ex; int count = 0, countri = 0; if (!path || !*path) return; nkofs = trav_path(hdesc, vofs, path, TPF_NK_EXACT); if(!nkofs) { printf("rdel_keys: Key <%s> not found\n",path); return; } nkofs += 4; key = (struct nk_key *)(hdesc->buffer + nkofs); /* VERBF(hdesc,"rdel of node at offset 0x%0x\n",nkofs); */ if (key->id != 0x6b6e) { printf("rdel_keys: ERROR: Not a 'nk' node!\n"); if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size); return; } #if 0 printf("Node has %d subkeys and %d values\n",key->no_subkeys,key->no_values); #endif if (key->no_subkeys) { while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) { #if 0 printf("%s\n",ex.name); #endif rdel_keys(hdesc, ex.name, nkofs); count = 0; countri = 0; FREE(ex.name); } } del_allvalues(hdesc, nkofs); key = (struct nk_key *)(hdesc->buffer + nkofs); del_key(hdesc, key->ofs_parent+0x1004, path); } /* Get and copy keys CLASS-data (if any) to buffer * Returns a buffer with the data (first int32_t is size). see ntreg.h * NOTE: caller must deallocate buffer! a simple free(keyval) will suffice. */ struct keyval *get_class(struct hive *hdesc, int curnk, char *path) { int clen = 0, dofs = 0, nkofs; struct nk_key *key; struct keyval *data; void *classdata; if (!path && !curnk) return(NULL); nkofs = trav_path(hdesc, curnk, path, 0); if(!nkofs) { printf("get_class: Key <%s> not found\n",path); return(NULL); } nkofs += 4; key = (struct nk_key *)(hdesc->buffer + nkofs); clen = key->len_classnam; if (!clen) { printf("get_class: Key has no class data.\n"); return(NULL); } dofs = key->ofs_classnam; classdata = (void *)(hdesc->buffer + dofs + 0x1004); #if 0 printf("get_class: len_classnam = %d\n",clen); printf("get_class: ofs_classnam = 0x%x\n",dofs); #endif ALLOC(data, sizeof(struct keyval) + clen,1); data->len = clen; memcpy(&data->data, classdata, clen); return(data); } /* Write to registry value. * If same size as existing, copy back in place to avoid changing too much * otherwise allocate new dataspace, then free the old * Thus enough space to hold both new and old data is needed * Pass inn buffer with data len as first DWORD (as routines above) * returns: 0 - error, len - OK (len of data) */ int put_buf2val(struct hive *hdesc, struct keyval *kv, int vofs, char *path, int type, int exact ) { int l; void *keydataptr, *addr; struct db_key *db; int copylen, blockofs, blocksize, restlen, point, i, list, parts; if (!kv) return(0); l = get_val_len(hdesc, vofs, path, exact); if (l == -1) return(0); /* error */ // printf("put_buf2val: l = %d\n",l); // printf("put_buf2val: %s, kv len = %d, l = %d\n",path,kv->len,l); if (kv->len != l) { /* Realloc data block if not same size as existing */ if (!alloc_val_data(hdesc, vofs, path, kv->len, exact)) { printf("put_buf2val: %s : alloc_val_data failed!\n",path); return(0); } } keydataptr = get_val_data(hdesc, vofs, path, type, exact); if (!keydataptr) { printf("put_buf2val: %s : get_val_data failed!\n",path); return(0); /* error */ } if (kv->len > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */ db = (struct db_key *)keydataptr; if (db->id != 0x6264) abort(); parts = db->no_part; list = db->ofs_data + 0x1004; printf("put_buf2val: Long value: parts = %d, list = %x\n",parts,list); point = 0; restlen = kv->len; for (i = 0; i < parts; i++) { blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000; blocksize = -get_int(hdesc->buffer + blockofs) - 8; /* Copy this part, up to size of block or rest lenght in last block */ copylen = (blocksize > restlen) ? restlen : blocksize; printf("put_buf2val: Datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize); printf(" : Point = %x, restlen = %x, copylen = %x\n",point,restlen,copylen); addr = (void *)&(kv->data) + point; fill_block( hdesc, blockofs, addr, copylen); // debugit((char *)&(kr->data), l); point += copylen; restlen -= copylen; } } else { memcpy(keydataptr, &kv->data, kv->len); } hdesc->state |= HMODE_DIRTY; return(kv->len); } /* And, yer basic DWORD write */ int put_dword(struct hive *hdesc, int vofs, char *path, int exact, int dword) { struct keyval *kr; int r; ALLOC(kr,1,sizeof(int)+sizeof(int)); kr->len = sizeof(int); kr->data = dword; r = put_buf2val(hdesc, kr, vofs, path, REG_DWORD, exact); FREE(kr); return(r); } /* ================================================================ */ /* Code to export registry entries to .reg file initiated by * Leo von Klenze * Then expanded a bit to handle more types etc. */ /* * converts a value string from an registry entry into a c string. It does not * use any encoding functions. * It works very primitive by just taking every second char. * The caller must free the resulting string, that was allocated with malloc. * * string: string where every second char is \0 * len: length of the string * return: the converted string as char* */ char * string_regw2prog(void *string, int len) { int i, k; char *cstring; int out_len = 0; for(i = 0; i < len; i += 2) { unsigned v = ((unsigned char *)string)[i] + ((unsigned char *)string)[i+1] * 256u; if (v < 128) out_len += 1; else if(v < 0x800) out_len += 2; else out_len += 3; } CREATE(cstring,char,out_len+1); for(i = 0, k = 0; i < len; i += 2) { unsigned v = ((unsigned char *)string)[i] + ((unsigned char *)string)[i+1] * 256u; if (v < 128) cstring[k++] = v; else if(v < 0x800) { cstring[k++] = 0xc0 | (v >> 6); cstring[k++] = 0x80 | (v & 0x3f); } else { cstring[k++] = 0xe0 | (v >> 12); cstring[k++] = 0x80 | ((v >> 6) & 0x3f); cstring[k++] = 0x80 | (v & 0x3f); } } cstring[out_len] = '\0'; return cstring; } #if 0 // Not used at the moment static char * string_rega2prog(void *string, int len) { int i, k; char *cstring; int out_len = 0; for(i = 0; i < len; ++i) { unsigned v = ((unsigned char *)string)[i]; if (v < 128) out_len += 1; else out_len += 2; } CREATE(cstring,char,out_len+1); for(i = 0, k = 0; i < len; ++i) { unsigned v = ((unsigned char *)string)[i]; if (v < 128) cstring[k++] = v; else { cstring[k++] = 0xc0 | (v >> 6); cstring[k++] = 0x80 | (v & 0x3f); } } cstring[out_len] = '\0'; return cstring; } static void string_prog2rega(char *string, int len) { char *out = string; unsigned char *in = (unsigned char*) string; for (;*in; ++in) { if (!(*in & 0x80)) { *out++ = *in; } else if ((in[0] & 0xe0) == 0xc0 && in[1]) { *out++ = (in[0] & 0x1f) << 6 | (in[1] & 0x3f); ++in; } else if (in[1] && in[2]) { /* assume 3 byte*/ *out++ = (in[1] & 0xf) << 6 | (in[2] & 0x3f); in += 2; } } *out = 0; } #endif // Not used static char * string_prog2regw(void *string, int len, int *out_len) { unsigned char *regw = (unsigned char*) malloc(len*2+2); unsigned char *out = regw; unsigned char *in = (unsigned char*) string; for (;len>0; ++in, --len) { if (!(in[0] & 0x80)) { *out++ = *in; *out++ = 0; } else if ((in[0] & 0xe0) == 0xc0 && len >= 2) { *out++ = (in[0] & 0x1f) << 6 | (in[1] & 0x3f); *out++ = (in[0] & 0x1f) >> 2; ++in, --len; } else if (len >= 3) { /* assume 3 byte*/ *out++ = (in[1] & 0xf) << 6 | (in[2] & 0x3f); *out++ = (in[0] & 0xf) << 4 | ((in[1] & 0x3f) >> 2); in += 2; len -= 2; } } *out_len = out - regw; out[0] = out[1] = 0; return (char *) regw; } static char * quote_string(const char *s) { int len = strlen(s); const char *p; char *dst, *out; for (p = s; *p; ++p) if (*p == '\\' || *p == '\"') ++len; dst = out = (char*) malloc(len + 1); for (p = s; *p; ++p) { if (*p == '\\' || *p == '\"') *dst++ = '\\'; *dst++ = *p; } *dst = 0; return out; } static void export_bin(int type, char *value, int len, int col, FILE* file) { int byte; if (type == REG_BINARY) { fprintf(file, "hex:"); col += 4; } else { fprintf(file, "hex(%x):", type); col += 7; } byte = 0; int start = (col - 2) / 3; while (byte + 1 < len) { /* go byte by byte.. probably slow.. */ fprintf(file, "%02x,", (unsigned char)value[byte]); byte++; if (!((byte + start) % 25)) fprintf(file, "\\\r\n "); } if (len) fprintf(file, "%02x\r\n", (unsigned char)value[len - 1]); } /* * Exports the named subkey and its values to the given file. * * hdesc: registry hive * nkofs: offset of parent node * name: name of key to export * prefix: prefix for each key. This prefix is prepended to the keyname * file: file descriptor of destination file */ void export_subkey(struct hive *hdesc, int nkofs, char *name, char *prefix, FILE *file) { int newofs; int count = 0; int countri = 0; int len; char *path = (char*) malloc(1024); char *value; struct nk_key *key; struct ex_data ex; struct vex_data vex; // switch to key newofs = trav_path(hdesc, nkofs, name, TPF_NK_EXACT); if(!newofs) { printf("export_subkey: Key '%s' not found!\n", name); free(path); return; } nkofs = newofs + 4; // get the key key = (struct nk_key *)(hdesc->buffer + nkofs); printf("Exporting key '%.*s' with %d subkeys and %d values...\n", key->len_name, key->keyname, key->no_subkeys, key->no_values); *path = 0; get_abs_path(hdesc, nkofs, path, 1024); // export the key fprintf(file, "\r\n"); fprintf(file, "[%s\%s]\r\n", prefix, path); // export values if(key->no_values) { while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0)) { int col = 0; char *name = quote_string(vex.name); /* print name */ if (!name[0]) { fprintf(file, "@="); free(name); name = str_dup("@"); col += 2; } else { fprintf(file, "\"%s\"=", name); col += strlen(name) + 3; } if(vex.type == REG_DWORD) { fprintf(file, "dword:%08x\r\n", vex.val); } else if(vex.type == REG_SZ) { char *val, *quoted; value = (char *)get_val_data(hdesc, nkofs, name, vex.type, TPF_VK_EXACT|TPF_ABS); len = get_val_len(hdesc, nkofs, name, TPF_VK_EXACT|TPF_ABS); val = string_regw2prog(value, len); /* dump as binary if invalid characters are embedded */ if (strchr(val, 0xa) || strchr(val, 0xd)) { free(val); //if (len >= 2 && value[len-2] == 0 && value[len-1] == 0) len -= 2; export_bin(vex.type, value, len, col, file); } else { quoted = quote_string(val); free(val); fprintf(file, "\"%s\"", quoted); free(quoted); fprintf(file, "\r\n"); } } else { /* All other types seems to simply be hexdumped. Format is: "valuename"=hex(typenum):xx,xx,xx,xx,xx... for example: "qword64"=hex(b):4e,03,51,db,fa,04,00,00 this is type = 0xb = 11 = REG_QWORD "expandstring"=hex(2):46,00,6f,00,6f,00,62,00,61,00,72,00,20,00,25,00,73,00,20,\ 00,42,00,61,00,7a,00,00,00 this is type 2 = REG_EXPAND_SZ and the line is continued with ,\ don't know how many bytes for each line. Around 18-24 seems to be it?? depends on name lenght?? NOTE: Exception: type = REG_BINARY starts like this: "valuename"=hex:xx,xx,xx... */ value = (char *)get_val_data(hdesc, nkofs, name, vex.type, TPF_VK_EXACT|TPF_ABS); len = get_val_len(hdesc, nkofs, name, TPF_VK_EXACT|TPF_ABS); export_bin(vex.type, value, len, col, file); } FREE(vex.name); free(name); } } // export subkeys if (key->no_subkeys) { count = 0; countri = 0; while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) { export_subkey(hdesc, nkofs, ex.name, prefix, file); FREE(ex.name); } } free(path); } /* * Exports the given key to a windows .reg file that can be imported to the * windows registry. * The prefix is used to determine the destination hive in the windows * registry, set it to HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE or whatever you * want. * * hdesc: hive * nkofs: offset of parent node * name: name of subkey to export * filename: name of destination .reg file (will be overridden) * prefix: prefix for each exported key */ void export_key(struct hive *hdesc, int nkofs, char *name, char *filename, char *prefix) { FILE *file; // open file file = fopen(filename, "w"); if(!file) { printf("export: Cannot open file '%s'. %s (%d).\n", filename, strerror(errno), errno); return; } printf("Exporting to file '%s'...\n", filename); fprintf(file, "Windows Registry Editor Version 5.00\r\n"); export_subkey(hdesc, nkofs, name, prefix, file); fprintf(file,"\r\n"); /* Must end file with an empty line, windows does that */ fclose(file); } /* ================================================================ */ /* Import from .reg file routines */ #define MAXLINE 20000 /* Wide character fgetsw() may not be available on all small libraries.. * so.. roll our own fgets() that handles wide if needed */ char *my_fgets(char *s, char *w, int max, FILE *file, int wide) { int i = 0; char prev = 1; char c = 1; while (c != '\n' && !feof(file) && max) { c = (char)fgetc(file); /* printf("char = %c\n",c); */ if (!c && (!prev && !wide) ) break; /* Stop on 1 (or 2 if wide) null */ prev = c; if (c != '\r') { *(s+i) = c; *(w+i) = c; i++; } max--; } *(s+i) = 0; *(s+i+1) = 0; *(w+i) = 0; *(w+i+1) = 0; if (wide) { /* Convert to C string, de widing it.. */ cheap_uni2ascii(w, s, i); // printf("cheap returns len = %d : %s\n",strlen(s), s); fgetc(file); /* Skip second byte of CR/LF termination */ } // printf("my_fgets returning :\n"); //hexdump(w, 0, i, 1); //printf("====== hexdump end\n"); return(s); } /* Read one line, while skipping blank lines, also checks for = * line = line buffer * file = you know.. * assigner - left part of value assignemt (before =) * value - right part of assignment (after =) * value = NULL if [KEY] line. * assigner = NULL if value continuation line * Returns total lenght of line */ #undef GETLINE_DEBUG int get_line(char s[], char w[], FILE *file, char **assigner, char **value, int wide) { int l,q; char *b; /* Start of line after spaces */ char *c; *assigner = NULL; *value = NULL; do { s[0] = 0; my_fgets(s, w, MAXLINE, file, wide); s[MAXLINE] = 0; b = s; l = strlen(s); #ifdef GETLINE_DEBUG printf("get_line: read line len %d : %s\n",l,s); #endif if (l == 0) { if (feof(file)) return 0; break; } while (isspace(*b)) b++; c = b; while (*c && *c != '\n' && *c != '\r') c++; *c = 0; /* Terminate with null not cr/lf */ if (!*b) continue; /* Blank line */ l = strlen(s); #ifdef GETLINE_DEBUG printf("get_line: stripped line len %d : %s\n",l,s); #endif c = b; if (*b == '[') { /* Key line starts with [ */ #ifdef GETLINE_DEBUG printf("get_line: key line..\n"); #endif while (*c && (*c != ']')) c++; if (!*c) { printf("get_line: WARNING: un-terminated key line..\n"); } *c = 0; *assigner = b+1; *value = NULL; return(l); } q = 0; while (*c) { /* printf(" char = %c : q = %d\n",*c,q); */ if (*c == '"') q ^= 1; /* Flip quote indicator */ if (*c == '=' && !q) { /* Found = outside quotes */ *c = 0; *assigner = b; *value = c + 1; #ifdef GETLINE_DEBUG printf("get_line: value line\n"); #endif return(l); } c++; } /* At this point we don't have a = outside quotes, so probably a value cont line */ *assigner = NULL; *value = b; #ifdef GETLINE_DEBUG printf("get_line: cont line\n"); #endif return(l); } while (!feof(file)); return(l); } /* Wide strlen */ int wide_strlen(char *s) { int l = 0; while ( *(s+l) || *(s+l+1) ) l += 2; return(l); } /* De-quote a string (removing quotes first and last) * Does nothing if no quoutes * String MUST be null-terminated */ char *dequote(char *s) { int l; if (*s != '"') return(s); /* No first quote, then don't change anything, even at end */ l = strlen(s); if ( *(s+l-1) == '"' ) { *(s+l-1) = 0; } return(s+1); } /* de-escape a string, handling \ backslash * s = string buffer, WILL BE CHANGED * wide = true to make it handle wide characters * returns new lenght */ int de_escape(char *s, int wide) { int src = 0; int dst = 0; if (wide) { while ( *(s + src) || *(s + src +1)) { if ( *(s + src) == '\\' && *(s + src + 1) == 0) src += 2; /* Skip over backslash */ *(s + dst) = *(s + src); *(s + dst + 1) = *(s + src + 1); dst += 2; src += 2; } *(s + dst) = 0; *(s + dst + 1) = 0; dst += 2; } else { while ( *(s + src) ) { if ( *(s + src) == '\\' ) src++; *(s + dst) = *(s + src); dst++; src++; } *(s + dst) = 0; dst++; } return(dst); } /* Parse the value to be assigned * s = string to parse * bufptr = pointer to buffer pointer for binary data (will be allocated). * First byte of buffer is value type. * returns lenght * "stringvalue"="this is a string" "binaryvalue"=hex:11,22,33,aa,bb,cc,12,34,01,02,ab,cd,ef,be,ef "dwordvalue"=dword:12345678 "qwordvalue"=hex(b):ef,cd,ab,89,67,45,23,01 "multistringvalue"=hex(7):73,00,74,00,72,00,69,00,6e,00,67,00,20,00,31,00,00,\ 00,73,00,74,00,72,00,69,00,6e,00,67,00,20,00,32,00,00,00,73,00,74,00,72,00,\ 69,00,6e,00,67,00,20,00,33,00,00,00,00,00 "expstringvalue"=hex(2):73,00,74,00,72,00,69,00,6e,00,67,00,20,00,77,00,69,00,\ 74,00,68,00,20,00,25,00,73,00,20,00,65,00,78,00,70,00,61,00,6e,00,73,00,69,\ 00,6f,00,6e,00,00,00 * */ int parse_valuestring(char *s, char *w, int len, int wide, struct keyval **kvptr) { unsigned int dword; int type = -1; int i; int quote; int strstart; uint8_t byte; uint8_t *array; char *widebuf = NULL; struct keyval *kv = NULL; // printf("parse_val: input string: <%s>\n",s); if (!strncmp(s,"dword",4)) { /* DWORD */ sscanf(s,"dword:%x",&dword); // printf("parse_vals: dword is %x\n",dword); type = REG_DWORD; len = 4; ALLOC(kv,1,len + 8); kv->data = dword; } else if (!strncmp(s,"hex",3)) { /* Hex string */ if (!sscanf(s,"hex(%x):",&type)) type = REG_BINARY; // printf("parse_vals: hex type is %d\n",type); while (*s && *s != ':') s++; /* Move up to : */ s++; len = strlen(s); if (len > 0) len = len / 3 + 1; /* 3 characters per byte */ // printf("parse_vals: hex byte count %d\n", len); ALLOC(kv,1,len + 8); array = (uint8_t *)&kv->data; for (i = 0; i < len; i++) { if (!sscanf(s,"%hhx",&byte)) { fprintf(stderr,"parse_values: hex string parse error: %s\n",s); abort(); } // printf("parse_vals: adding byte: %02x\n",byte); *(array+i) = byte; s += 3; } } else { /* Assume it simply is a string */ type = REG_SZ; if (wide) { /* File is wide, find string limits and de-escape it, then copy in as wide */ quote = 0; i = 0; strstart = 0; while ( *(w+i) ) { if ( *(w+i) == '"' && *(w+i+1) == 0) quote ^= 1; if (!quote && !strstart && ( *(w+i) == '=' && *(w+i+1) == 0) ) { strstart = i + 4; /* Skip past start quote */ } i += 2; } if ( *(w+i-2) == '"' && *(w+i-1) == 0) { /* Remove end quoute */ *(w+i-2) = 0; *(w+i-1) = 0; i -= 2; } i += 2; // hexdump(w+strstart,0,i-strstart,1); len = de_escape(w + strstart, wide); // hexdump(w+strstart,0,len,1); // printf("wide string: i = %d, strstart = %d, len = %d\n",i,strstart,len); ALLOC(kv,1,len + 8); memcpy(&kv->data,w + strstart, len); } else { /* File is not wide, so we must widen string before putting into registry */ len = strlen(s); // printf("parse_vals: len %d string <%s>\n",len,s); len = de_escape(s, 0); // printf("parse_vals: after de-escape len %d string <%s>\n",len,s); widebuf = string_prog2regw(s, strlen(s), &len); len += 2; /* Also store the terminating NULLs */ // printf("parse_vals: len after wide expansion: %d\n",len); ALLOC(kv,1,len + 8); memcpy(&kv->data,widebuf,len); FREE(widebuf); } } if (kv) { kv->len = len; *kvptr = kv; } return(type); } /* Main import routine * Only reading into one hive at a time supported. * Will support 8 bit or 16 bit (wide) character .reg file. * Windows regedit.exe usually generates 16 bit. * NOTE: Not a lot of sanity checking, a broken input file may end in strange behaviour or corrupt registry! * hdesc - Loaded hive * filename - (path)name of .reg file to read * prefix - HKEY_ */ void import_reg(struct hive *hdesc, char *filename, char *prefix) { FILE *file; char line[MAXLINE+2]; char wline[MAXLINE+2]; /* Wide buffer */ int l, plen; char *assigner = NULL; char *value = NULL; int wide = 0; int c,wl; // void *valbuf; char *valname = NULL; char *plainname = NULL; char *valstr, *key, *walstr = NULL; int nk, prevnk; /* offsets to nk */ int type,oldtype; struct keyval *valbinbuf = NULL; int numkeys = 0; int numkeyadd = 0; int numtotvals = 0; int numkeyvals = 0; int bailout = 0; plen = strlen(prefix); file = fopen(filename, "r"); if(!file) { fprintf(stderr,"import_reg: Cannot open file '%s'. %s (%d).\n", filename, strerror(errno), errno); return; } c = fgetc(file); if (c == 0xff) { /* Wide characters file */ fprintf(stderr,"import_reg: WARNING: Wide character (16 bit) file..\n" "import_reg: WARNING: Implementation is not 100%% accurate, some things may not import correctly!\n"); c = fgetc(file); /* Get second wide indicator character */ wide = 1; } else { ungetc(c, file); } line[0] = 0; my_fgets(line, wline, MAXLINE, file, wide); line[MAXLINE] = 0; if (strncmp("Windows Registry Editor",line,23)) { fprintf(stderr,"import_reg: ERROR: Windows Registry Editor signature missing on first line\n"); fclose(file); return; } do { l = get_line(line, wline, file, &assigner, &value, wide); if (!feof(file) && !assigner && value) { // printf("import_reg: value continuation line: %s\n",value); l = strlen(value); if ( *(value+l-1) == '\\' ) { /* Strip off cont character */ *(value+l-1) = 0; l--; } value = dequote(value); l = strlen(value); valstr = realloc(valstr, strlen(valstr) + l + 3); if (valstr == NULL) { perror("import_reg: realloc(valstr)"); abort(); } strcat(valstr, value); // printf("import_reg: build value string: %s\n",valstr); if (wide) { wl = wide_strlen(wline); walstr = realloc(walstr, wide_strlen(walstr) + wl + 4); if (walstr == NULL) { perror("import_reg: realloc(valstr)"); abort(); } memcpy(walstr + wl, wline, wl); } } else { /* End continuation, store built up value */ if (valname) { // printf("import_reg: end of value %s, result string: %s\n\n",valname,valstr); type = parse_valuestring(valstr, walstr, l, wide, &valbinbuf); // printf("import_reg: got value type = %d\n",type); // printf("import_reg: data lenght = %d\n",(*valbinbuf).len); VERBF(hdesc," Value <%s> of type %d length %d",valname,type,(*valbinbuf).len); oldtype = get_val_type(hdesc, nk + 4, valname, TPF_VK_ABS|TPF_EXACT); if (oldtype == -1) { // printf("Value <%s> not found, creating it new\n",valname); plainname = str_dup(valname); de_escape(plainname,0); // printf("de-escaped to <%s> creating it new\n",plainname); add_value(hdesc, nk + 4, plainname, type); oldtype = get_val_type(hdesc, nk + 4, valname, TPF_VK_ABS|TPF_EXACT); FREE(plainname); VERB(hdesc," [CREATED]"); } if (oldtype != type) { fprintf(stderr,"ERROR: import_reg: unable to change value <%s>, new type is %d while old is %d\n",valname,type,oldtype); bailout = 1; } else { VERB(hdesc,"\n"); put_buf2val(hdesc, valbinbuf, nk + 4, valname, type, TPF_VK_ABS|TPF_EXACT); numkeyvals++; numtotvals++; FREE(valbinbuf); FREE(valstr); FREE(valname); FREE(walstr); } } } if (assigner && !value) { /* Its a key line */ //printf("import_reg: read key name: %s\n",assigner); if ( !strncmp(assigner,prefix,plen)) { /* Check and strip of prefix of key name */ assigner += plen; } else { fprintf(stderr,"import_reg: WARNING: found key <%s> not matching prefix <%s>\n",assigner,prefix); abort(); } if (numkeys) { if (hdesc->state & HMODE_VERBOSE) printf("--- END of key, with %d values\n",numkeyvals); else printf(" with %d values.\n",numkeyvals); numkeyvals = 0; } printf("--- Import KEY <%s> ",assigner); numkeys++; key = strtok(assigner,"\\"); prevnk = hdesc->rootofs; while (key) { nk = trav_path(hdesc, prevnk + 4, key, TPF_NK_EXACT); if (!nk) { if (!add_key(hdesc, prevnk + 4, key)) { fprintf(stderr,"\nERROR: import_reg: failed to add (sub)key <%s>\n",key); bailout = 1; } else { printf(" [added <%s>] ",key); nk = trav_path(hdesc, prevnk + 4, key, TPF_NK_EXACT); numkeyadd++; } } prevnk = nk; key = strtok(NULL,"\\"); } fflush(stdout); VERB(hdesc,"\n"); } if (assigner && value) { // printf("import_reg: value assignment line: %s = %s\n",assigner,value); valname = str_dup(dequote(assigner)); if (wide) { FREE(walstr); ALLOC(walstr, 1, wide_strlen(wline)); memcpy(walstr, wline, wide_strlen(wline)); } l = strlen(value); if ( *(value+l-1) == '\\' ) { /* Strip off cont character */ *(value+l-1) = 0; l--; } value = dequote(value); valstr = str_dup(value); // valbuf = NULL; //printf("import_reg: val name = %s\n",valname); //printf("import_reg: val str = %s\n",valstr); } } while (!feof(file) && !bailout); printf("\nEND OF IMPORT, file <%s>, operation %s!\n", filename, (bailout ? "FAILED" : "SUCCEEDED")); printf("%d keys\n",numkeys); printf("%d new keys added\n",numkeyadd); printf("%d values total\n\n",numtotvals); fclose(file); if (bailout) hdesc->state &= ~HMODE_DIRTY; /* Don't save if error. Or should we? */ } /* ================================================================ */ /* Hive control (load/save/close) etc */ void closeHive(struct hive *hdesc) { // printf("closing hive %s\n",hdesc->filename); if (hdesc->state & HMODE_OPEN) { close(hdesc->filedesc); } FREE(hdesc->filename); FREE(hdesc->buffer); FREE(hdesc); } /* Compute checksum of REGF header page * hdesc = hive * returns checksum value, 32 bit int */ int32_t calc_regfsum(struct hive *hdesc) { int32_t checksum = 0; struct regf_header *hdr; int i; hdr = (struct regf_header *) hdesc->buffer; for (i = 0; i < 0x1fc/4; ++i) checksum ^= ((int32_t *) hdr)[i]; return(checksum); } /* Write the hive back to disk (only if dirty & not readonly) */ int writeHive(struct hive *hdesc) { int len; struct regf_header *hdr; if (hdesc->state & HMODE_RO) return(0); if ( !(hdesc->state & HMODE_DIRTY)) return(0); if ( !(hdesc->state & HMODE_OPEN)) { /* File has been closed */ if (!(hdesc->filedesc = open(hdesc->filename,O_RDWR))) { fprintf(stderr,"writeHive: open(%s) failed: %s, FILE NOT WRITTEN!\n",hdesc->filename,strerror(errno)); return(1); } hdesc->state |= HMODE_OPEN; } /* Seek back to begginning of file (in case it's already open) */ lseek(hdesc->filedesc, 0, SEEK_SET); /* compute new checksum */ hdr = (struct regf_header *) hdesc->buffer; hdr->checksum = calc_regfsum(hdesc); len = write(hdesc->filedesc, hdesc->buffer, hdesc->size); if (len != hdesc->size) { fprintf(stderr,"writeHive: write of %s failed: %s.\n",hdesc->filename,strerror(errno)); return(1); } hdesc->state &= (~HMODE_DIRTY); return(0); } #undef LOAD_DEBUG struct hive *openHive(char *filename, int mode) { struct hive *hdesc; int fmode,r,vofs; struct stat sbuf; uint32_t pofs; int32_t checksum; char *c; int rt; struct hbin_page *p; struct regf_header *hdr; struct nk_key *nk; struct ri_key *rikey; int verbose = (mode & HMODE_VERBOSE); int trace = (mode & HMODE_TRACE) ? 1 : 0; int info = (mode & HMODE_INFO); if (!filename || !*filename) return(NULL); CREATE(hdesc,struct hive,1); hdesc->filename = str_dup(filename); hdesc->state = 0; hdesc->size = 0; hdesc->buffer = NULL; if ( (mode & HMODE_RO) ) { fmode = O_RDONLY; } else { fmode = O_RDWR; } /* Some non-unix platforms may need this. Thanks to Dan Schmidt */ #ifdef O_BINARY fmode |= O_BINARY; #endif hdesc->filedesc = open(hdesc->filename,fmode); if (hdesc->filedesc < 0) { fprintf(stderr,"openHive(%s) failed: %s, trying read-only\n",hdesc->filename,strerror(errno)); fmode = O_RDONLY; mode |= HMODE_RO; hdesc->filedesc = open(hdesc->filename,fmode); if (hdesc->filedesc < 0) { fprintf(stderr,"openHive(%s) in fallback RO-mode failed: %s\n",hdesc->filename,strerror(errno)); closeHive(hdesc); return(NULL); } } if ( fstat(hdesc->filedesc,&sbuf) ) { perror("stat()"); exit(1); } hdesc->size = sbuf.st_size; hdesc->state = mode | HMODE_OPEN; /* Read the whole file */ ALLOC(hdesc->buffer,1,hdesc->size); rt = 0; do { /* On some platforms read may not block, and read in chunks. handle that */ r = read(hdesc->filedesc, hdesc->buffer + rt, hdesc->size - rt); rt += r; } while ( !errno && (rt < hdesc->size) ); if (errno) { perror("openHive(): read error: "); closeHive(hdesc); return(NULL); } if (rt < hdesc->size) { fprintf(stderr,"Could not read file, got %d bytes while expecting %d\n", r, hdesc->size); closeHive(hdesc); return(NULL); } /* Now run through file, tallying all pages */ /* NOTE/KLUDGE: Assume first page starts at offset 0x1000 */ pofs = 0x1000; hdr = (struct regf_header *)hdesc->buffer; if (hdr->id != 0x66676572) { fprintf(stderr,"openHive(%s): File does not seem to be a registry hive!\n",filename); return(hdesc); } checksum = calc_regfsum(hdesc); #ifdef LOAD_DEBUG printf("openhive: calculated checksum: %08x\n",checksum); printf("openhive: file REGF checksum: %08x\n",hdr->checksum); #endif if (checksum != hdr->checksum) { fprintf(stderr,"openHive(%s): WARNING: REGF header checksum mismatch! calc: 0x%08x != file: 0x%08x\n",filename,checksum,hdr->checksum); } hdesc->rootofs = hdr->ofs_rootkey + 0x1000; if (info) { printf("Hive <%s> name (from header): <",filename); for (c = hdr->name; *c && (c < hdr->name + 64); c += 2) putchar(*c); printf(">\nROOT KEY at offset: 0x%06x * ",hdesc->rootofs); } /* Cache the roots subkey index type (li,lf,lh) so we can use the correct * one when creating the first subkey in a key */ nk = (struct nk_key *)(hdesc->buffer + hdesc->rootofs + 4); if (nk->id == 0x6b6e) { rikey = (struct ri_key *)(hdesc->buffer + nk->ofs_lf + 0x1004); hdesc->nkindextype = rikey->id; if (hdesc->nkindextype == 0x6972) { /* Gee, big root, must check indirectly */ fprintf(stderr,"openHive: DEBUG: BIG ROOT!\n"); rikey = (struct ri_key *)(hdesc->buffer + rikey->hash[0].ofs_li + 0x1004); hdesc->nkindextype = rikey->id; } if (hdesc->nkindextype != 0x666c && hdesc->nkindextype != 0x686c && hdesc->nkindextype != 0x696c) { hdesc->nkindextype = 0x666c; } if (info) printf("Subkey indexing type is: %04x <%c%c>\n", hdesc->nkindextype, hdesc->nkindextype & 0xff, hdesc->nkindextype >> 8); } else { fprintf(stderr,"openHive: WARNING: ROOT key does not seem to be a key! (not type == nk)\n"); } while (pofs < hdr->filesize + 0x1000) { /* Loop through hbins until end according to regf header */ #ifdef LOAD_DEBUG int htrace = 1; // if (htrace) hexdump(hdesc->buffer,pofs,pofs+0x20,1); #endif p = (struct hbin_page *)(hdesc->buffer + pofs); if (p->id != 0x6E696268) { if (info) printf("Page at 0x%x is not 'hbin', assuming file contains garbage at end\n",pofs); break; } hdesc->pages++; if (verbose) printf("###### Page at 0x%0x ofs_self 0x%0x, size (delta ofs_next) 0x%0x ######\n", pofs,p->ofs_self,p->ofs_next); if (p->ofs_next == 0) { fprintf(stderr,"openHive: ERROR: Page at 0x%x has size zero! File may be corrupt, or program has a bug\n",pofs); return(hdesc); } vofs = pofs + 0x20; /* Skip page header, and run through blocks in hbin */ while (vofs-pofs < p->ofs_next && vofs < hdesc->size) { vofs += parse_block(hdesc,vofs,trace); } pofs += p->ofs_next; } /* hbin loop */ hdesc->endofs = hdr->filesize + 0x1000; hdesc->lastbin = pofs - p->ofs_next; /* Compensate for loop that added at end above */ if (verbose) { printf("Last HBIN at offset : 0x%x\n",hdesc->lastbin); printf("First non-HBIN page offset: 0x%x\n",hdesc->endofs); printf("hdr->unknown4 (version?) : 0x%x\n",hdr->unknown4); } if (info || verbose) { printf("File size %d [%x] bytes, containing %d pages (+ 1 headerpage)\n",hdesc->size,hdesc->size, hdesc->pages); printf("Used for data: %d/%d blocks/bytes, unused: %d/%d blocks/bytes.\n\n", hdesc->useblk,hdesc->usetot,hdesc->unuseblk,hdesc->unusetot); } /* So, let's guess what kind of hive this is, based on keys in its root */ hdesc->type = HTYPE_UNKNOWN; if (trav_path(hdesc, 0, "\\SAM", 0)) hdesc->type = HTYPE_SAM; else if (trav_path(hdesc, 0, "\\ControlSet", 0)) hdesc->type = HTYPE_SYSTEM; else if (trav_path(hdesc, 0, "\\Policy", 0)) hdesc->type = HTYPE_SECURITY; else if (trav_path(hdesc, 0, "\\Microsoft", 0)) hdesc->type = HTYPE_SOFTWARE; if (verbose) printf("Type of hive guessed to be: %d\n",hdesc->type); return(hdesc); } chntpw-1.0/Makefile0000600000175000017500000000346512273223316012444 0ustar jfsjfs# # Makefile for the Offline NT Password Editor # # # Change here to point to the needed OpenSSL libraries & .h files # See INSTALL for more info. # #SSLPATH=/usr/local/ssl OSSLPATH=/usr OSSLINC=$(OSSLPATH)/include CC=gcc # Force 32 bit CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall -m32 OSSLLIB=$(OSSLPATH)/lib # 64 bit if default for compiler setup #CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall #OSSLLIB=$(OSSLPATH)/lib64 # This is to link with whatever we have, SSL crypto lib we put in static #LIBS=-L$(OSSLLIB) $(OSSLLIB)/libcrypto.a LIBS=-L$(OSSLLIB) all: chntpw chntpw.static cpnt reged reged.static samusrgrp samusrgrp.static sampasswd sampasswd.static chntpw: chntpw.o ntreg.o edlib.o libsam.o $(CC) $(CFLAGS) -o chntpw chntpw.o ntreg.o edlib.o libsam.o $(LIBS) chntpw.static: chntpw.o ntreg.o edlib.o libsam.o $(CC) -static $(CFLAGS) -o chntpw.static chntpw.o ntreg.o edlib.o libsam.o $(LIBS) cpnt: cpnt.o $(CC) $(CFLAGS) -o cpnt cpnt.o $(LIBS) reged: reged.o ntreg.o edlib.o $(CC) $(CFLAGS) -o reged reged.o ntreg.o edlib.o reged.static: reged.o ntreg.o edlib.o $(CC) -static $(CFLAGS) -o reged.static reged.o ntreg.o edlib.o samusrgrp.static: samusrgrp.o ntreg.o libsam.o $(CC) -static $(CFLAGS) -o samusrgrp.static samusrgrp.o ntreg.o libsam.o samusrgrp: samusrgrp.o ntreg.o libsam.o $(CC) $(CFLAGS) -o samusrgrp samusrgrp.o ntreg.o libsam.o sampasswd: sampasswd.o ntreg.o libsam.o $(CC) $(CFLAGS) -o sampasswd sampasswd.o ntreg.o libsam.o sampasswd.static: sampasswd.o ntreg.o libsam.o $(CC) -static $(CFLAGS) -o sampasswd.static sampasswd.o ntreg.o libsam.o #ts: ts.o ntreg.o # $(CC) $(CFLAGS) -nostdlib -o ts ts.o ntreg.o $(LIBS) # -Wl,-t .c.o: $(CC) -c $(CFLAGS) $< clean: rm -f *.o chntpw chntpw.static cpnt reged reged.static samusrgrp samusrgrp.static sampasswd sampasswd.static *~ chntpw-1.0/edlib.c0000644000175000017500000004202712273223316012234 0ustar jfsjfs/* * edlib.c - Registry edit interactive fuctions. * * Point of this is so that interactive registry editor * can be accessed from several other programs * * 2013-aug: Some minor bugfixes and adjustments * Thanks to David Collett for catching and fixing bug in REG_MULTI_SZ editing. * 2010-jun: New function from Aleksander Wojdyga: dpi, decode product ID * Mostly used on \Microsoft\Windows NT\CurrentVersion\DigitalProductId * Now as command in registry editor, but may be moved to chnpw menu later. * 2010-apr: Lots of bugfix and other patches from * Frediano Ziglio * His short patch comments: * remove leak * fix default value, bin and quote * support wide char in key * support wide character into value names * fix export for string with embedded end lines * remove some warnings * compute checksum writing * * 2008-mar: First version. Moved from chntpw.c * See HISTORY.txt for more detailed info on history. * ***** * * Copyright (c) 1997-2014 Petter Nordahl-Hagen. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See file GPL.txt for the full license. * ***** */ #include #include #include #include #include #include "ntreg.h" const char edlib_version[] = "edlib version 0.1 140201, (c) Petter N Hagen"; #define ALLOC_DEBUG 0 /* Reg allocation debug hooks */ #define ADDBIN_DEBUG 0 /* Reg expansion debug hooks */ extern char *val_types[REG_MAX+1]; struct cmds { char cmd_str[12]; int cmd_num; }; #define MCMD_CD 1 #define MCMD_LS 2 #define MCMD_QUIT 3 #define MCMD_CAT 4 #define MCMD_STRUCT 5 #define MCMD_DEBUG 6 #define MCMD_HELP 7 #define MCMD_EXPORTKEY 8 #define MCMD_HIVE 9 #define MCMD_EDIT 10 #define MCMD_ALLOC 11 #define MCMD_FREE 12 #define MCMD_ADDV 13 #define MCMD_DELV 14 #define MCMD_DELVALL 15 #define MCMD_NEWKEY 16 #define MCMD_DELKEY 17 #define MCMD_CATHEX 18 #define MCMD_RDEL 19 #define MCMD_CK 20 #define MCMD_CAT_DPI 21 #define MCMD_ADDBIN 22 struct cmds maincmds[] = { { "cd" , MCMD_CD } , { "ls" , MCMD_LS } , { "dir", MCMD_LS } , { "q" , MCMD_QUIT } , { "cat", MCMD_CAT } , { "type",MCMD_CAT } , { "st" , MCMD_STRUCT } , { "debug", MCMD_DEBUG } , { "hive", MCMD_HIVE } , { "ed", MCMD_EDIT } , #if ALLOC_DEBUG { "alloc", MCMD_ALLOC } , { "free", MCMD_FREE } , #endif #if ADDBIN_DEBUG { "addbin", MCMD_ADDBIN }, #endif { "nv", MCMD_ADDV } , { "dv", MCMD_DELV } , { "delallv", MCMD_DELVALL } , { "nk", MCMD_NEWKEY } , { "dk", MCMD_DELKEY } , { "hex", MCMD_CATHEX } , { "rdel", MCMD_RDEL } , { "ek", MCMD_EXPORTKEY }, { "ck", MCMD_CK } , { "?", MCMD_HELP } , { "dpi", MCMD_CAT_DPI } , { "", 0 } }; /* display decoded DigitalProductId * nkofs = node * path = "DigitalProductId" or some other */ void cat_dpi(struct hive *hdesc, int nkofs, char *path) { void *data; int len,i,type; type = get_val_type(hdesc, nkofs, path, 0); if (type == -1) { printf("cat_dpi: No such value <%s>\n",path); return; } len = get_val_len(hdesc, nkofs, path, 0); if (len < 67) { printf("cat_dpi: Value <%s> is too short for decoding\n",path); return; } data = (void *)get_val_data(hdesc, nkofs, path, 0, 0); if (!data) { printf("cat_dpi: Value <%s> references NULL-pointer (bad boy!)\n",path); abort(); return; } if (type != REG_BINARY) { printf ("Only binary values\n"); return; } printf("Value <%s> of type %s, data length %d [0x%x]\n", path, (type < REG_MAX ? val_types[type] : "(unknown)"), len, len); char digits[] = {'B','C','D','F','G','H','J','K','M','P','Q','R','T','V','W','X','Y','2','3','4','6','7','8','9'}; #define RESULT_LEN 26 char result[RESULT_LEN]; memset (result, 0, RESULT_LEN); #define START_OFFSET 52 #define BUF_LEN 15 unsigned char buf[BUF_LEN]; memcpy (buf, data + START_OFFSET, BUF_LEN); for (i = RESULT_LEN - 2; i >= 0; i--) { unsigned int x = 0; int j; for (j = BUF_LEN - 1; j >= 0; j--) { x = (x << 8) + buf[j]; buf[j] = x / 24; x = x % 24; } result[i] = digits[x]; } printf ("\nDecoded product ID: [%s]\n", result); } /* display (cat) the value, * vofs = offset to 'nk' node, paths relative to this (or 0 for root) * path = path string to value * Does not handle all types yet (does a hexdump instead) */ void cat_vk(struct hive *hdesc, int nkofs, char *path, int dohex) { void *data; int len,i,type; // char string[SZ_MAX+1]; char *string = NULL; struct keyval *kv = NULL; type = get_val_type(hdesc, nkofs, path, TPF_VK); if (type == -1) { printf("cat_vk: No such value <%s>\n",path); return; } len = get_val_len(hdesc, nkofs, path, TPF_VK); if (!len) { printf("cat_vk: Value <%s> has zero length\n",path); return; } #if 0 data = (void *)get_val_data(hdesc, nkofs, path, 0, TPF_VK); if (!data) { printf("cat_vk: Value <%s> references NULL-pointer (bad boy!)\n",path); abort(); return; } #endif kv = get_val2buf(hdesc, NULL, nkofs, path, 0, TPF_VK); if (!kv) { printf("cat_vk: Value <%s> could not fetch data\n",path); abort(); } data = (void *)&(kv->data); printf("Value <%s> of type %s (%x), data length %d [0x%x]\n", path, (type < REG_MAX ? val_types[type] : "(unknown)"), type, len, len); if (dohex) type = REG_BINARY; switch (type) { case REG_SZ: case REG_EXPAND_SZ: case REG_MULTI_SZ: string = string_regw2prog(data, len); // cheap_uni2ascii(data,string,len); for (i = 0; i < (len>>1)-1; i++) { if (string[i] == 0) string[i] = '\n'; if (type == REG_SZ) break; } puts(string); FREE(string); break; case REG_DWORD: printf("0x%08x",*(unsigned short *)data); break; default: printf("Don't know how to handle type yet!\n"); case REG_BINARY: hexdump((char *)data, 0, len, 1); } putchar('\n'); FREE(kv); } /* Edit value: Invoke whatever is needed to edit it * based on its type */ void edit_val(struct hive *h, int nkofs, char *path) { struct keyval *kv, *newkv; int type,len,n,i,in,go, newsize, d = 0, done, insert = 0; char inbuf[SZ_MAX+4]; char origstring[SZ_MAX+4]; char *newstring; char *dbuf; type = get_val_type(h, nkofs, path, TPF_VK); if (type == -1) { printf("Value <%s> not found!\n",path); return; } kv = get_val2buf(h, NULL, nkofs, path, type, TPF_VK); if (!kv) { printf("Unable to get data of value <%s>\n",path); return; } len = kv->len; printf("EDIT: <%s> of type %s (%x) with length %d [0x%x]\n", path, (type < REG_MAX ? val_types[type] : "(unknown)"), type, len, len); switch(type) { case REG_DWORD: printf("DWORD: Old value %d [0x%x], ", kv->data, kv->data); fmyinput("enter new value (prepend 0x if hex, empty to keep old value)\n-> ", inbuf, 12); if (*inbuf) { sscanf(inbuf,"%i",&kv->data); d = 1; } printf("DWORD: New value %d [0x%x], ", kv->data, kv->data); break; case REG_SZ: case REG_EXPAND_SZ: case REG_MULTI_SZ: newstring = NULL; dbuf = (char *)&kv->data; cheap_uni2ascii(dbuf,origstring,len); n = 0; i = 0; while (i < (len>>1)-1) { printf("[%2d]: %s\n",n,origstring+i); i += strlen(origstring+i) + 1; n++; } printf("\nNow enter new strings, one by one.\n"); printf("Enter nothing to keep old.\n"); if (type == REG_MULTI_SZ) { printf("'--n' to quit (remove rest of strings)\n"); printf("'--i' insert new string at this point\n"); printf("'--q' to quit (leaving remaining strings as is)\n"); printf("'--Q' to quit and discard all changes\n"); printf("'--e' for empty string in this position\n"); } n = 0; i = 0; in = 0; go = 0; done = 0; /* Now this one is RATHER UGLY :-} */ while (i < (len>>1)-1 || !done) { printf("[%2d]: %s\n",n, insert == 1 ? "[INSERT]" : ((i < (len>>1)-1 ) ? origstring+i : "[NEW]")); if (insert) insert++; if (!go) fmyinput("-> ",inbuf, 500); else *inbuf = 0; if (*inbuf && strcmp("--q", inbuf)) { if (!strcmp("--n", inbuf) || !strcmp("--Q", inbuf)) { /* Zap rest */ i = (len>>1) ; done = 1; } else if (strcmp("--i", inbuf)) { /* Copy out given string */ if (!strcmp("--e",inbuf)) *inbuf = '\0'; if (newstring) newstring = realloc(newstring, in+strlen(inbuf)+1); else newstring = malloc(in+strlen(inbuf)+1); strcpy(newstring+in, inbuf); in += strlen(inbuf)+1; } else { insert = 1; } } else { /* Copy out default string */ if (newstring) newstring = realloc(newstring, in+strlen(origstring+i)+1); else newstring = malloc(in + strlen(origstring+i) + 1); strcpy(newstring+in, origstring+i); in += strlen(origstring+i)+1; if (!strcmp("--q", inbuf)) { go = 1; done = 1; if (!(i < (len>>1)-1 )) { in--; /* remove last empty if in NEW-mode */ } } } if (!insert) i += strlen(origstring+i) + 1; if (insert != 1) n++; if (insert == 2) insert = 0; if (type != REG_MULTI_SZ) { i = (len<<1); done = 1; } } if (strcmp("--Q", inbuf)) { /* We didn't bail out */ if (newstring) newstring = realloc(newstring, in+1); else newstring = malloc(in+1); if (type == REG_MULTI_SZ) { *(newstring+in) = '\0'; /* Must add null termination */ in++; } ALLOC(newkv,1,(in<<1)+sizeof(int)); newkv->len = in<<1; VERBF(h,"newkv->len: %d\n",newkv->len); cheap_ascii2uni(newstring, (char *)&(newkv->data), in); d = 1; FREE(kv); kv = newkv; } break; default: printf("Type not handeled (yet), invoking hex editor on data!\n"); case REG_BINARY: fmyinput("New length (ENTER to keep same): ",inbuf,90); if (*inbuf) { newsize = atoi(inbuf); ALLOC(newkv,1,newsize+sizeof(int)+4); bzero(newkv,newsize+sizeof(int)+4); memcpy(newkv, kv, ((len < newsize) ? (len) : (newsize)) + sizeof(int)); FREE(kv); kv = newkv; kv->len = newsize; } d = debugit((char *)&kv->data, kv->len); break; } if (d) { if (!(put_buf2val(h, kv, nkofs, path, type, TPF_VK))) { printf("Failed to set value!?\n"); } } FREE(kv); } /* look up command in array */ int parsecmd(char **s, struct cmds *cmd) { int l = 0; while ((*s)[l] && ((*s)[l] != ' ')) { l++; } while (cmd->cmd_num) { if (!strncmp(*s, cmd->cmd_str, l)) { *s += l; return(cmd->cmd_num); } cmd++; } return(0); } /* Lot of people didn't understand the "nv" command. * Actually the command should understand the type names too, but.. some later time */ void nv_help(void) { int i; printf("Command syntax is:\n\n" " nv \n\n" "where should be the HEX NUMBER from one of these registry value types:\n\n"); for (i=0; i < REG_MAX; i++) { printf(" %2x : %s\n",i,val_types[i]); } printf("\nExample:\n nv 4 foobar\n"); printf("to make a new value named foobar of the type REG_DWORD\n\n"); } /* Interactive registry editor * hive - list of loaded hives (array pointing to hive structs) * no_hives - max number of hives loaded */ void regedit_interactive(struct hive *hive[], int no_hives) { struct hive *hdesc; int cdofs, newofs; // struct nk_key *cdkey; char inbuf[100], *bp, *file, *prefix; char path[1000]; int l, vkofs, nh, i; int usehive = 0; struct keyval *kv; #if ALLOC_DEBUG int pagestart; int freetest; #endif hdesc = hive[usehive]; cdofs = hdesc->rootofs; printf("Simple registry editor. ? for help.\n"); while (1) { // cdkey = (struct nk_key *)(hdesc->buffer + cdofs); *path = 0; get_abs_path(hdesc,cdofs+4, path, 50); #if ALLOC_DEBUG pagestart = find_page_start(hdesc,cdofs); printf("find_page_start: 0x%x\n",pagestart); freetest = find_free_blk(hdesc,pagestart,10); printf("find_free_blk: 0x%x\n",freetest); #endif if (hdesc->state & HMODE_VERBOSE) printf("\n[%0x] %s> ",cdofs,path); else printf("\n%s> ",path); l = fmyinput("",inbuf,90); bp = inbuf; skipspace(&bp); if (l > 0 && *bp) { switch(parsecmd(&bp,maincmds)) { case MCMD_HELP: printf("Simple registry editor:\n"); printf("hive [] - list loaded hives or switch to hive numer n\n"); printf("cd - change current key\n"); printf("ls | dir [] - show subkeys & values,\n"); printf("cat | type - show key value\n"); printf("dpi - show decoded DigitalProductId value\n"); printf("hex - hexdump of value data\n"); printf("ck [] - Show keys class data, if it has any\n"); printf("nk - add key\n"); printf("dk - delete key (must be empty)\n"); printf("ed - Edit value\n"); printf("nv - Add value\n"); printf("dv - Delete value\n"); printf("delallv - Delete all values in current key\n"); printf("rdel - Recursively delete key & subkeys\n"); printf("ek - export key to (Windows .reg file format)\n"); printf("debug - enter buffer hexeditor\n"); printf("st [] - debug function: show struct info\n"); printf("q - quit\n"); break; case MCMD_DELKEY : bp++; skipspace(&bp); del_key(hdesc, cdofs + 4, bp); break; case MCMD_NEWKEY : bp++; skipspace(&bp); add_key(hdesc, cdofs + 4, bp); break; case MCMD_DELVALL : bp++; skipspace(&bp); del_allvalues(hdesc, cdofs + 4); break; case MCMD_DELV : bp++; skipspace(&bp); del_value(hdesc, cdofs + 4, bp, 0); break; case MCMD_ADDV : bp++; skipspace(&bp); if (!isxdigit(*bp)) { nv_help(); break; } nh = gethex(&bp); skipspace(&bp); if (!*bp) { nv_help(); break; } add_value(hdesc, cdofs+4, bp, nh); break; #if ALLOC_DEBUG case MCMD_FREE : bp++; skipspace(&bp); nh = gethex(&bp); free_block(hdesc, nh); break; case MCMD_ALLOC : bp++; skipspace(&bp); nh = gethex(&bp); alloc_block(hdesc, cdofs+4, nh); break; #endif #if ADDBIN_DEBUG case MCMD_ADDBIN : bp++; skipspace(&bp); nh = gethex(&bp); add_bin(hdesc, nh); break; #endif case MCMD_LS : bp++; skipspace(&bp); nk_ls(hdesc, bp, cdofs+4, 0); break; case MCMD_CK : bp++; skipspace(&bp); kv = get_class(hdesc, cdofs+4, bp); if (kv) { hexdump((char *)&kv->data, 0, kv->len, 1); FREE(kv); } break; case MCMD_RDEL : bp++; skipspace(&bp); rdel_keys(hdesc, bp, cdofs+4); break; case MCMD_EDIT : bp++; skipspace(&bp); edit_val(hdesc, cdofs+4, bp); break; case MCMD_HIVE : bp++; skipspace(&bp); if (*bp) { nh = gethex(&bp); if (nh >= 0 && nh < no_hives) { usehive = nh; printf("Switching to hive #%d, named <%s>, size %d [0x%x]\n", usehive, hive[usehive]->filename, hive[usehive]->size, hive[usehive]->size); hdesc = hive[usehive]; cdofs = hdesc->rootofs; } } else { for (nh = 0; nh < no_hives; nh++) { printf("%c %c %2d %9d 0x%08x <%s>\n", (nh == usehive) ? '*' : ' ', (hive[nh]->state & HMODE_DIRTY) ? 'D' : ' ', nh, hive[nh]->size, hive[nh]->size, hive[nh]->filename); } } break; case MCMD_CD : bp++; skipspace(&bp); newofs = trav_path(hdesc, cdofs+4,bp,TPF_NK); if (newofs) cdofs = newofs; else printf("Key %s not found!\n",bp); break; case MCMD_CAT: bp++; skipspace(&bp); cat_vk(hdesc,cdofs+4,bp,0); break; case MCMD_CAT_DPI: bp++; skipspace(&bp); cat_dpi (hdesc, cdofs+4, bp); break; case MCMD_CATHEX: bp++; skipspace(&bp); cat_vk(hdesc,cdofs+4,bp,1); break; case MCMD_EXPORTKEY : bp++; skipspace(&bp); file = bp; i = 0; while(*bp != ' ' && (*bp)) { i++; bp++; } file[i] = '\0'; bp++; skipspace(&bp); prefix = bp; i = 0; while(*bp != ' ' && (*bp)) { i++; bp++; } prefix[i] = '\0'; bp++; skipspace(&bp); export_key(hdesc, cdofs + 4, bp, file, prefix); break; case MCMD_STRUCT: bp++; skipspace(&bp); vkofs = cdofs; if (*bp) { vkofs = gethex(&bp); } parse_block(hdesc,vkofs,2); break; case MCMD_DEBUG: if (debugit(hdesc->buffer,hdesc->size)) hdesc->state |= HMODE_DIRTY; break; case MCMD_QUIT: return; break; default: printf("Unknown command: %s, type ? for help\n",bp); break; } } } } chntpw-1.0/chntpw.c0000600000175000017500000010136712273223316012453 0ustar jfsjfs/* * chntpw.c - Offline Password Edit Utility for Windows SAM database * * This program uses the "ntreg" library to load and access the registry, * the "libsam" library for user / group handling * it's main purpose is to reset password based information. * It can also call the registry editor etc * 2013-may: Added group add/remove in user edit (using new functions * in sam library) * 2013-apr: Changed around a bit on some features, chntpw is now * mainly used for interactive edits. * For automatic/scripted functions, use new programs: * sampasswd and samusrgrp ! * 2011-apr: Command line options added for hive expansion safe mode * 2010-jun: Syskey not visible in menu, but is selectable (2) * 2010-apr: Interactive menu adapts to show most relevant * selections based on what is loaded * 2008-mar: Minor other tweaks * 2008-mar: Interactive reg ed moved out of this file, into edlib.c * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev * http://bugs.gentoo.org/show_bug.cgi?id=185411 * 2007-sep: Group handling extended, promotion now public * 2007-sep: User edit menu, some changes to user info edit * 2007-apr-may: Get and display users group memberships * 2007-apr: GNU license. Some bugfixes. Cleaned up some output. * 2004-aug: More stuff in regedit. Stringinput bugfixes. * 2004-jan: Changed some of the verbose/debug stuff * 2003-jan: Changed to use more of struct based V + some small stuff * 2003-jan: Support in ntreg for adding keys etc. Editor updated. * 2002-dec: New option: Specify user using RID * 2002-dec: New option: blank the pass (zero hash lengths). * 2001-jul: extra blank password logic (when NT or LANMAN hash missing) * 2001-jan: patched & changed to use OpenSSL. Thanks to Denis Ducamp * 2000-jun: changing passwords regardless of syskey. * 2000-jun: syskey disable works on NT4. Not properly on NT5. * 2000-jan: Attempt to detect and disable syskey * 1999-feb: Now able to browse registry hives. (write support to come) * See HISTORY.txt for more detailed info on history. * ***** * * Copyright (c) 1997-2014 Petter Nordahl-Hagen. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See file GPL.txt for the full license. * ***** * * Information and ideas taken from pwdump by Jeremy Allison. * * More info from NTCrack by Jonathan Wilkins. * */ #include #include #include #include #include #include #include #include #include #include /* Define DOCRYPTO in makefile to include cryptostuff to be able to change passwords to * a new one. * Changing passwords is seems not to be working reliably on XP and newer anyway. * When not defined, only reset (nulling) of passwords available. */ #ifdef DOCRYPTO #include #include #endif #define uchar u_char #define MD4Init MD4_Init #define MD4Update MD4_Update #define MD4Final MD4_Final #include "ntreg.h" #include "sam.h" const char chntpw_version[] = "chntpw version 1.00 140201, (c) Petter N Hagen"; extern char *val_types[REG_MAX+1]; /* Global verbosity */ int gverbose = 0; #define MAX_HIVES 10 /* Array of loaded hives */ struct hive *hive[MAX_HIVES+1]; int no_hives = 0; /* Icky icky... globals used to refer to hives, will be * set when loading, so that hives can be loaded in any order */ int H_SAM = -1; int H_SYS = -1; int H_SEC = -1; int H_SOF = -1; int syskeyreset = 0; int dirty = 0; int max_sam_lock = 0; /* ============================================================== */ #ifdef DOCRYPTO /* Crypto-stuff & support for what we'll do in the V-value */ /* Zero out string for lanman passwd, then uppercase * the supplied password and put it in here */ void make_lanmpw(char *p, char *lm, int len) { int i; for (i=0; i < 15; i++) lm[i] = 0; for (i=0; i < len; i++) lm[i] = toupper(p[i]); } /* * Convert a 7 byte array into an 8 byte des key with odd parity. */ void str_to_key(unsigned char *str,unsigned char *key) { int i; key[0] = str[0]>>1; key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); key[7] = str[6]&0x7F; for (i=0;i<8;i++) { key[i] = (key[i]<<1); } DES_set_odd_parity((des_cblock *)key); } /* * Function to convert the RID to the first decrypt key. */ void sid_to_key1(uint32_t sid,unsigned char deskey[8]) { unsigned char s[7]; s[0] = (unsigned char)(sid & 0xFF); s[1] = (unsigned char)((sid>>8) & 0xFF); s[2] = (unsigned char)((sid>>16) & 0xFF); s[3] = (unsigned char)((sid>>24) & 0xFF); s[4] = s[0]; s[5] = s[1]; s[6] = s[2]; str_to_key(s,deskey); } /* * Function to convert the RID to the second decrypt key. */ void sid_to_key2(uint32_t sid,unsigned char deskey[8]) { unsigned char s[7]; s[0] = (unsigned char)((sid>>24) & 0xFF); s[1] = (unsigned char)(sid & 0xFF); s[2] = (unsigned char)((sid>>8) & 0xFF); s[3] = (unsigned char)((sid>>16) & 0xFF); s[4] = s[0]; s[5] = s[1]; s[6] = s[2]; str_to_key(s,deskey); } /* DES encrypt, for LANMAN */ void E1(uchar *k, uchar *d, uchar *out) { des_key_schedule ks; des_cblock deskey; str_to_key(k,(uchar *)deskey); #ifdef __FreeBSD__ des_set_key(&deskey,ks); #else /* __FreeBsd__ */ des_set_key((des_cblock *)deskey,ks); #endif /* __FreeBsd__ */ des_ecb_encrypt((des_cblock *)d,(des_cblock *)out, ks, DES_ENCRYPT); } #endif /* DOCRYPTO */ /* Promote user into administrators group (group ID 0x220) * rid - users rid * no returns yet */ void promote_user(int rid) { char yn[5]; if (!rid || (H_SAM < 0)) return; printf("\n=== PROMOTE USER\n\n"); printf("Will add the user to the administrator group (0x220)\n" "and to the users group (0x221). That should usually be\n" "what is needed to log in and get administrator rights.\n" "Also, remove the user from the guest group (0x222), since\n" "it may forbid logins.\n\n"); printf("(To add or remove user from other groups, please other menu selections)\n\n"); printf("Note: You may get some errors if the user is already member of some\n" "of these groups, but that is no problem.\n\n"); fmyinput("Do it? (y/n) [n] : ", yn, 3); if (*yn == 'y') { printf("* Adding to 0x220 (Administrators) ...\n"); sam_add_user_to_grp(hive[H_SAM], rid, 0x220); printf("* Adding to 0x221 (Users) ...\n"); sam_add_user_to_grp(hive[H_SAM], rid, 0x221); printf("* Removing from 0x222 (Guests) ...\n"); sam_remove_user_from_grp(hive[H_SAM], rid, 0x222); printf("\nPromotion DONE!\n"); } else { printf("Nothing done, going back..\n"); } } void interactive_remusrgrp(int rid) { char inp[20]; int grp; printf("\n=== REMOVE USER FROM A GROUP\n"); sam_list_user_groups(hive[H_SAM], rid,0); printf("\nPlease enter group number (for example 220), or 0 to go back\n"); fmyinput("Group number? : ",inp,16); sscanf(inp, "%x", &grp); if (!grp) { printf("Going back..\n"); return; } printf("Removing user from group 0x%x (%d)\n",grp,grp); printf("Error messages if the user was not member of the group are harmless\n\n"); sam_remove_user_from_grp(hive[H_SAM], rid, grp); printf("\nFinished removing user from group\n\n"); } void interactive_addusrgrp(int rid) { char inp[20]; int grp; printf("\n == ADD USER TO A GROUP\n"); sam_list_groups(hive[H_SAM], 0, 1); printf("\nPlease enter group number (for example 220), or 0 to go back\n"); fmyinput("Group number? : ",inp,16); sscanf(inp, "%x", &grp); if (!grp) { printf("Going back..\n"); return; } printf("Adding user to group 0x%x (%d)\n",grp,grp); printf("Error messages if the user was already member of the group are harmless\n\n"); sam_add_user_to_grp(hive[H_SAM], rid, grp); printf("\nFinished adding user to group\n\n"); } /* Decode the V-struct, and change the password * vofs - offset into SAM buffer, start of V struct * rid - the users RID, required for the DES decrypt stage * * Some of this is ripped & modified from pwdump by Jeremy Allison * */ char *change_pw(char *buf, int rid, int vlen, int stat) { int pl; char *vp; static char username[128],fullname[128]; char comment[128], homedir[128], newp[20]; int username_offset,username_len; int fullname_offset,fullname_len; int comment_offset,comment_len; int homedir_offset,homedir_len; int ntpw_len,lmpw_len,ntpw_offs,lmpw_offs; unsigned short acb; struct user_V *v; #ifdef DOCRYPT int dontchange = 0; int i; char md4[32],lanman[32]; char newunipw[34], despw[20], newlanpw[16], newlandes[20]; des_key_schedule ks1, ks2; des_cblock deskey1, deskey2; MD4_CTX context; unsigned char digest[16]; uchar x1[] = {0x4B,0x47,0x53,0x21,0x40,0x23,0x24,0x25}; #endif while (1) { /* Loop until quit input */ v = (struct user_V *)buf; vp = buf; username_offset = v->username_ofs; username_len = v->username_len; fullname_offset = v->fullname_ofs; fullname_len = v->fullname_len; comment_offset = v->comment_ofs; comment_len = v->comment_len; homedir_offset = v->homedir_ofs; homedir_len = v->homedir_len; lmpw_offs = v->lmpw_ofs; lmpw_len = v->lmpw_len; ntpw_offs = v->ntpw_ofs; ntpw_len = v->ntpw_len; if (!rid) { printf("No RID given. Unable to change passwords..\n"); return(0); } if (gverbose) { printf("lmpw_offs: 0x%x, lmpw_len: %d (0x%x)\n",lmpw_offs,lmpw_len,lmpw_len); printf("ntpw_offs: 0x%x, ntpw_len: %d (0x%x)\n",ntpw_offs,ntpw_len,ntpw_len); } *username = 0; *fullname = 0; *comment = 0; *homedir = 0; if(username_len <= 0 || username_len > vlen || username_offset <= 0 || username_offset >= vlen || comment_len < 0 || comment_len > vlen || fullname_len < 0 || fullname_len > vlen || homedir_offset < 0 || homedir_offset >= vlen || comment_offset < 0 || comment_offset >= vlen || lmpw_offs < 0 || lmpw_offs >= vlen) { if (stat != 1) printf("change_pw: Not a legal V struct? (negative struct lengths)\n"); return(NULL); } /* Offsets in top of struct is relative to end of pointers, adjust */ username_offset += 0xCC; fullname_offset += 0xCC; comment_offset += 0xCC; homedir_offset += 0xCC; ntpw_offs += 0xCC; lmpw_offs += 0xCC; cheap_uni2ascii(vp + username_offset,username,username_len); cheap_uni2ascii(vp + fullname_offset,fullname,fullname_len); cheap_uni2ascii(vp + comment_offset,comment,comment_len); cheap_uni2ascii(vp + homedir_offset,homedir,homedir_len); #if 0 /* Reset hash-lengths to 16 if syskey has been reset */ if (syskeyreset && ntpw_len > 16 && !stat) { ntpw_len = 16; lmpw_len = 16; ntpw_offs -= 4; (unsigned int)*(vp+0xa8) = ntpw_offs - 0xcc; *(vp + 0xa0) = 16; *(vp + 0xac) = 16; } #endif printf("================= USER EDIT ====================\n"); printf("\nRID : %04d [%04x]\n",rid,rid); printf("Username: %s\n",username); printf("fullname: %s\n",fullname); printf("comment : %s\n",comment); printf("homedir : %s\n\n",homedir); sam_list_user_groups(hive[H_SAM], rid,0); printf("\n"); acb = sam_handle_accountbits(hive[H_SAM], rid,1); if (lmpw_len < 16 && gverbose) { printf("** LANMAN password not set. User MAY have a blank password.\n** Usually safe to continue. Normal in Vista\n"); } if (ntpw_len < 16) { printf("** No NT MD4 hash found. This user probably has a BLANK password!\n"); if (lmpw_len < 16) { printf("** No LANMAN hash found either. Try login with no password!\n"); #ifdef DOCRYPTO dontchange = 1; #endif } else { printf("** LANMAN password IS however set. Will now install new password as NT pass instead.\n"); printf("** NOTE: Continue at own risk!\n"); ntpw_offs = lmpw_offs; *(vp+0xa8) = ntpw_offs - 0xcc; ntpw_len = 16; lmpw_len = 0; } } if (gverbose) { hexprnt("Crypted NT pw: ",(unsigned char *)(vp+ntpw_offs),16); hexprnt("Crypted LM pw: ",(unsigned char *)(vp+lmpw_offs),16); } #ifdef DOCRYPTO /* Get the two decrpt keys. */ sid_to_key1(rid,(unsigned char *)deskey1); des_set_key((des_cblock *)deskey1,ks1); sid_to_key2(rid,(unsigned char *)deskey2); des_set_key((des_cblock *)deskey2,ks2); /* Decrypt the NT md4 password hash as two 8 byte blocks. */ des_ecb_encrypt((des_cblock *)(vp+ntpw_offs ), (des_cblock *)md4, ks1, DES_DECRYPT); des_ecb_encrypt((des_cblock *)(vp+ntpw_offs + 8), (des_cblock *)&md4[8], ks2, DES_DECRYPT); /* Decrypt the lanman password hash as two 8 byte blocks. */ des_ecb_encrypt((des_cblock *)(vp+lmpw_offs), (des_cblock *)lanman, ks1, DES_DECRYPT); des_ecb_encrypt((des_cblock *)(vp+lmpw_offs + 8), (des_cblock *)&lanman[8], ks2, DES_DECRYPT); if (gverbose) { hexprnt("MD4 hash : ",(unsigned char *)md4,16); hexprnt("LANMAN hash : ",(unsigned char *)lanman,16); } #endif /* DOCRYPTO */ printf("\n- - - - User Edit Menu:\n"); printf(" 1 - Clear (blank) user password\n"); printf("%s2 - Unlock and enable user account%s\n", (acb & 0x8000) ? " " : "(", (acb & 0x8000) ? " [probably locked now]" : ") [seems unlocked already]"); printf(" 3 - Promote user (make user an administrator)\n"); printf(" 4 - Add user to a group\n"); printf(" 5 - Remove user from a group\n"); #ifdef DOCRYPTO printf(" 9 - Edit (set new) user password (careful with this on XP or Vista)\n"); #endif printf(" q - Quit editing user, back to user select\n"); pl = fmyinput("Select: [q] > ",newp,16); if ( (pl < 1) || (*newp == 'q') || (*newp == 'Q')) return(0); if (*newp == '2') { acb = sam_handle_accountbits(hive[H_SAM], rid,2); // return(username); } if (*newp == '3') { promote_user(rid); // return(username); } if (*newp == '4') { interactive_addusrgrp(rid); // return(username); } if (*newp == '5') { interactive_remusrgrp(rid); // return(username); } #ifdef DOCRYPT if (*newp == '9') { /* Set new password */ if (dontchange) { printf("Sorry, unable to edit since password seems blank already (thus no space for it)\n"); return(0); } pl = fmyinput("New Password: ",newp,16); if (pl < 1) { printf("No change.\n"); return(0); } cheap_ascii2uni(newp,newunipw,pl); make_lanmpw(newp,newlanpw,pl); /* printf("Ucase Lanman: %s\n",newlanpw); */ MD4Init (&context); MD4Update (&context, newunipw, pl<<1); MD4Final (digest, &context); if (gverbose) hexprnt("\nNEW MD4 hash : ",digest,16); E1((uchar *)newlanpw, x1, (uchar *)lanman); E1((uchar *)newlanpw+7, x1, (uchar *)lanman+8); if (gverbose) hexprnt("NEW LANMAN hash : ",(unsigned char *)lanman,16); /* Encrypt the NT md4 password hash as two 8 byte blocks. */ des_ecb_encrypt((des_cblock *)digest, (des_cblock *)despw, ks1, DES_ENCRYPT); des_ecb_encrypt((des_cblock *)(digest+8), (des_cblock *)&despw[8], ks2, DES_ENCRYPT); des_ecb_encrypt((des_cblock *)lanman, (des_cblock *)newlandes, ks1, DES_ENCRYPT); des_ecb_encrypt((des_cblock *)(lanman+8), (des_cblock *)&newlandes[8], ks2, DES_ENCRYPT); if (gverbose) { hexprnt("NEW DES crypt : ",(unsigned char *)despw,16); hexprnt("NEW LANMAN crypt: ",(unsigned char *)newlandes,16); } /* Reset hash length to 16 if syskey enabled, this will cause * a conversion to syskey-hashes upon next boot */ if (syskeyreset && ntpw_len > 16) { ntpw_len = 16; lmpw_len = 16; ntpw_offs -= 4; *(vp+0xa8) = (unsigned int)(ntpw_offs - 0xcc); *(vp + 0xa0) = 16; *(vp + 0xac) = 16; } for (i = 0; i < 16; i++) { *(vp+ntpw_offs+i) = (unsigned char)despw[i]; if (lmpw_len >= 16) *(vp+lmpw_offs+i) = (unsigned char)newlandes[i]; } printf("Password changed!\n"); } /* new password */ #endif /* DOCRYPT */ if (pl == 1 && *newp == '1') { /* Setting hash lengths to zero seems to make NT think it is blank * However, since we cant cut the previous hash bytes out of the V value * due to missing resize-support of values, it may leak about 40 bytes * each time we do this. */ v->ntpw_len = 0; v->lmpw_len = 0; dirty = 1; printf("Password cleared!\n"); } #if 0 hexprnt("Pw in buffer: ",(vp+ntpw_offs),16); hexprnt("Lm in buffer: ",(vp+lmpw_offs),16); #endif } // Forever... return(username); } /* Find a username in the SAM registry, then get it's V-value, * and feed it to the password changer. */ void find_n_change(char *username) { char s[200]; struct keyval *v; int rid = 0; if ((H_SAM < 0) || (!username)) return; if (*username == '0' && *(username+1) == 'x') sscanf(username,"%i",&rid); if (!rid) { /* Look up username */ /* Extract the unnamed value out of the username-key, value is RID */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",username); rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT); if (rid == -1) { printf("Cannot find user, path is <%s>\n",s); return; } } /* printf("Username: %s, RID = %d (0x%0x)\n",username,rid,rid); */ /* Now that we have the RID, build the path to, and get the V-value */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid); v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT); if (!v) { printf("Cannot find users V value <%s>\n",s); return; } if (v->len < 0xcc) { printf("Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n", s, v->len); } else { change_pw( (char *)&v->data , rid, v->len, 0); if (dirty) { if (!(put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY, TPF_VK_EXACT))) { printf("Failed to write updated <%s> to registry! Password change not completed!\n",s); } } } FREE(v); } /* Check for presence of syskey and possibly disable it if * user wants it. * This is tricky, and extremely undocumented! * See docs for more info on what's going on when syskey is installed */ #undef LSADATA void handle_syskey(void) { /* This is \SAM\Domains\Account\F */ struct samkeyf { char unknown[0x50]; /* 0x0000 - Unknown. May be machine SID */ char unknown2[0x14]; char syskeymode; /* 0x0064 - Type/mode of syskey in use */ char syskeyflags1[0xb]; /* 0x0065 - More flags/settings */ char syskeyobf[0x30]; /* 0x0070 - This may very well be the obfuscated syskey */ }; /* There may be more, usually 8 null-bytes? */ /* Security\Policy\SecretEncryptionKey\@, only on NT5 */ /* Probably contains some keyinfo for syskey. Second DWORD seems to be syskeymode */ struct secpoldata { int unknown1; /* Some kind of flag? usually 1 */ int syskeymode; /* Is this what we're looking for? */ int unknown2; /* Usually 0? */ char keydata[0x40]; /* Some kind of scrambled keydata? */ }; #ifdef LSADATA /* SYSTEM\CurrentControlSet\Control\Lsa\Data, only on NT5?? */ /* Probably contains some keyinfo for syskey. Byte 0x34 seems to be mode */ struct lsadata { char keydata[0x34]; /* Key information */ int syskeymode; /* Is this what we're looking for? */ }; #endif /* void *fdata; */ struct samkeyf *ff = NULL; struct secpoldata *sf = NULL; /* struct lsadata *ld = NULL; */ int /* len, */ i,secboot, samfmode, secmode /* , ldmode */ ; struct keyval *samf, *secpol /* , *lsad */ ; char *syskeytypes[4] = { "off", "key-in-registry", "enter-passphrase", "key-on-floppy" }; char yn[5]; printf("\n---------------------> SYSKEY CHECK <-----------------------\n"); if (H_SAM < 0) { printf("ERROR: SAM hive not loaded!\n"); return; } samf = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT); if (samf && samf->len > 0x70 ) { ff = (struct samkeyf *)&samf->data; samfmode = ff->syskeymode; } else { samfmode = -1; } secboot = -1; if (H_SYS >= 0) { secboot = get_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT ); } secmode = -1; if (H_SEC >=0) { secpol = get_val2buf(hive[H_SEC], NULL, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_NONE, TPF_VK_EXACT); if (secpol) { /* Will not be found in NT 4, take care of that */ sf = (struct secpoldata *)&secpol->data; secmode = sf->syskeymode; } } #ifdef LSADATA lsad = get_val2buf(hive[H_SYS], NULL, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT); if (lsad && lsad->len >= 0x38) { ld = (struct lsadata *)&lsad->data; ldmode = ld->syskeymode; } else { ldmode = -1; } #endif printf("SYSTEM SecureBoot : %d -> %s\n", secboot, (secboot < 0 || secboot > 3) ? "Not Set (not installed, good!)" : syskeytypes[secboot]); printf("SAM Account\\F : %d -> %s\n", samfmode, (samfmode < 0 || samfmode > 3) ? "Not Set" : syskeytypes[samfmode]); printf("SECURITY PolSecretEncryptionKey: %d -> %s\n", secmode, (secmode < 0 || secmode > 3) ? "Not Set (OK if this is NT4)" : syskeytypes[secmode]); #ifdef LSADATA printf("SYSTEM LsaData : %d -> %s\n\n", ldmode, (ldmode < 0 || ldmode > 3) ? "Not Set (strange?)" : syskeytypes[ldmode]); #endif if (secboot != samfmode && secboot != -1) { printf("WARNING: Mismatch in syskey settings in SAM and SYSTEM!\n"); printf("WARNING: It may be dangerous to continue (however, resetting syskey\n"); printf(" may very well fix the problem)\n"); } if (secboot > 0 || samfmode > 0) { printf("\n***************** SYSKEY IS ENABLED! **************\n"); printf("This installation very likely has the syskey passwordhash-obfuscator installed\n"); printf("It's currently in mode = %d, %s-mode\n",secboot, (secboot < 0 || secboot > 3) ? "Unknown" : syskeytypes[secboot]); if (no_hives < 2) { printf("\nSYSTEM (and possibly SECURITY) hives not loaded, unable to disable syskey!\n"); printf("Please start the program with at least SAM & SYSTEM-hive filenames as arguments!\n\n"); return; } printf("SYSKEY is on! However, DO NOT DISABLE IT UNLESS YOU HAVE TO!\n"); printf("This program can change passwords even if syskey is on, however\n"); printf("if you have lost the key-floppy or passphrase you can turn it off,\n"); printf("but please read the docs first!!!\n"); printf("\n** IF YOU DON'T KNOW WHAT SYSKEY IS YOU DO NOT NEED TO SWITCH IT OFF!**\n"); printf("NOTE: On WINDOWS 2000 and XP it will not be possible\n"); printf("to turn it on again! (and other problems may also show..)\n\n"); printf("NOTE: Disabling syskey will invalidate ALL\n"); printf("passwords, requiring them to be reset. You should at least reset the\n"); printf("administrator password using this program, then the rest ought to be\n"); printf("done from NT.\n"); printf("\nEXTREME WARNING: Do not try this on Vista or Win 7, it will go into endless re-boots\n\n"); fmyinput("\nDo you really wish to disable SYSKEY? (y/n) [n] ",yn,2); if (*yn == 'y') { /* Reset SAM syskey infostruct, fill with zeroes */ if (ff) { ff->syskeymode = 0; for (i = 0; i < 0x3b; i++) { ff->syskeyflags1[i] = 0; } put_buf2val(hive[H_SAM], samf, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT); } /* Reset SECURITY infostruct (if any) */ if (sf) { memset(sf, 0, secpol->len); sf->syskeymode = 0; put_buf2val(hive[H_SEC], secpol, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_BINARY, TPF_VK_EXACT); } #if LSADATA if (ld) { ld->syskeymode = 0; put_buf2val(hive[H_SYS], lsad, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT); } #endif /* And SYSTEM SecureBoot parameter */ put_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT, 0); dirty = 1; syskeyreset = 1; printf("Updating passwordhash-lengths..\n"); sam_list_users(hive[H_SAM], 1); printf("* SYSKEY RESET!\nNow please set new administrator password!\n"); } else { syskeyreset = 1; } } else { printf("Syskey not installed!\n"); return; } } /* Interactive user edit */ void useredit(void) { char iwho[100]; int il, admrid; int rid = 0; printf("\n\n===== chntpw Edit User Info & Passwords ====\n\n"); if (H_SAM < 0) { printf("ERROR: SAM registry file (which contains user data) is not loaded!\n\n"); return; } admrid = sam_list_users(hive[H_SAM], 1); printf("\nPlease enter user number (RID) or 0 to exit: [%x] ", admrid); il = fmyinput("",iwho,32); if (il == 0) { sprintf(iwho,"0x%x",admrid); rid = admrid; } else { sscanf(iwho, "%x", &rid); sprintf(iwho,"0x%x",rid); } if (!rid) return; find_n_change(iwho); } void recoveryconsole() { int cmd = 0; int sec = 0; static char *scpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SetCommand"; static char *slpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SecurityLevel"; char yn[5]; if (H_SOF < 0) { printf("\nSOFTWARE-hive not loaded, and there's where RecoveryConsole settings are..\n"); return; } cmd = get_dword(hive[H_SOF],0,scpath,TPF_VK_EXACT); sec = get_dword(hive[H_SOF],0,slpath,TPF_VK_EXACT); if (cmd == -1 && sec == -1) { printf("\nDid not find registry entries for RecoveryConsole.\n(RecoveryConsole is only in Windows 2000 and XP)\n"); return; } printf("\nRecoveryConsole:\n- Extended SET command is \t%s\n", cmd>0 ? "ENABLED (1)" : "DISABLED (0)"); printf("- Administrator password login: %s\n", sec>0 ? "SKIPPED (1)" : "ENFORCED (0)"); fmyinput("\nDo you want to change it? (y/n) [n] ",yn,2); if (*yn == 'y') { cmd ^= 1; sec ^= 1; if (!put_dword(hive[0], 0, scpath, TPF_VK_EXACT, cmd)) printf("Update of SET level failed registry edit\n"); if (!put_dword(hive[0], 0, slpath, TPF_VK_EXACT, sec)) printf("Update of login level failed registry edit\n"); printf("Done!\n"); } } void listgroups(void) { char yn[8]; int il; int members = 0; il = fmyinput("Also list group members? [n] ", yn, 2); if (il && (yn[0] == 'y' || yn[0] == 'Y')) members = 1; sam_list_groups(hive[H_SAM], members, 1); } /* Interactive menu system */ void interactive(void) { int il; char inbuf[20]; while(1) { printf("\n\n<>========<> chntpw Main Interactive Menu <>========<>\n\n" "Loaded hives:"); for (il = 0; il < no_hives; il++) { printf(" <%s>",hive[il]->filename); } printf("\n\n"); /* Make menu selection depending on what is loaded but it is still possible to select even if not shown */ if (H_SAM >= 0) { printf(" 1 - Edit user data and passwords\n"); printf(" 2 - List groups\n"); } if (H_SOF >= 0) { printf(" 3 - RecoveryConsole settings\n"); printf(" 4 - Show product key (DigitalProductID)\n"); } #if 0 if (H_SAM >= 0 && H_SYS >= 0 && H_SEC >= 0) { printf(" 8 - Syskey status & change\n"); } #endif printf(" - - -\n" " 9 - Registry editor, now with full write support!\n" " q - Quit (you will be asked if there is something to save)\n" "\n\n"); il = fmyinput("What to do? [1] -> ", inbuf, 10); if (!il) useredit(); if (il) { switch(inbuf[0]) { case '1': useredit(); break; case '2': listgroups(); break; case '3': recoveryconsole(); break; case '4': cat_dpi(hive[H_SOF],0,"\\Microsoft\\Windows NT\\CurrentVersion\\DigitalProductId"); break; case '8': handle_syskey(); break; case '9': regedit_interactive(hive, no_hives); break; case 'q': return; break; } } } } void usage(void) { printf("chntpw: change password of a user in a Windows SAM file,\n" "or invoke registry editor. Should handle both 32 and 64 bit windows and\n" "all version from NT3.x to Win8.1\n" "chntpw [OPTIONS] [systemfile] [securityfile] [otherreghive] [...]\n" " -h This message\n" " -u Username or RID (0x3e9 for example) to interactively edit\n" " -l list all users in SAM file and exit\n" " -i Interactive Menu system\n" // " -f Interactively edit first admin user\n" " -e Registry editor. Now with full write support!\n" " -d Enter buffer debugger instead (hex editor), \n" " -v Be a little more verbose (for debuging)\n" " -L For scripts, write names of changed files to /tmp/changed\n" " -N No allocation mode. Only same length overwrites possible (very safe mode)\n" " -E No expand mode, do not expand hive file (safe mode)\n" "\nUsernames can be given as name or RID (in hex with 0x first)\n" "\nSee readme file on how to get to the registry files, and what they are.\n" "Source/binary freely distributable under GPL v2 license. See README for details.\n" "NOTE: This program is somewhat hackish! You are on your own!\n" ); } int main(int argc, char **argv) { int dodebug = 0, list = 0, inter = 0,edit = 0,il,d = 0, dd = 0, logchange = 0; int mode = HMODE_INFO; extern int /* opterr, */ optind; extern char* optarg; char *filename,c; char *who = "Administrator"; char iwho[100]; FILE *ch; /* Write out names of touched files to this */ char *options = "LENidehflvu:"; while((c=getopt(argc,argv,options)) > 0) { switch(c) { case 'd': dodebug = 1; break; case 'e': edit = 1; break; case 'L': logchange = 1; break; case 'N': mode |= HMODE_NOALLOC; break; case 'E': mode |= HMODE_NOEXPAND; break; case 'l': list = 1; break; case 'v': mode |= HMODE_VERBOSE; gverbose = 1; break; case 'i': inter = 1; break; case 'u': who = optarg; break; case 'h': usage(); exit(0); break; default: usage(); exit(1); break; } } printf("%s\n",chntpw_version); filename=argv[optind]; if (!filename || !*filename) { usage(); exit(1); } do { if (!(hive[no_hives] = openHive(filename, HMODE_RW|mode))) { fprintf(stderr,"%s: Unable to open/read a hive, exiting..\n",argv[0]); exit(1); } switch(hive[no_hives]->type) { case HTYPE_SAM: H_SAM = no_hives; break; case HTYPE_SOFTWARE: H_SOF = no_hives; break; case HTYPE_SYSTEM: H_SYS = no_hives; break; case HTYPE_SECURITY: H_SEC = no_hives; break; } no_hives++; filename = argv[optind+no_hives]; } while (filename && *filename && no_hives < MAX_HIVES); if (dodebug) { debugit(hive[0]->buffer,hive[0]->size); } else { if (H_SAM != -1) max_sam_lock = sam_get_lockoutinfo(hive[H_SAM], 0); if (inter) { interactive(); } else if (edit) { regedit_interactive(hive, no_hives); } else if (list) { sam_list_users(hive[H_SAM], 1); } else if (who) { find_n_change(who); } } if (list != 1) { printf("\nHives that have changed:\n # Name\n"); for (il = 0; il < no_hives; il++) { if (hive[il]->state & HMODE_DIRTY) { if (!logchange) printf("%2d <%s>",il,hive[il]->filename); if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n"); printf("\n"); d = 1; } } if (d) { /* Only prompt user if logging of changed files has not been set */ /* Thus we assume confirmations are done externally if they ask for a list of changes */ if (!logchange) fmyinput("Write hive files? (y/n) [n] : ",iwho,3); if (*iwho == 'y' || logchange) { if (logchange) { ch = fopen("/tmp/changed","w"); } for (il = 0; il < no_hives; il++) { if (hive[il]->state & HMODE_DIRTY) { printf("%2d <%s> - ",il,hive[il]->filename); if (!writeHive(hive[il])) { printf("OK"); if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n"); printf("\n"); if (logchange) fprintf(ch,"%s ",hive[il]->filename); dd = 2; } } } if (logchange) { fprintf(ch,"\n"); fclose(ch); } } else { printf("Not written!\n\n"); } } else { printf("None!\n\n"); } } /* list only check */ return(dd); } chntpw-1.0/sam.h0000644000175000017500000002341712273223316011744 0ustar jfsjfs/* * sam.h - known structures in the SAM hive of NT registry * ***** * * NTREG - Window registry file reader / writer library * Copyright (c) 1997-2012 Petter Nordahl-Hagen. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * See file LGPL.txt for the full license. * */ #ifndef _INCLUDE_SAM_H #define _INCLUDE_SAM_H 1 #include #include /* Account V, contains machine SID and a lot of unknonw stuff */ #define SID_BIN_LEN 24 /* Lenght of binary machine SID, incl header */ #define SID_STR_MAX 50 /* Max string lenght of S-1-5-xxx-xxx-xxx-xxx type SID */ #define ACCOUNTDB_V_PATH "\\SAM\\Domains\\Account\\V" struct accountdb_V { char unknown1[0x38]; /* No idea what it does */ uint32_t sid_ofs; /* 38 - Offset to machine SID */ uint32_t sid_len; /* 3c - Lenght of sid (should be 0x14?) */ // char unknown2[0x4]; /* 40 */ /* Data start at 0x40 ?? */ char data[4]; }; /* This contains some policy settings for the account database */ #define ACCOUNTDB_F_PATH "\\SAM\\Domains\\Account\\F" struct accountdb_F { char unknown1[8]; /* 0 */ char unknown2[8]; /* 8 */ uint32_t updatecnt; /* 10 Number of times policy data updated */ char unknown3[4]; /* 14 */ char t_maxpwage[8]; /* 18 Maximum password age, GUI shows only days */ char t_minpwage[8]; /* 20 Minimum password age, GUI shows only days */ char unknown4[8]; /* 28 */ char t_lockdur[8]; /* 30 Account lockout duration, GUI shows minutes */ char t_lockrel[8]; /* 38 Release account lockout after, GUI show minutes */ char unknown5[8]; /* 40 */ uint32_t rid; /* 48 RID of user doing last edit? */ uint32_t flags; /* 4c Some flags & options, see below */ unsigned short minpwlen; /* 50 Minimum password lenght */ unsigned short numhist; /* 52 How many passwords to keep in history */ unsigned short locklimit; /*54 How many tries before lockout */ char unknown6[0x9a]; /* Rest is unknown */ }; /* Total size 0xF0 bytes, seems to be constant */ /* Known bits in flags field */ #define ACF_COMPLEX 0x0001 /* Pass must meet complexity req. */ #define ACF_REVERSIBLE 0x0010 /* Store password using reversible encryption */ /* This is users F value, contains account type & state etc */ #define USER_F_PATH "\\SAM\\Domains\\Account\\Users\\%08X\\F" struct user_F { char unknown1[8]; char t_lockout[8]; /* Time of lockout */ char unknown2[8]; char t_creation[8]; /* Time of account creation */ char unknown3[8]; char t_login[8]; /* Time of last login */ int32_t rid; char unknown4[4]; unsigned short ACB_bits; /* Account type and status flags */ char unknown5[6]; unsigned short failedcnt; /* Count of failed logins, if > than policy it is locked */ unsigned short logins; /* Total logins since creation */ char unknown6 [0xc]; }; #define ACB_DISABLED 0x0001 /* Act disabled */ #define ACB_HOMDIRREQ 0x0002 /* Home directory required */ #define ACB_PWNOTREQ 0x0004 /* User password not req */ #define ACB_TEMPDUP 0x0008 /* Temporary duplicate account?? */ #define ACB_NORMAL 0x0010 /* Normal user account */ #define ACB_MNS 0x0020 /* MNS logon user account */ #define ACB_DOMTRUST 0x0040 /* Interdomain trust account */ #define ACB_WSTRUST 0x0080 /* Workstation trust account */ #define ACB_SVRTRUST 0x0100 /* Server trust account */ #define ACB_PWNOEXP 0x0200 /* User password does not expire */ /* Seems not to be used on failed console logins at least */ #define ACB_AUTOLOCK 0x0400 /* Account auto locked */ /* Account Bits Fields strings (defined in libsam.c) char *acb_fields[16] = { "Disabled" , "Homedir req." , "Passwd not req." , "Temp. duplicate" , "Normal account" , "NMS account" , "Domain trust act." , "Wks trust act." , "Srv trust act" , "Pwd don't expire" , "Auto lockout" , "(unknown 0x08)" , "(unknown 0x10)" , "(unknown 0x20)" , "(unknown 0x40)" , "(unknown 0x80)" , }; */ /* Users V data struct */ /* First 0xCC bytes is pointer & len table, rest is data which * the table points to * String data is unicode, not zero terminated (must use len) */ struct user_V { int unknown1_1; /* 0x00 - always zero? */ int unknown1_2; /* 0x04 - points to username? */ int unknown1_3; /* 0x08 - always 0x02 0x00 0x01 0x00 ? */ int username_ofs; /* 0x0c */ int username_len; /* 0x10 */ int unknown2_1; /* 0x14 - always zero? */ int fullname_ofs; /* 0x18 */ int fullname_len; /* 0x1c */ int unknown3_1; /* 0x20 - always zero? */ int comment_ofs; /* 0x24 */ int comment_len; /* 0x28 */ int unknown4_1; /* 0x2c - alway zero? */ int unknown4_2; /* 0x30 - points 4 or 8 byte field before hashes */ int unknown4_3; /* 0x34 - zero? or size? */ int unknown4_4; /* 0x38 - zero? */ int unknown4_5; /* 0x3c - to field 8 bytes before hashes */ int unknown4_6; /* 0x40 - zero? or size of above? */ int unknown4_7; /* 0x44 - zero? */ int homedir_ofs; /* 0x48 */ int homedir_len; /* 0x4c */ int unknown5_1; /* 0x50 - zero? */ int drvletter_ofs; /* 0x54 - drive letter for home dir */ int drvletter_len; /* 0x58 - len of above, usually 4 */ int unknown6_1; /* 0x5c - zero? */ int logonscr_ofs; /* 0x60 - users logon script path */ int logonscr_len; /* 0x64 - length of string */ int unknown7_1; /* 0x68 - zero? */ int profilep_ofs; /* 0x6c - profile path string */ int profilep_len; /* 0x70 - profile path stringlen */ char unknown7[0x90-0x74]; /* 0x74 */ int unknown8_1; /* 0x90 - pointer to some place before hashes, after comments */ int unknown8_2; /* 0x94 - size of above? */ int unknown8_3; /* 0x98 - unknown? always 1? */ int lmpw_ofs; /* 0x9c */ int lmpw_len; /* 0xa0 */ int unknown9_1; /* 0xa4 - zero? */ int ntpw_ofs; /* 0xa8 */ int ntpw_len; /* 0xac */ int unknowna_1; /* 0xb0 */ int unknowna_2; /* 0xb4 - points to field after hashes */ int unknowna_3; /* 0xb8 - size of above field */ int unknowna_4; /* 0xbc - zero? */ int unknowna_5; /* 0xc0 - points to field after that */ int unknowna_6; /* 0xc4 - size of above */ int unknowna_7; /* 0xc8 - zero ? */ char data[4]; /* Data starts here. All pointers above is relative to this, that is V + 0xCC */ }; /* Groups C data struct * First 0x34 bytes is pointer & len table, rest is data which * the table points to * String data is unicode, not zero terminated (must use len) */ struct group_C { int groupid; /* 0x00 - Seems to be the group ID */ int unknown1_1; /* 0x04 - always zero? */ int unknown1_2; /* 0x08 - points to groupname? */ int unknown1_3; /* 0x0c - always 0x02 0x00 0x01 0x00 ? */ int grpname_ofs; /* 0x10 */ int grpname_len; /* 0x14 */ int unknown2_1; /* 0x18 - always zero? */ int fullname_ofs; /* 0x1c */ int fullname_len; /* 0x20 */ int unknown3_1; /* 0x24 - always zero? */ int members_ofs; /* 0x28 - offset to member list, which is SIDs */ int members_len; /* 0x2c - member list size */ int grp_members; /* 0x30 - number of group members */ /* 0x34 - data starts here. pointers above are offset from this */ char data[]; }; /* Variable length binary structure that most SIDs are stored in */ struct sid_binary { uint8_t revision; /* 0x0 - Don't know. Always 1? Revision level? number of SIDs following? */ uint8_t sections; /* 0x1 - Number of parts, 4 bytes each (unsigne integer) */ uint8_t unknown2; /* 0x2 - Unknown, seems to be null padding */ uint8_t unknown3; /* 0x3 - Unknown, seems to be null padding */ uint8_t unknown4; /* 0x4 - Unknown */ uint8_t unknown5; /* 0x5 - Unknown */ uint8_t unknown6; /* 0x6 - Unknown */ uint8_t authority; /* 0x7 - Unknown, often 5 (could this be 8bit authority number?) */ uint32_t array[8]; /* As many as sections value says it is */ }; /* Array of SIDs */ struct sid_array { int len; struct sid_binary *sidptr; }; /* libsam.c functions */ int sam_get_lockoutinfo(struct hive *hdesc, int show); short sam_handle_accountbits(struct hive *hdesc, int rid, int mode); int sam_get_machine_sid(struct hive *hdesc, char *sidbuf); char *sam_sid_to_string(struct sid_binary *sidbuf); struct sid_array *sam_make_sid_array(struct sid_binary *sidbuf, int size); void sam_free_sid_array(struct sid_array *array); int sam_sid_cmp(struct sid_binary *s1, struct sid_binary *s2); int sam_get_grp_members_sid(struct hive *hdesc, int grp, struct sid_array **sarray); int sam_put_grp_members_sid(struct hive *hdesc, int grp, struct sid_array *sarray); struct keyval *sam_get_user_grpids(struct hive *hdesc, int rid); int sam_put_user_grpids(struct hive *hdesc, int rid, struct keyval *val); int sam_add_user_to_grp(struct hive *hdesc, int rid, int grp); int sam_remove_user_from_grp(struct hive *hdesc, int rid, int grp); char *sam_get_username(struct hive *hdesc, int rid); char *sam_get_groupname(struct hive *hdesc, int grpid); int sam_list_users(struct hive *hdesc, int readable); int sam_list_user_groups(struct hive *hdesc, int rid, int check); int sam_reset_pw(struct hive *hdesc, int rid); void sam_list_groups(struct hive *hdesc, int listmembers, int human); int sam_reset_all_pw(struct hive *hdesc, int list); #endif chntpw-1.0/ntreg.h0000600000175000017500000004365112273223316012275 0ustar jfsjfs/* * ntreg.h - NT Registry Hive access library, constants & structures * * NOTE: defines are not frozen. It can and will change every release. * ***** * * NTREG - Window registry file reader / writer library * Copyright (c) 1997-2013 Petter Nordahl-Hagen. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * See file LGPL.txt for the full license. * */ #ifndef _INCLUDE_NTREG_H #define _INCLUDE_NTREG_H 1 #define SZ_MAX 4096 /* Max unicode strlen before we truncate */ #define KEY_ROOT 0x2c /* Type ID of ROOT key node */ #define KEY_NORMAL 0x20 /* Normal nk key */ #define ABSPATHLEN 4096 /* hbin page size. hbins are minimum this, and always multiple of this */ #define HBIN_PAGESIZE 0x1000 /* Hive filesize seems to always be multiple of this */ #define REGF_FILEDIVISOR 0x40000 /* Larger than this, and values seems split into several blocks */ #define VAL_DIRECT_LIMIT 0x3fd0 /* Datatypes of the values in the registry */ #define REG_NONE 0 /* No value type */ #define REG_SZ 1 /* Unicode nul terminated string */ #define REG_EXPAND_SZ 2 /* Unicode nul terminated string + env */ #define REG_BINARY 3 /* Free form binary */ #define REG_DWORD 4 /* 32-bit number */ #define REG_DWORD_BIG_ENDIAN 5 /* 32-bit number */ #define REG_LINK 6 /* Symbolic Link (unicode) */ #define REG_MULTI_SZ 7 /* Multiple Unicode strings */ #define REG_RESOURCE_LIST 8 /* Resource list in the resource map */ #define REG_FULL_RESOURCE_DESCRIPTOR 9 /* Resource list in the hardware description */ #define REG_RESOURCE_REQUIREMENTS_LIST 10 /* Uh? Rait.. */ #define REG_QWORD 11 /* Quad word 64 bit, little endian */ #define REG_MAX 12 /* The first page of the registry file is some kind of header, lot of * it's contents is unknown, and seems to be mostly NULLs anyway. * Note also, that this is the only place in the registry I've been * able to find _any_ kind of checksumming */ struct regf_header { int32_t id; /* 0x00000000 D-Word ID: ASCII-"regf" = 0x66676572 */ int32_t unknown1; /* 0x00000004 D-Word ???? Mount count */ int32_t unknown2; /* 0x00000008 D-Word ???? Always the same value as at 0x00000004 */ char timestamp[8]; /* 0x0000000C Q-Word last modify date in WinNT date-format */ int32_t unknown3; /* 0x00000014 D-Word 1 */ int32_t unknown4; /* 0x00000018 D-Word 3 - probably version #. 2 in NT3.51 */ int32_t unknown5; /* 0x0000001C D-Word 0 */ int32_t unknown6; /* 0x00000020 D-Word 1 */ int32_t ofs_rootkey; /* 0x00000024 D-Word Offset of 1st key record */ int32_t filesize; /* 0x00000028 D-Word Offset of first non-used data at end of file */ int32_t unknown7; /* 0x0000002C D-Word 1 */ char name[0x1fc-0x30]; /* 0x00000030 Seems like the hive's name is buried here, max len unknown */ int32_t checksum; /* 0x000001FC D-Word Xor sum of all D-Words from 0x00000000 to 0x000001FB */ }; /* The page header, I don't know if the 14 "dummy" bytes has a meaning, * they seem to be mostly NULLS */ struct hbin_page { int32_t id; /* 0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268 */ int32_t ofs_self; /* 0x0004 D-Word Offset to itself, could be for sanity check */ int32_t ofs_next; /* 0x0008 D-Word Relative offset to next hbin (practically length of this one) */ char dummy1[14]; /* 0x14 to 0x001b may be timestamp in some windows versions, at least in first hbin */ int32_t len_page; /* 0x001C D-Word Block-size??? Don't look like it. Most often zero. */ int32_t firstlink; /* 0x0020 First data block likage */ /* char data[1]; 0x0020 First data block starts here */ }; /* Minimum block size utilized at end of hbin * Make routines accept 0 size block when at end */ #define HBIN_ENDFILL 0 /* Security descriptor. I know how it's linked, but don't know how the real security data is constructed, it may as well be like the higher level security structs defined by MS in its includes & NT docs. Currently, I have no use for it. Note that keys sharing the exact same security settings will most likely point to the same security descriptor, thus saving space and making it fast to make objects inherit settings (is inheritance supported? they speak of security inheritance as a "new" feature in the filesystem on NT5, even though I think it was also supported by the lower levels in the earlier versions) */ struct sk_key { short id; /* 0x0000 Word ID: ASCII-"sk" = 0x6B73 */ short dummy1; /* 0x0002 Word Unused */ int32_t ofs_prevsk; /* 0x0004 D-Word Offset of previous "sk"-Record */ int32_t ofs_nextsk; /* 0x0008 D-Word Offset of next "sk"-Record */ int32_t no_usage; /* 0x000C D-Word usage-counter */ int32_t len_sk; /* 0x0010 D-Word Size of "sk"-record in bytes */ char data[4]; /* Security data up to len_sk bytes */ }; /* This is the subkeylist/hash structure. NT4.0+. * ID + count, then count number of offset/4byte "hash". (not true hash) * Probably changed from the 3.x version to make it faster to * traverse the registry if you're looking for a specific name * (saves lookups in 'nk's that have the first 4 name chars different) */ struct lf_key { short id; /* 0x0000 Word ID: ASCII-"lf" = 0x666C or "lh" = 0x686c */ short no_keys; /* 0x0002 Word number of keys */ /* 0x0004 ???? Hash-Records */ union { struct lf_hash { int32_t ofs_nk; /* 0x0000 D-Word Offset of corresponding "nk"-Record */ char name[4]; /* 0x0004 D-Word ASCII: the first 4 characters of the key-name, */ } hash[1]; /* WinXP uses a more real hash instead (base 37 of uppercase name chars) */ /* padded with 0's. Case sensitiv! */ struct lh_hash { int32_t ofs_nk; /* 0x0000 D-Word Offset of corresponding "nk"-Record */ int32_t hash; /* 0x0004 D-Word ASCII: the first 4 characters of the key-name, */ } lh_hash[1]; }; }; /* 3.x version of the above, contains only offset table, NOT * any start of names "hash". Thus needs 'nk' lookups for searches. */ struct li_key { short id; /* 0x0000 Word ID: ASCII-"li" = 0x696C */ short no_keys; /* 0x0002 Word number of keys */ /* 0x0004 ???? Hash-Records */ struct li_hash { int32_t ofs_nk; /* 0x0000 D-Word Offset of corresponding "nk"-Record */ } hash[1]; }; /* Indirect pointer list for value data, vk points to this instead of values data directly * Seems to be used when value data is large, maybe larger than 3-4k. */ struct db_key { short id; /* 0x0000 Word ID: ASCII-"li" = 0x6462 */ short no_part; /* 0x0002 Word number of data parts */ /* 0x0004 ???? Pointers to data */ int32_t ofs_data; /* 0x0000 D-Word Offset to list of data blocks */ /* Something else seems to follow here, 4 bytes at least */ /* and why not list the data block in here ???? why make another list?? */ }; /* This is a list of pointers to struct li_key, ie * an extention record if many li's. * This happens in NT4&5 when the lf hashlist grows larger * than about 400-500 entries/subkeys??, then the nk_key->ofs_lf points to this * instead of directly to an lf. * The sub-indices this points to seems to be li (yes!) in NT4 and 2k. * In XP and newer they point to lh which is more efficient. * Likely to happen in HKLM\Software\classes (file extention list) and * in SAM when many users. */ struct ri_key { short id; /* 0x0000 Word ID: ASCII-"ri" = 0x6972 */ short no_lis; /* 0x0002 Word number of pointers to li */ /* 0x0004 ???? Hash-Records */ struct ri_hash { int32_t ofs_li; /* 0x0000 D-Word Offset of corresponding "li"-Record */ } hash[1]; }; /* This is the value descriptor. * If the sign bit (31st bit) in the length field is set, the value is * stored inline this struct, and not in a seperate data chunk - * the data itself is then in the ofs_data field, happens for DWORD all the time * If the name size == 0, then the struct is probably cut short right * after the val_type or flag. * The flag meaning is rather unknown. */ struct vk_key { /* Offset Size Contents */ short id; /* 0x0000 Word ID: ASCII-"vk" = 0x6B76 */ short len_name; /* 0x0002 Word name length */ int32_t len_data; /* 0x0004 D-Word length of the data */ int32_t ofs_data; /* 0x0008 D-Word Offset of Data */ int32_t val_type; /* 0x000C D-Word Type of value */ short flag; /* 0x0010 Word Flag 0x1 ANSI encoding */ short dummy1; /* 0x0012 Word Unused (data-trash) */ char keyname[1]; /* 0x0014 ???? Name */ }; /* This is the key node (ie directory) descriptor, can contain subkeys and/or values. * Note that for values, the count is stored here, but for subkeys * there's a count both here and in the offset-table (lf or li struct). * What happens if these mismatch is not known. * What's the classname thingy? Can't remember seeing that used in * anything I've looked at. */ struct nk_key { /* Offset Size Contents */ short id; /* 0x0000 Word ID: ASCII-"nk" = 0x6B6E */ short type; /* 0x0002 Word for the root-key: 0x2C, otherwise 0x20 */ /* 0x20 seems a flag for ANSI encoding */ /* 0x1000 is used in some places in Vista and newer */ char timestamp[12]; /* 0x0004 Q-Word write-date/time in windows nt notation */ int32_t ofs_parent; /* 0x0010 D-Word Offset of Owner/Parent key */ int32_t no_subkeys; /* 0x0014 D-Word number of sub-Keys */ int32_t dummy1; int32_t ofs_lf; /* 0x001C D-Word Offset of the sub-key lf-Records */ int32_t dummy2; int32_t no_values; /* 0x0024 D-Word number of values */ int32_t ofs_vallist; /* 0x0028 D-Word Offset of the Value-List */ int32_t ofs_sk; /* 0x002C D-Word Offset of the sk-Record */ int32_t ofs_classnam; /* 0x0030 D-Word Offset of the Class-Name */ int32_t dummy3; /* 0x0034 unknown some of these may be used by vista */ int32_t dummy4; /* 0x0038 unknown and newer ?? */ int32_t dummy5; /* 0x003c unknown */ int32_t dummy6; /* 0x0040 unknown */ int32_t dummy7; /* 0x0044 unknown */ short len_name; /* 0x0048 Word name-length */ short len_classnam; /* 0x004A Word class-name length */ char keyname[1]; /* 0x004C ???? key-name */ }; /*********************************************************************************/ /* Structure defines for my routines */ struct ex_data { int nkoffs; struct nk_key *nk; char *name; }; struct vex_data { int vkoffs; struct vk_key *vk; int type; /* Value type REG_??? */ int size; /* Values size (normalized, inline accounted for) */ int val; /* Actual value itself if type==REG_DWORD */ char *name; }; struct keyval { int len; /* Length of databuffer */ int data; /* Data. Goes on for length of value */ }; struct keyvala { int len; /* Length of databuffer */ int data[1]; /* Data. Goes on for length of value */ }; /* Types to trav_path() */ #define TPF_NK 0 #define TPF_VK 1 #define TPF_ABS 64 #define TPF_EXACT 128 #define TPF_VK_SHORT 256 /* To get type field instead of data field, used in SAM */ #define TPF_NK_EXACT (TPF_NK | TPF_EXACT) #define TPF_VK_EXACT (TPF_VK | TPF_EXACT) #define TPF_VK_ABS (TPF_VK | TPF_ABS) /* Name is literal, not a path */ /* Hive open modes */ #define HMODE_RW 0 #define HMODE_RO 0x1 #define HMODE_OPEN 0x2 #define HMODE_DIRTY 0x4 #define HMODE_NOALLOC 0x8 /* Don't allocate new blocks */ #define HMODE_NOEXPAND 0x10 /* Don't expand file with new hbin */ #define HMODE_DIDEXPAND 0x20 /* File has been expanded */ #define HMODE_VERBOSE 0x1000 #define HMODE_TRACE 0x2000 #define HMODE_INFO 0x4000 /* Show some info on open and close */ /* Suggested type of hive loaded, guessed by library, but not used by it */ #define HTYPE_UNKNOWN 0 #define HTYPE_SAM 1 #define HTYPE_SYSTEM 2 #define HTYPE_SECURITY 3 #define HTYPE_SOFTWARE 4 /* Hive definition, allocated by openHive(), dealloc by closeHive() * contains state data, must be passed in all functions */ struct hive { char *filename; /* Hives filename */ int filedesc; /* File descriptor (only valid if state == OPEN) */ int state; /* Current state of hive */ int type; /* Suggested type of hive. NOTE: Library will guess when it loads it, but application may change it if needed */ int pages; /* Number of pages, total */ int useblk; /* Total # of used blocks */ int unuseblk; /* Total # of unused blocks */ int usetot; /* total # of bytes in useblk */ int unusetot; /* total # of bytes in unuseblk */ int size; /* Hives size (filesize) in bytes, incl regf header */ int rootofs; /* Offset of root-node */ int lastbin; /* Offset to last HBIN */ int endofs; /* Offset of first non HBIN page, we can expand from here */ short nkindextype; /* Subkey-indextype the root key uses */ char *buffer; /* Files raw contents */ }; /***************************************************/ /* Various nice macros */ #define CREATE(result, type, number)\ { \ if (!((result) = (type *) calloc ((number), sizeof(type)))) { \ perror("malloc failure"); \ abort() ; \ } \ } #define ALLOC(result, size, number)\ { \ if (!((result) = (void *) calloc ((number), (size)))) { \ perror("malloc failure"); \ abort() ; \ } \ } #define FREE(p) { if (p) { free(p); (p) = 0; } } /* Debug / verbosity message macro */ #define VERB(h, string) \ { \ if ((h)->state & HMODE_VERBOSE) printf((string)); \ } #define VERBF(h, ...) \ { \ if ((h)->state & HMODE_VERBOSE) printf(__VA_ARGS__); \ } /******* Function prototypes **********/ char *str_dup( const char *str ); char *str_cat(char *str, char *add); char *str_catf(char *str, const char *format, ... ); int fmyinput(char *prmpt, char *ibuf, int maxlen); void hexprnt(char *s, unsigned char *bytes, int len); void hexdump(char *hbuf, int start, int stop, int ascii); int find_in_buf(char *buf, char *what, int sz, int len, int start); int get_int( char *array ); void cheap_uni2ascii(char *src, char *dest, int l); void cheap_ascii2uni(char *src, char *dest, int l); void skipspace(char **c); int gethex(char **c); int gethexorstr(char **c, char *wb); int debugit(char *buf, int sz); struct keyval *reg_valcat(struct keyval *a, struct keyval *b); int parse_block(struct hive *hdesc, int vofs,int verbose); int ex_next_n(struct hive *hdesc, int nkofs, int *count, int *countri, struct ex_data *sptr); int ex_next_v(struct hive *hdesc, int nkofs, int *count, struct vex_data *sptr); int get_abs_path(struct hive *hdesc, int nkofs, char *path, int maxlen); int trav_path(struct hive *hdesc, int vofs, char *path, int type); int get_val_type(struct hive *hdesc, int vofs, char *path, int exact); int set_val_type(struct hive *hdesc, int vofs, char *path, int exact, int type); int get_val_len(struct hive *hdesc, int vofs, char *path, int exact); void *get_val_data(struct hive *hdesc, int vofs, char *path, int val_type, int exact); struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv, int vofs, char *path, int type, int exact ); int get_dword(struct hive *hdesc, int vofs, char *path, int exact); int put_buf2val(struct hive *hdesc, struct keyval *kv, int vofs, char *path, int type, int exact ); int put_dword(struct hive *hdesc, int vofs, char *path, int exact, int dword); void export_key(struct hive *hdesc, int nkofs, char *name, char *filename, char *prefix); void closeHive(struct hive *hdesc); int writeHive(struct hive *hdesc); struct hive *openHive(char *filename, int mode); void nk_ls(struct hive *hdesc, char *path, int vofs, int type); struct vk_key *add_value(struct hive *hdesc, int nkofs, char *name, int type); void del_allvalues(struct hive *hdesc, int nkofs); int del_value(struct hive *hdesc, int nkofs, char *name, int exact); struct nk_key *add_key(struct hive *hdesc, int nkofs, char *name); int del_key(struct hive *hdesc, int nkofs, char *name); void rdel_keys(struct hive *hdesc, char *path, int nkofs); struct keyval *get_class(struct hive *hdesc, int curnk, char *path); int add_bin(struct hive *hdesc, int size); void import_reg(struct hive *hdesc, char *filename, char *prefix); int de_escape(char *s, int wide); char *string_regw2prog(void *string, int len); /* From edlib.c */ void regedit_interactive(struct hive *hive[], int no_hives); void cat_dpi(struct hive *hdesc, int nkofs, char *path); #endif chntpw-1.0/README.txt0000600000175000017500000001532312273223316012476 0ustar jfsjfs The Offline Windows Password Editor (c) 1997-2014 Petter Nordahl-Hagen This is free software, licensed under the following: "ntreg" (the registry library) and "libsam" (SAM manipulation library, user, groups etc) is licensed under the GNU Lesser Public License. See LGPL.txt. "chntpw" (the password reset / registry editor frontend) "reged" (registry editor, export and import tool) "sampasswd" (password reset command line program) "samusrgrp" (user and group command line program) is licensed under the GNU General Public License, see GPL.txt. For manual to the different commands, see MANUAL.txt Also, all have some help built in, just use the -h option. See INSTALL.txt for compile instructions. Where to get more info: ----------------------- http://pogostick.net/~pnh/ntpasswd/ At that site there's a floppy and a bootable CD that use chntpw to access the NT/2k/XP/Vista/Win7/Win8 system it is booted on to edit password etc. The instructions below are for the standalone program itself, not the floppy. What does chntpw do? -------------------- This little program will enable you to view some information and change user passwords, change user/group memberships in a Windows (NT/XP/Vista/win7/win8) etc SAM userdatabase file. You do not need to know the old passwords. However, you need to get at the registry files some way or another yourself. In addition it contains a simple registry editor with full write support, and hex-editor which enables you to fiddle around with bits&bytes in the file as you wish yourself. Also have registry import or export ----------------------------------- "reged" is a program that can do import and export of .reg files into the registry hive (binary) files. Also has an editor, but still rudimentary text based command line type thing. And by popular request Even have programs that can be used in scripts! ----------------------------------------------- "sampasswd" can be used in scripts to get lists of users or reset passwords automatically "samusrgrp" can be used in scripts to list or change memberships in groups automatically. Why? ---- I often forget passwords. Especially on test installations (that I just _must_ have some stuff out of half a year later..) On most unix-based boxes you just boot the thingy off some kind of rescue bootmedia (cd/floppy etc), and simply edit the password file. On Windows however, as far as I know, there is no way except reinstalling the userdatabase, losing all users except admin. (ok, some companies let you pay lotsa $$$$$ for some rescue service..) (ok, from Windows Vista or something you can make a password reset file, but you have to remember to do that BEFORE you forget your password...) How? ---- Currently, this thing only runs under linux, but it may just happen to compile on other platforms, too. So, to set a new adminpassword on your Windows installation you either: 1) Take the harddrive and mount it on a linux-box or 2) Boot a "live" linux CD with full GUI (many available: Ubuntu, Knoppix and more. Search for them) In both those cases, use the "chntpw.static" program found in the "static" zip file on my website. or 3) Use my linux boot CD (or USB) at: http://pogostick.net/~pnh/ntpasswd/ Usage: ------ For manual to the different commands, see MANUAL.txt Also, all have some help built in, just use the -h option. Some old tech babble on how the password is stored -------------------------------------------------- (still mostly valid, but should be moved somewhere else than this file) A struct, called the V value of a key in the NT registry was suddenly somewhat documented through the pwdump utility included in the unix Samba distribution. This struct contains some info on a user of the NT machine, along with 2 crypted versions of the password associated with the account. One password is the NT console login password, the other the LANMAN network share password (which essentially is the first one in uppercase only, and no unicode) This is how NT encrypts the passwords: The logon cleartext password a user enters is: 1) Converted to unicode 2) A MD4 hash is made out of the unicode string 3) Then the hash is crypted with DES, using the RID (lower part of the SID, userid) as the crypt key. This is the so called "obfuscation" step, so it's not obvious on a hex dump of the file that two or more users have the same password. 4) The result of stage 3 (16 bytes) is put into the V struct. For the LANMAN password: 1) Uppercased (and illegal characters probably removed) 14 bytes max, if less the remaining bytes are zeroed. 2) A known (constant) string is DES-encrypted using 7 first characters of the password as the key. Another constant is encrypted using the last 7 chars as the key. The result of these two crypts are simply appended, resulting in a 16 byte string. 3) The same obfuscation DES stage as 3 above. 4) 16 bytes result put into the V struct. Since the number of possible combinations in the lanman password is relatively low compared to the other one, and it's easy to see if it's shorter than 8 chars or not it's used first in brute-force-crackers. This program, however, don't care at all what the old one is, it just overwrites it with the new one. Ok. So, how do we find and identify the V struct? Yeah.. that was the hard part.. The files structure is not documented (as far as I know..) But, with help from an unnamed German, and a lot of testing and guesswork from myself, it's now possible to follow the actual registry tree. (see source code for struct-defines and comments on the registry structure) The usernames are listed in: \SAM\Domains\Account\Users\Names\ [2d18] \SAM\Domains\Account\Users\Names> l ls of node at offset 0x2d1c Node has 4 subkeys and 1 values nk-offset name 0x003290 - 0x003630 - 0x001c88 - 0x003428 - Each name is a subkey, with one namless value containing the RID. [2d18] \SAM\Domains\Account\Users\Names> cd pnh [3428] \SAM\Domains\Account\Users\Names\pnh> l ls of node at offset 0x342c Node has 0 subkeys and 1 values vk-offs size type name 0x003688 0 (unknown) <> INLINE: val (in type field?): 1000 (0x3e8) To get the userinfo (V struct), access \SAM\Domains\Account\Users\\V [2c90] \SAM\Domains\Account\Users> l ls of node at offset 0x2c94 Node has 5 subkeys and 1 values nk-offset name 0x003320 - <000001F4> 0x0036b8 - <000001F5> 0x003550 - <000003E8> 0x001d00 - <000003E9> 0x002d18 - [2c90] \SAM\Domains\Account\Users> cd 000003E8 [3550] \SAM\Domains\Account\Users\000003E8> l ls of node at offset 0x3554 Node has 0 subkeys and 2 values vk-offs size type name 0x0035a8 80 REG_BINARY 0x003228 508 REG_BINARY For more techincal info, look it up in the source code. chntpw-1.0/chntpw-presplit.c0000600000175000017500000017616112273223316014317 0ustar jfsjfs/* * chntpw.c - Offline Password Edit Utility for Windows SAM database * * This program uses the "ntreg" library to load and access the registry, * it's main purpose is to reset password based information. * It can also call the registry editor etc * 2011-apr: Command line options added for hive expansion safe mode * 2010-jun: Syskey not visible in menu, but is selectable (2) * 2010-apr: Interactive menu adapts to show most relevant * selections based on what is loaded * 2008-mar: Minor other tweaks * 2008-mar: Interactive reg ed moved out of this file, into edlib.c * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev * http://bugs.gentoo.org/show_bug.cgi?id=185411 * 2007-sep: Group handling extended, promotion now public * 2007-sep: User edit menu, some changes to user info edit * 2007-apr-may: Get and display users group memberships * 2007-apr: GNU license. Some bugfixes. Cleaned up some output. * 2004-aug: More stuff in regedit. Stringinput bugfixes. * 2004-jan: Changed some of the verbose/debug stuff * 2003-jan: Changed to use more of struct based V + some small stuff * 2003-jan: Support in ntreg for adding keys etc. Editor updated. * 2002-dec: New option: Specify user using RID * 2002-dec: New option: blank the pass (zero hash lengths). * 2001-jul: extra blank password logic (when NT or LANMAN hash missing) * 2001-jan: patched & changed to use OpenSSL. Thanks to Denis Ducamp * 2000-jun: changing passwords regardless of syskey. * 2000-jun: syskey disable works on NT4. Not properly on NT5. * 2000-jan: Attempt to detect and disable syskey * 1999-feb: Now able to browse registry hives. (write support to come) * See HISTORY.txt for more detailed info on history. * ***** * * Copyright (c) 1997-2012 Petter Nordahl-Hagen. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See file GPL.txt for the full license. * ***** * * Information and ideas taken from pwdump by Jeremy Allison. * * More info from NTCrack by Jonathan Wilkins. * */ /* TODO: This is getting ugly. Most likely best to split up into different programs * and put commun stuff in library */ #include #include #include #include #include #include #include #include #include #include /* Define DOCRYPTO in makefile to include cryptostuff to be able to change passwords to * a new one. * Changing passwords is seems not to be working reliably on XP and newer anyway. * When not defined, only reset (nulling) of passwords available. */ #ifdef DOCRYPTO #include #include #endif #define uchar u_char #define MD4Init MD4_Init #define MD4Update MD4_Update #define MD4Final MD4_Final #include "ntreg.h" #include "sam.h" const char chntpw_version[] = "chntpw version 0.99.6 110511 , (c) Petter N Hagen"; extern char *val_types[REG_MAX+1]; /* Global verbosity */ int gverbose = 0; #define MAX_HIVES 10 /* Array of loaded hives */ struct hive *hive[MAX_HIVES+1]; int no_hives = 0; /* Icky icky... globals used to refer to hives, will be * set when loading, so that hives can be loaded in any order */ int H_SAM = -1; int H_SYS = -1; int H_SEC = -1; int H_SOF = -1; int syskeyreset = 0; int dirty = 0; int max_sam_lock = 0; /* * of user with RID 500, because silly MS decided * to localize the bloody admin-username!! AAAGHH! */ char admuser[129]="Administrator"; /* ============================================================== */ #ifdef DOCRYPTO /* Crypto-stuff & support for what we'll do in the V-value */ /* Zero out string for lanman passwd, then uppercase * the supplied password and put it in here */ void make_lanmpw(char *p, char *lm, int len) { int i; for (i=0; i < 15; i++) lm[i] = 0; for (i=0; i < len; i++) lm[i] = toupper(p[i]); } /* * Convert a 7 byte array into an 8 byte des key with odd parity. */ void str_to_key(unsigned char *str,unsigned char *key) { int i; key[0] = str[0]>>1; key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); key[7] = str[6]&0x7F; for (i=0;i<8;i++) { key[i] = (key[i]<<1); } DES_set_odd_parity((des_cblock *)key); } /* * Function to convert the RID to the first decrypt key. */ void sid_to_key1(uint32_t sid,unsigned char deskey[8]) { unsigned char s[7]; s[0] = (unsigned char)(sid & 0xFF); s[1] = (unsigned char)((sid>>8) & 0xFF); s[2] = (unsigned char)((sid>>16) & 0xFF); s[3] = (unsigned char)((sid>>24) & 0xFF); s[4] = s[0]; s[5] = s[1]; s[6] = s[2]; str_to_key(s,deskey); } /* * Function to convert the RID to the second decrypt key. */ void sid_to_key2(uint32_t sid,unsigned char deskey[8]) { unsigned char s[7]; s[0] = (unsigned char)((sid>>24) & 0xFF); s[1] = (unsigned char)(sid & 0xFF); s[2] = (unsigned char)((sid>>8) & 0xFF); s[3] = (unsigned char)((sid>>16) & 0xFF); s[4] = s[0]; s[5] = s[1]; s[6] = s[2]; str_to_key(s,deskey); } /* DES encrypt, for LANMAN */ void E1(uchar *k, uchar *d, uchar *out) { des_key_schedule ks; des_cblock deskey; str_to_key(k,(uchar *)deskey); #ifdef __FreeBSD__ des_set_key(&deskey,ks); #else /* __FreeBsd__ */ des_set_key((des_cblock *)deskey,ks); #endif /* __FreeBsd__ */ des_ecb_encrypt((des_cblock *)d,(des_cblock *)out, ks, DES_ENCRYPT); } #endif /* DOCRYPTO */ /* Get machines SID as binary (raw data) * str = pointer to buffer, first 20 bytes will be filled in * returns true if found, else 0 */ int get_machine_sid(char *sidbuf) { struct accountdb_V *v; struct keyval *kv; uint32_t ofs; uint32_t len; if (H_SAM >= 0) { /* Get accoundb V value */ kv = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_V_PATH, REG_BINARY, TPF_VK); if (!kv) { fprintf(stderr,"get_machine_sid: Machine SID not found in SAM\n"); return(0); } // hexdump(&(kv->data), 0, kv->len,1); v = (struct accountdb_V *)&kv->data; ofs = v->sid_ofs; len = v->sid_len + 4; ofs += 0x40; if (len != SID_BIN_LEN) { fprintf(stderr,"get_machine_sid: WARNING: SID found, but it has len=%d instead of expected %d bytes\n",len,SID_BIN_LEN); } // printf("get_machine_sid: adjusted ofs = %x, len = %x (%d)\n",ofs,len,len); memcpy(sidbuf, (char *)v+ofs, len); // hexdump(sidbuf, 0, len, 1); return(1); } return(0); } /* Make string out of SID, in S-1-5 authority (NT authority) * like S-1-5-21-516312364-151943033-2698651 * Will allocate return string (which can be of variable lenght) * NOTE: caller must free it * sidbuf = the SID binary data structure with it's type+counter first * * str = string buffer to fill, be sure to have at least space: * 6 chars athority prefix (S-1-5-) * 4 * 10 digits (the 4 32 bit groups) * 3 for the - between the groups * 1 for null termination * 50 chars */ char *sid_to_string(struct sid_binary *sidbuf) { int cnt, i; int *array; char *str = NULL; // hexdump(sidbuf, 0, 24, 1); array = (int *)&sidbuf->array; if (sidbuf->unknown0 != 1) { fprintf(stderr,"sid_to_string: DEBUG: first byte unexpected: %d\n",sidbuf->unknown0); } cnt = sidbuf->sections; // printf("sid_to_string: DEBUG: sections = %d\n",cnt); str = str_dup("S-1-5"); for (i = 0; i < cnt; i++) { str = str_catf(str,"-%u",sidbuf->array[i]); } // printf("sid_to_string: returning <%s>\n",str); return(str); } /* Check if hive is SAM, and if it is, extract some * global policy information from it, like lockout counts etc */ void check_get_samdata(int show) { struct accountdb_F *f; struct keyval *v; if (H_SAM >= 0) { /* Get accoundb F value */ v = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_F_PATH, REG_BINARY, TPF_VK); if (!v) { fprintf(stderr,"WARNING: Login counts data not found in SAM\n"); return; } f = (struct accountdb_F *)&v->data; max_sam_lock = f->locklimit; if (show) { printf("\n* SAM policy limits:\n"); printf("Failed logins before lockout is: %d\n",max_sam_lock); printf("Minimum password length : %d\n",f->minpwlen); printf("Password history count : %d\n",f->minpwlen); } } } /* Try to decode and possibly change account lockout etc * This is \SAM\Domains\Account\Users\\F * It's size seems to always be 0x50. * Params: RID - user ID, mode - 0 silent, 1 silent, 2 edit. * Returns: ACB bits with high bit set if lockout count is >0 */ short handle_F(int rid, int mode) { struct user_F *f; char s[200]; struct keyval *v; unsigned short acb; int b; if (H_SAM < 0) return(0); /* Get users F value */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\F",rid); v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT); if (!v) { printf("Cannot find value <%s>\n",s); return(0); } if (v->len < 0x48) { printf("handle_F: F value is 0x%x bytes, need >= 0x48, unable to check account flags!\n",v->len); FREE(v); return(0); } f = (struct user_F *)&v->data; acb = f->ACB_bits; if (mode == 1) { printf("Account bits: 0x%04x =\n",acb); for (b=0; b < 15; b++) { printf("[%s] %-15.15s | ", (acb & (1<failedcnt,max_sam_lock); printf("Total login count: %u\n",f->logins); } if (mode == 2) { acb |= ACB_PWNOEXP; acb &= ~ACB_DISABLED; acb &= ~ACB_AUTOLOCK; f->ACB_bits = acb; f->failedcnt = 0; put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY,TPF_VK_EXACT); printf("Unlocked!\n"); } return (acb | ( (f->failedcnt > 0 && f->failedcnt >= max_sam_lock)<<15 ) | (acb & ACB_AUTOLOCK)<<15 | (acb & ACB_DISABLED)<<15); } /* Stuff SID binary list into more easily handled arrays * sidbuf = binary list buffer (not changed, may point into value structure) * size = number of bytes of raw data * returns pointer to array, terminated with NULL pointer. * Keeps full binary data from each SID * All array space is allocated, call free_sid_array() to free it. */ struct sid_array *make_sid_array(struct sid_binary *sidbuf, int size) { int num = 0; int sidlen; struct sid_binary *sb; struct sid_array *array; CREATE(array, struct sid_array, 1); array[0].len = 0; array[0].sidptr = NULL; while (size > 0) { sidlen = sidbuf->sections * 4 + 8; // printf("make_sid_array: sidlen = %d\n",sidlen); ALLOC(sb, 1, sidlen); memcpy(sb, sidbuf, sidlen); array[num].len = sidlen; array[num].sidptr = sb; sidbuf = (void *)sidbuf + sidlen; size -= sidlen; num++; array = realloc(array, (num + 1) * sizeof(struct sid_array)); array[num].len = 0; array[num].sidptr = NULL; } return(array); } /* Free the sid array (from the function above) */ void free_sid_array(struct sid_array *array) { int num = 0; while (array[num].sidptr) { free(array[num].sidptr); num++; } free(array); } /* Compare two SIDs, and return like strcmp */ int sid_cmp(struct sid_binary *s1, struct sid_binary *s2) { int p; if (!s1 && !s2) return(0); if (!s1) return(-1); if (!s2) return(1); if (s1->sections < s2->sections) return(-1); /* s1 has shorter len, always smaller */ if (s1->sections > s2->sections) return(1); /* s1 has longer len, always larger */ /* Run compare since same length */ for (p = 0; p < s1->sections; p++) { if (s1->array[p] < s2->array[p]) return (-1); if (s1->array[p] > s2->array[p]) return (1); } /* At end. Thus equal */ return(0); } /* Get list of group members for a group * Will get the SID list (as binary) into a buffer that will be allocated * according to the neccessary size (based on member count) * NOTE: Caller must free the buffer when not needed any more * grp = group ID * sidarray = pointer to pointer to sid array which will be allocated * Returns number of members in the group */ int get_grp_members_sid(int grp, struct sid_array **sarray) { char g[200]; // char groupname[128]; struct sid_array *marray; struct keyval *c = NULL; struct group_C *cd; // int grpnamoffs, grpnamlen; int mofs, mlen; snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp); c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT); if (c) { cd = (struct group_C *)&c->data; // grpnamoffs = cd->grpname_ofs + 0x34; // grpnamlen = cd->grpname_len; // cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen); // printf("get_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members); mofs = cd->members_ofs; mlen = cd->members_len; // printf("get_grp_members_sid: mofs = %x, mlen = %x (%d)\n", mofs,mlen,mlen); // printf("get_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen); // hexdump(&c->data, 0, c->len, 1); // hexdump(&cd->data[mofs], 0, mlen, 1); marray = make_sid_array((struct sid_binary *)&cd->data[mofs], mlen); *sarray = marray; // free_sid_array(marray); free(c); } else { printf("Group info for %x not found!\n",grp); *sarray = NULL; return(0); } return(cd->grp_members); } /* Put list of group members back into group C structure * grp = group ID * sidarray = pointer to sid array * Returns true if success */ int put_grp_members_sid(int grp, struct sid_array *sarray) { char g[200]; char groupname[128]; struct keyval *c = NULL; struct group_C *cd; int grpnamoffs, grpnamlen; int mofs, mlen; int sidlen = 0; void *sidptr; int i; char *str; snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp); c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT); if (c) { cd = (struct group_C *)&c->data; grpnamoffs = cd->grpname_ofs + 0x34; grpnamlen = cd->grpname_len; cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen); if (gverbose) printf("put_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members); mofs = cd->members_ofs; mlen = cd->members_len; if (gverbose) printf("put_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen); if (gverbose) hexdump(&c->data, 0, c->len, 1); /* Get total size of new SID data */ for (i = 0; sarray[i].sidptr; i++) sidlen += sarray[i].len; if (gverbose) printf("put_grp_members_sid: new count : %d, new sidlen: %x\n",i,sidlen); /* Resize buffer with C structure */ c = realloc(c, 4 + mofs + sidlen + 0x34); /* offset of SIDs + sids lenght + pointer list at start */ c->len = 0x34 + mofs + sidlen; cd = (struct group_C *)&c->data; mofs = cd->members_ofs; sidptr = &cd->data[mofs]; for (i = 0; sarray[i].sidptr; i++) { if (gverbose) printf(" copying : %d len %x, at %x\n",i,sarray[i].len, sidptr); str = sid_to_string(sarray[i].sidptr); if (gverbose) printf(" Member # %d = <%s>\n", i, str); FREE(str); memcpy(sidptr, sarray[i].sidptr, sarray[i].len); sidptr += sarray[i].len; } cd->members_len = sidlen; /* Update member count in C struct */ cd->grp_members = i; if (gverbose) hexdump(&c->data, 0, c->len, 1); if (!put_buf2val(hive[H_SAM], c, 0, g, 0, TPF_VK_EXACT)) { fprintf(stderr,"put_grp_members_sid: could not write back group info in value %s\n",g); free(c); return(0); } free(c); } else { printf("Group info for %x not found!\n",grp); return(0); } return(1); } /* List groups, optionally with members */ void list_groups(int listmembers) { struct ex_data ex; struct sid_array *sids = NULL; int nkofs; unsigned int grp; int count = 0, countri = 0; struct keyval *c = NULL; struct group_C *cd; int grpnamoffs, grpnamlen, i; char groupname[200]; char *str; if (H_SAM < 0) return; nkofs = trav_path(hive[H_SAM], 0,"\\SAM\\Domains\\Builtin\\Aliases",0); if (!nkofs) { printf("list_groups: Cannot find group list in registry! (is this a SAM-hive?)\n"); return; } /* Pick up all subkeys here, they are local groups */ while ((ex_next_n(hive[H_SAM], nkofs+4, &count, &countri, &ex) > 0)) { // printf("Group ID %s\n",ex.name); sscanf(ex.name,"%x",&grp); /* Groups keys have a C value, get it and pick up the name etc */ /* Some other keys also exists (Members, Names at least), but we skip them */ c = get_val2buf(hive[H_SAM], NULL, ex.nkoffs+4, "C", 0, TPF_VK_EXACT); if (c) { cd = (struct group_C *)&c->data; grpnamoffs = cd->grpname_ofs + 0x34; grpnamlen = cd->grpname_len; cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen); printf("Group #%x named <%s> has %d members\n",grp,groupname,cd->grp_members); if (listmembers) { get_grp_members_sid(grp, &sids); for (i = 0; sids[i].sidptr; i++) { str = sid_to_string(sids[i].sidptr); printf(" Member # %d = <%s>\n", i, str); FREE(str); } free_sid_array(sids); } } } } /* Get group IDs a user is member of * rid = user ID * returns: since value data is just an array of grp ids (4 bytes each), * just return the keyval structure (size + data) * caller must free() keyval */ struct keyval *get_user_grpids(int rid) { char s[200]; struct sid_binary sid; char *sidstr; int nk = 0; struct keyval *m = NULL; int count = 0; int size; if (!rid || (H_SAM < 0)) return(NULL); if (!get_machine_sid((char *)&sid)) { fprintf(stderr,"get_user_grpids: Could not find machine SID\n"); return(0); } sidstr = sid_to_string(&sid); /* Get member list for user on this machine */ snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s\\%08X",sidstr,rid); free(sidstr); /* Now, the TYPE field is the number of groups the user is member of */ /* Don't we just love the inconsistent use of fields!! */ nk = trav_path(hive[H_SAM], 0, s, 0); if (!nk) { /* This probably means user is not in any group. Seems to be the case for a couple of XPs built in support / guest users. So just return */ if (gverbose) printf("get_user_grpids: Cannot find RID under computer SID <%s>\n",s); return(NULL); } nk += 4; count = get_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT); if (count == -1) { printf("get_user_grpids: Cannot find value <%s\\@>\n",s); return(NULL); } // printf("get_user_grpids: User is member of %d groups:\n",count); /* This is the data size */ size = get_val_len(hive[H_SAM],nk,"@",TPF_VK_EXACT); /* It should be 4 bytes for each group */ if (gverbose) printf("Data size %d bytes.\n",size); if (size != count * 4) { printf("get_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n"); } m = get_val2buf(hive[H_SAM], NULL, nk, "@", 0, TPF_VK_EXACT); if (!m) { printf("get_user_grpids: Could not get value data! Giving up.\n"); return(NULL); } return(m); } /* Put/set group IDs a user is member of * rid = user ID * val = keyval structure of data, actual value data is a list * of ints, one per group * returns true if successful setting the value */ int put_user_grpids(int rid, struct keyval *val) { char s[200]; struct sid_binary sid; char *sidstr; int newcount = 0; int nk = 0; int count = 0; if (!rid || (H_SAM < 0)) return(0); if (!val || !val->len) return(0); if (!get_machine_sid((char *)&sid)) { fprintf(stderr,"put_user_grpids: Could not find machine SID\n"); return(0); } sidstr = sid_to_string(&sid); /* Get member list for user on this machine */ snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s\\%08X",sidstr,rid); free(sidstr); /* Now, the TYPE field is the number of groups the user is member of */ nk = trav_path(hive[H_SAM], 0, s, 0); if (!nk) { /* This probably means user is not in any group. Seems to be the case for a couple of XPs built in support / guest users. So just return */ if (gverbose) printf("put_user_grpids: Cannot find RID under computer SID <%s>\n",s); return(0); } nk += 4; count = get_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT); if (count == -1) { printf("put_user_grpids: Cannot find value <%s\\@>\n",s); return(1); } if (gverbose) printf("put_user_grpids: User was member of %d groups:\n",count); /* This is the data size */ /* It should be 4 bytes for each group */ newcount = val->len >> 2; if (gverbose) printf("Data size %d bytes.\n",val->len); if (val->len != newcount << 2) { printf("set_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n"); } if (gverbose) printf("put_user_grpids: User is NOW member of %d groups:\n",newcount); set_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT,newcount); if (!put_buf2val(hive[H_SAM], val, nk, "@", 0, TPF_VK_EXACT) ) { printf("put_user_grpids: Could not set reg value data!\n"); return(0); } return(1); } /* List users membership or check if admin (is in admin group) * rid - users rid * check - if 1 just check if admin, do not list * returns true if user is admin */ int list_user_groups(int rid, int check) { char g[200]; char groupname[128]; struct keyval *m = NULL, *c = NULL; struct group_C *cd; unsigned int *grps; int count = 0, isadmin = 0; int i, grp, grpnamoffs, grpnamlen; if (!rid || (H_SAM < 0)) return(0); m = get_user_grpids(rid); if (!m) return(0); grps = (unsigned int *)&m->data; count = m->len >> 2; for (i = 0; i < count; i++) { grp = grps[i]; if (!check) printf("%08x ",grp); if (grp == 0x220) isadmin = 1; if (!check) { snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp); c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT); if (c) { cd = (struct group_C *)&c->data; grpnamoffs = cd->grpname_ofs + 0x34; grpnamlen = cd->grpname_len; cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen); printf("= %s (which has %d members)\n",groupname,cd->grp_members); // get_grp_members_sid(grp, &sidbuf); } else { printf("Group info for %x not found!\n",grp); } } } free(m); return(isadmin); } /* Add user to a group * rid = user RID * grp = group ID * return true if success */ int add_user_to_grp(int rid, int grp) { struct keyvala *usrgrplist, *newusrgrplist; struct sid_array *sarray, *narray; struct sid_binary *usid; struct sid_binary msid; int members, newmembers; char *str; int ugcnt; int o,n,hit,c; unsigned int *og, *ng; if (!rid || !grp || (H_SAM < 0)) return(0); /* Build user SID (add RID to machine SID) */ if (!get_machine_sid((char *)&msid)) { fprintf(stderr,"get_user_grpids: Could not find machine SID\n"); return(0); } /* well, and hope that machine SID is always same size here too */ ALLOC(usid, sizeof(struct sid_binary) +4, 1); memcpy(usid, &msid, sizeof(struct sid_binary)); usid->array[4] = rid; /* Tack RID on at end */ usid->sections = 5; str = sid_to_string(usid); if (gverbose) printf("add_user_to_grp: user SID is <%s>\n", str); free(str); /* With all of the above functions, it should now just be to get * the list of groups the user account has listed under it * and the list of users the group has listed under it */ usrgrplist = (struct keyvala *)get_user_grpids(rid); if (!usrgrplist) { printf("add_user_to_grp: user # %x not found!\n",rid); return(0); } members = get_grp_members_sid(grp, &sarray); if (!sarray) { printf("add_user_to_grp: group # %x not found!\n",grp); FREE(usrgrplist); return(0); } /* Add the group to the users list of groups it is member of */ ugcnt = usrgrplist->len >> 2; /* Count of groups already on user */ /* Allocate new larger usrgrplist for one more entry */ ALLOC(newusrgrplist, usrgrplist->len + 4 + 4, 1); bzero(newusrgrplist, usrgrplist->len + 4 + 4); /* for sanity.. */ newusrgrplist->len = usrgrplist->len + 4; og = (unsigned int *)&usrgrplist->data; ng = (unsigned int *)&newusrgrplist->data; if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len); #if 0 /* If list should be sorted, but seems windows does not do that? */ /* Copy over users group list, adding in where needed */ hit = 0; for (o = 0, n = 0; o < ugcnt; o++, n++) { printf(":: %d %d : %x\n",o,n,og[o]); if (og[o] == grp) { /* Was already in there, so just don't increase size.. */ newusrgrplist->len = usrgrplist->len; hit = 1; } if (og[o] > grp && !hit) { ng[n++] = grp; /* Next is higher, so insert out rid */ hit = 1; printf(" -- insert\n"); } ng[n] = og[o]; } printf("n = %d\n",n); if (!hit) ng[n] = grp; /* Insert at end if we run down */ #endif /* Copy over users group list, checking if already there */ hit = 0; for (o = 0; o < ugcnt; o++) { if (gverbose) printf(":: %d : %x\n",o,og[o]); if (og[o] == grp) { /* Was already in there, so just don't increase size.. */ newusrgrplist->len = usrgrplist->len; hit = 1; if (gverbose) printf(" -- match\n"); } ng[o] = og[o]; } if (gverbose) printf(" - end of list at o = %d\n",o); if (!hit) ng[o] = grp; /* Just stuff new group in at end if not already in list */ if (gverbose) { for (o = 0; o < (newusrgrplist->len >> 2); o++) { printf("grp index %d = %08x\n", o, ng[o]); } } /* And then we add the user SID into the groups list of members */ if (gverbose) { printf("add_user_to_grp: grp memberlist BEFORE:\n"); for (o = 0; sarray[o].sidptr; o++) { str = sid_to_string(sarray[o].sidptr); printf(" Member # %d = <%s>\n", o, str); FREE(str); } } newmembers = members + 1; ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1); /* Add one entry size */ if (gverbose) printf("members = %d\n", members); hit = 0; for (o = 0, n = 0; o <= members; o++, n++) { c = sid_cmp(sarray[o].sidptr, usid); /* Compare slot with new SID */ if (gverbose) printf("sid_cmp returns %d\n",c); if (c == 0) { newmembers--; /* Already there, don't change anything */ hit = 1; } if (!hit && ((c > 0) || !sarray[o].sidptr)) { /* Next is higher, insert new SID */ if (gverbose) printf(" -- add\n"); narray[n].len = usid->sections * 4 + 8; /* Hmm */ narray[n].sidptr = usid; n++; hit = 1; } narray[n].len = sarray[o].len; narray[n].sidptr = sarray[o].sidptr; } if (gverbose) { printf("add_user_to_grp: grp memberlist AFTER:\n"); for (o = 0; narray[o].sidptr; o++) { str = sid_to_string(narray[o].sidptr); printf(" Member # %d = <%s>\n", o, str); FREE(str); } } /* Write new lists back to registry */ if (!put_user_grpids(rid, (struct keyval *)newusrgrplist)) { fprintf(stderr, "add_user_to_grp: failed storing users group list\n"); } else if (!put_grp_members_sid(grp, narray)) { fprintf(stderr,"add_user_to_grp: failed storing groups user list\n"); put_user_grpids(rid, (struct keyval *)usrgrplist); /* Try to roll back */ } FREE(usrgrplist); FREE(newusrgrplist); free_sid_array(narray); FREE(sarray); /* Pointers was copied to narray, and freed above, just free the array here */ return(1); } /* Remove user from a group * rid = user RID * grp = group ID * return true if success */ int remove_user_from_grp(int rid, int grp) { struct keyvala *usrgrplist, *newusrgrplist; struct sid_array *sarray, *narray; struct sid_binary *usid; struct sid_binary msid; int members, newmembers; char *str; int ugcnt; int o,n,hit,c; unsigned int *og, *ng; if (!rid || !grp || (H_SAM < 0)) return(0); /* Build user SID (add RID to machine SID) */ if (!get_machine_sid((char *)&msid)) { fprintf(stderr,"get_user_grpids: Could not find machine SID\n"); return(0); } /* well, and hope that machine SID is always same size here too */ ALLOC(usid, sizeof(struct sid_binary) +4, 1); memcpy(usid, &msid, sizeof(struct sid_binary)); usid->array[4] = rid; /* Tack RID on at end */ usid->sections = 5; if (gverbose) { str = sid_to_string(usid); printf("remove_user_from_grp: user SID is <%s>\n", str); free(str); } /* With all of the above functions, it should now just be to get * the list of groups the user account has listed under it * and the list of users the group has listed under it */ usrgrplist = (struct keyvala *)get_user_grpids(rid); if (!usrgrplist) { printf("remove_user_from_grp: user # %x not found!\n",rid); return(0); } members = get_grp_members_sid(grp, &sarray); if (!sarray) { printf("remove_user_from_grp: group # %x not found!\n",grp); FREE(usrgrplist); return(0); } /* Add the group to the users list of groups it is member of */ ugcnt = usrgrplist->len >> 2; /* Count of groups already on user */ /* Allocate same size usrgrplist, since we don't know if we are in there and need to be removed */ ALLOC(newusrgrplist, usrgrplist->len + 4, 1); bzero(newusrgrplist, usrgrplist->len + 4); /* for sanity.. */ newusrgrplist->len = usrgrplist->len; og = (unsigned int *)&usrgrplist->data; ng = (unsigned int *)&newusrgrplist->data; if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len); /* Copy over users group list, if relevant group found, don't copy it over */ hit = 0; for (o = 0; o < ugcnt; o++) { if (gverbose) printf(":: %d : %x\n",o,og[o]); if (og[o] == grp) { /* Group found */ hit = 1; if (gverbose) printf(" -- match\n"); } else { ng[o-hit] = og[o]; } } if (gverbose) printf(" - end of list at o = %d\n",o); if (hit) { newusrgrplist->len -= 4; /* Decrease size if found */ } else { fprintf(stderr, "remove_user_from_grp: NOTE: group not in users list of groups, may mean user not member at all. Safe. Continuing.\n"); } if (gverbose) { for (o = 0; o < (newusrgrplist->len >> 2); o++) { printf("grp index %d = %08x\n", o, ng[o]); } /* Remove the user SID from the groups list of members */ printf("remove_user_from_grp: grp memberlist BEFORE:\n"); for (o = 0; sarray[o].sidptr; o++) { str = sid_to_string(sarray[o].sidptr); printf(" Member # %d = <%s>\n", o, str); FREE(str); } } newmembers = members; ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1); if (gverbose) printf("members = %d\n", members); hit = 0; for (o = 0, n = 0; o <= members; o++, n++) { c = sid_cmp(sarray[o].sidptr, usid); /* Compare slot with new SID */ if (gverbose) printf("sid_cmp returns %d\n",c); if (c == 0) { newmembers--; /* Found, skip copy and decrease list size */ hit = 1; n--; } else { narray[n].len = sarray[o].len; /* Copy entry */ narray[n].sidptr = sarray[o].sidptr; } } 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"); if (gverbose) { printf("remove_user_from_grp: grp memberlist AFTER:\n"); for (o = 0; narray[o].sidptr; o++) { str = sid_to_string(narray[o].sidptr); printf(" Member # %d = <%s>\n", o, str); FREE(str); } } /* Write new lists back to registry */ if (!put_user_grpids(rid, (struct keyval *)newusrgrplist)) { fprintf(stderr, "remove_user_from_grp: failed storing users group list\n"); } else if (!put_grp_members_sid(grp, narray)) { fprintf(stderr,"remvoe_user_from_grp: failed storing groups user list\n"); put_user_grpids(rid, (struct keyval *)usrgrplist); /* Try to roll back */ } FREE(usrgrplist); FREE(newusrgrplist); free_sid_array(narray); FREE(sarray); /* Pointers was copied to narray, and freed above, just free the array here */ return(1); } /* Promote user into administrators group (group ID 0x220) * And remove from all others... * rid - users rid * no returns yet */ void promote_user(int rid) { char yn[5]; if (!rid || (H_SAM < 0)) return; printf("\n=== PROMOTE USER\n\n"); printf("Will add the user to the administrator group (0x220)\n" "and to the users group (0x221). That should usually be\n" "what is needed to log in and get administrator rights.\n" "Also, remove the user from the guest group (0x222), since\n" "it may forbid logins.\n\n"); printf("(To add or remove user from other groups, please other menu selections)\n\n"); printf("Note: You may get some errors if the user is already member of some\n" "of these groups, but that is no problem.\n\n"); fmyinput("Do it? (y/n) [n] : ", yn, 3); if (*yn == 'y') { printf("* Adding to 0x220 (Administrators) ...\n"); add_user_to_grp(rid, 0x220); printf("* Adding to 0x221 (Users) ...\n"); add_user_to_grp(rid, 0x221); printf("* Removing from 0x222 (Guests) ...\n"); remove_user_from_grp(rid, 0x222); printf("\nPromotion DONE!\n"); } else { printf("Nothing done, going back..\n"); } } void interactive_remusrgrp(int rid) { char inp[20]; int grp, l; printf("\n=== REMOVE USER FROM A GROUP\n"); list_user_groups(rid,0); printf("\nPlease enter group number (for example 220), or 0 to go back\n"); l = fmyinput("Group number? : ",inp,16); sscanf(inp, "%x", &grp); if (!grp) { printf("Going back..\n"); return; } printf("Removing user from group 0x%x (%d)\n",grp,grp); printf("Error messages if the user was not member of the group are harmless\n\n"); remove_user_from_grp(rid, grp); printf("\nFinished removing user from group\n\n"); } void interactive_addusrgrp(int rid) { char inp[20]; int grp, l; printf("\n == ADD USER TO A GROUP\n"); list_groups(0); printf("\nPlease enter group number (for example 220), or 0 to go back\n"); l = fmyinput("Group number? : ",inp,16); sscanf(inp, "%x", &grp); if (!grp) { printf("Going back..\n"); return; } printf("Adding user to group 0x%x (%d)\n",grp,grp); printf("Error messages if the user was already member of the group are harmless\n\n"); add_user_to_grp(rid, grp); printf("\nFinished adding user to group\n\n"); } /* Decode the V-struct, and change the password * vofs - offset into SAM buffer, start of V struct * rid - the users RID, required for the DES decrypt stage * * Some of this is ripped & modified from pwdump by Jeremy Allison * */ char *change_pw(char *buf, int rid, int vlen, int stat) { int pl; char *vp; static char username[128],fullname[128]; char comment[128], homedir[128], newp[20]; int username_offset,username_len; int fullname_offset,fullname_len; int comment_offset,comment_len; int homedir_offset,homedir_len; int ntpw_len,lmpw_len,ntpw_offs,lmpw_offs; int dontchange = 0; unsigned short acb; struct user_V *v; #ifdef DOCRYPT int i; char md4[32],lanman[32]; char newunipw[34], despw[20], newlanpw[16], newlandes[20]; des_key_schedule ks1, ks2; des_cblock deskey1, deskey2; MD4_CTX context; unsigned char digest[16]; uchar x1[] = {0x4B,0x47,0x53,0x21,0x40,0x23,0x24,0x25}; #endif v = (struct user_V *)buf; vp = buf; username_offset = v->username_ofs; username_len = v->username_len; fullname_offset = v->fullname_ofs; fullname_len = v->fullname_len; comment_offset = v->comment_ofs; comment_len = v->comment_len; homedir_offset = v->homedir_ofs; homedir_len = v->homedir_len; lmpw_offs = v->lmpw_ofs; lmpw_len = v->lmpw_len; ntpw_offs = v->ntpw_ofs; ntpw_len = v->ntpw_len; if (!rid) { printf("No RID given. Unable to change passwords..\n"); return(0); } if (gverbose) { printf("lmpw_offs: 0x%x, lmpw_len: %d (0x%x)\n",lmpw_offs,lmpw_len,lmpw_len); printf("ntpw_offs: 0x%x, ntpw_len: %d (0x%x)\n",ntpw_offs,ntpw_len,ntpw_len); } *username = 0; *fullname = 0; *comment = 0; *homedir = 0; if(username_len <= 0 || username_len > vlen || username_offset <= 0 || username_offset >= vlen || comment_len < 0 || comment_len > vlen || fullname_len < 0 || fullname_len > vlen || homedir_offset < 0 || homedir_offset >= vlen || comment_offset < 0 || comment_offset >= vlen || lmpw_offs < 0 || lmpw_offs >= vlen) { if (stat != 1) printf("change_pw: Not a legal V struct? (negative struct lengths)\n"); return(NULL); } /* Offsets in top of struct is relative to end of pointers, adjust */ username_offset += 0xCC; fullname_offset += 0xCC; comment_offset += 0xCC; homedir_offset += 0xCC; ntpw_offs += 0xCC; lmpw_offs += 0xCC; cheap_uni2ascii(vp + username_offset,username,username_len); cheap_uni2ascii(vp + fullname_offset,fullname,fullname_len); cheap_uni2ascii(vp + comment_offset,comment,comment_len); cheap_uni2ascii(vp + homedir_offset,homedir,homedir_len); #if 0 /* Reset hash-lengths to 16 if syskey has been reset */ if (syskeyreset && ntpw_len > 16 && !stat) { ntpw_len = 16; lmpw_len = 16; ntpw_offs -= 4; (unsigned int)*(vp+0xa8) = ntpw_offs - 0xcc; *(vp + 0xa0) = 16; *(vp + 0xac) = 16; } #endif printf("\nRID : %04d [%04x]\n",rid,rid); printf("Username: %s\n",username); printf("fullname: %s\n",fullname); printf("comment : %s\n",comment); printf("homedir : %s\n\n",homedir); list_user_groups(rid,0); printf("\n"); acb = handle_F(rid,1); if (lmpw_len < 16 && gverbose) { printf("** LANMAN password not set. User MAY have a blank password.\n** Usually safe to continue. Normal in Vista\n"); } if (ntpw_len < 16) { printf("** No NT MD4 hash found. This user probably has a BLANK password!\n"); if (lmpw_len < 16) { printf("** No LANMAN hash found either. Try login with no password!\n"); dontchange = 1; } else { printf("** LANMAN password IS however set. Will now install new password as NT pass instead.\n"); printf("** NOTE: Continue at own risk!\n"); ntpw_offs = lmpw_offs; *(vp+0xa8) = ntpw_offs - 0xcc; ntpw_len = 16; lmpw_len = 0; } } if (gverbose) { hexprnt("Crypted NT pw: ",(unsigned char *)(vp+ntpw_offs),16); hexprnt("Crypted LM pw: ",(unsigned char *)(vp+lmpw_offs),16); } #ifdef DOCRYPTO /* Get the two decrpt keys. */ sid_to_key1(rid,(unsigned char *)deskey1); des_set_key((des_cblock *)deskey1,ks1); sid_to_key2(rid,(unsigned char *)deskey2); des_set_key((des_cblock *)deskey2,ks2); /* Decrypt the NT md4 password hash as two 8 byte blocks. */ des_ecb_encrypt((des_cblock *)(vp+ntpw_offs ), (des_cblock *)md4, ks1, DES_DECRYPT); des_ecb_encrypt((des_cblock *)(vp+ntpw_offs + 8), (des_cblock *)&md4[8], ks2, DES_DECRYPT); /* Decrypt the lanman password hash as two 8 byte blocks. */ des_ecb_encrypt((des_cblock *)(vp+lmpw_offs), (des_cblock *)lanman, ks1, DES_DECRYPT); des_ecb_encrypt((des_cblock *)(vp+lmpw_offs + 8), (des_cblock *)&lanman[8], ks2, DES_DECRYPT); if (gverbose) { hexprnt("MD4 hash : ",(unsigned char *)md4,16); hexprnt("LANMAN hash : ",(unsigned char *)lanman,16); } #endif /* DOCRYPTO */ printf("\n- - - - User Edit Menu:\n"); printf(" 1 - Clear (blank) user password\n"); printf("%s2 - Unlock and enable user account%s\n", (acb & 0x8000) ? " " : "(", (acb & 0x8000) ? " [probably locked now]" : ") [seems unlocked already]"); printf(" 3 - Promote user (make user an administrator)\n"); printf(" 4 - Add user to a group\n"); printf(" 5 - Remove user from a group\n"); #ifdef DOCRYPTO printf(" 9 - Edit (set new) user password (careful with this on XP or Vista)\n"); #endif printf(" q - Quit editing user, back to user select\n"); pl = fmyinput("Select: [q] > ",newp,16); if ( (pl < 1) || (*newp == 'q') || (*newp == 'Q')) return(0); if (*newp == '2') { acb = handle_F(rid,2); return(username); } if (*newp == '3') { promote_user(rid); return(username); } if (*newp == '4') { interactive_addusrgrp(rid); return(username); } if (*newp == '5') { interactive_remusrgrp(rid); return(username); } #ifdef DOCRYPT if (*newp == '9') { /* Set new password */ if (dontchange) { printf("Sorry, unable to edit since password seems blank already (thus no space for it)\n"); return(0); } pl = fmyinput("New Password: ",newp,16); if (pl < 1) { printf("No change.\n"); return(0); } cheap_ascii2uni(newp,newunipw,pl); make_lanmpw(newp,newlanpw,pl); /* printf("Ucase Lanman: %s\n",newlanpw); */ MD4Init (&context); MD4Update (&context, newunipw, pl<<1); MD4Final (digest, &context); if (gverbose) hexprnt("\nNEW MD4 hash : ",digest,16); E1((uchar *)newlanpw, x1, (uchar *)lanman); E1((uchar *)newlanpw+7, x1, (uchar *)lanman+8); if (gverbose) hexprnt("NEW LANMAN hash : ",(unsigned char *)lanman,16); /* Encrypt the NT md4 password hash as two 8 byte blocks. */ des_ecb_encrypt((des_cblock *)digest, (des_cblock *)despw, ks1, DES_ENCRYPT); des_ecb_encrypt((des_cblock *)(digest+8), (des_cblock *)&despw[8], ks2, DES_ENCRYPT); des_ecb_encrypt((des_cblock *)lanman, (des_cblock *)newlandes, ks1, DES_ENCRYPT); des_ecb_encrypt((des_cblock *)(lanman+8), (des_cblock *)&newlandes[8], ks2, DES_ENCRYPT); if (gverbose) { hexprnt("NEW DES crypt : ",(unsigned char *)despw,16); hexprnt("NEW LANMAN crypt: ",(unsigned char *)newlandes,16); } /* Reset hash length to 16 if syskey enabled, this will cause * a conversion to syskey-hashes upon next boot */ if (syskeyreset && ntpw_len > 16) { ntpw_len = 16; lmpw_len = 16; ntpw_offs -= 4; *(vp+0xa8) = (unsigned int)(ntpw_offs - 0xcc); *(vp + 0xa0) = 16; *(vp + 0xac) = 16; } for (i = 0; i < 16; i++) { *(vp+ntpw_offs+i) = (unsigned char)despw[i]; if (lmpw_len >= 16) *(vp+lmpw_offs+i) = (unsigned char)newlandes[i]; } printf("Password changed!\n"); } /* new password */ #endif /* DOCRYPT */ if (pl == 1 && *newp == '1') { /* Setting hash lengths to zero seems to make NT think it is blank * However, since we cant cut the previous hash bytes out of the V value * due to missing resize-support of values, it may leak about 40 bytes * each time we do this. */ v->ntpw_len = 0; v->lmpw_len = 0; printf("Password cleared!\n"); } #if 0 hexprnt("Pw in buffer: ",(vp+ntpw_offs),16); hexprnt("Lm in buffer: ",(vp+lmpw_offs),16); #endif dirty = 1; return(username); } /* Registry edit wrapper */ void mainloop(void) { regedit_interactive(hive, no_hives); } /* Iterate over users in SAM file, and do things with it * automode - if null, just list, else handle auto change * f = reset first user that is in admin group * a = reset all users in admin group * 0x1f4 (built-in administrator account) will only be reset * if no other users are found to be admin group */ int list_users(int readable) { char s[200]; struct keyval *v; int nkofs /* ,vkofs */ ; int rid; int count = 0, countri = 0; int ntpw_len; unsigned short acb; struct user_V *vpwd; struct ex_data ex; if (H_SAM < 0) return(1); nkofs = trav_path(hive[H_SAM], 0,"\\SAM\\Domains\\Account\\Users\\Names\\",0); if (!nkofs) { printf("list_users: Cannot find usernames in registry! (is this a SAM-hive?)\n"); return(1); } if (readable) printf("| RID -|---------- Username ------------| Admin? |- Lock? --|\n"); while ((ex_next_n(hive[H_SAM], nkofs+4, &count, &countri, &ex) > 0)) { /* Extract the value out of the username-key, value is RID */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",ex.name); rid = get_dword(hive[H_SAM], 0, s, TPF_VK_EXACT|TPF_VK_SHORT); if (rid == 500) strncpy(admuser,ex.name,128); /* Copy out admin-name */ /* Now that we have the RID, build the path to, and get the V-value */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid); v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT); if (!v) { printf("Cannot find value <%s>\n",s); return(1); } if (v->len < 0xcc) { printf("list_users: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n", s, v->len); } else { vpwd = (struct user_V *)&(v->data); ntpw_len = vpwd->ntpw_len; acb = handle_F(rid,0); if (readable) { printf("| %04x | %-30.30s | %-6s | %-8s |\n", rid, ex.name, (list_user_groups(rid,1) ? "ADMIN" : "") , ( acb & 0x8000 ? "dis/lock" : (ntpw_len < 16) ? "*BLANK*" : "") ); } else { printf("%04x:%s:%d:%x:%x\n", rid, ex.name, list_user_groups(rid,1) , acb, ntpw_len ); } // change_pw( (char *)&v->data , rid, v->len, (*automode == 'l') ? 2 : 1); } FREE(v); FREE(ex.name); } return(0); } /* Find a username in the SAM registry, then get it's V-value, * and feed it to the password changer. */ void find_n_change(char *username) { char s[200]; struct keyval *v; int rid = 0; if ((H_SAM < 0) || (!username)) return; if (*username == '0' && *(username+1) == 'x') sscanf(username,"%i",&rid); if (!rid) { /* Look up username */ /* Extract the unnamed value out of the username-key, value is RID */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",username); rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT); if (rid == -1) { printf("Cannot find value <%s>\n",s); return; } } /* printf("Username: %s, RID = %d (0x%0x)\n",username,rid,rid); */ /* Now that we have the RID, build the path to, and get the V-value */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid); v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT); if (!v) { printf("Cannot find value <%s>\n",s); return; } if (v->len < 0xcc) { printf("Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n", s, v->len); } else { change_pw( (char *)&v->data , rid, v->len, 0); if (dirty) { if (!(put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY, TPF_VK_EXACT))) { printf("Failed to write updated <%s> to registry! Password change not completed!\n",s); } } } FREE(v); } /* Check for presence of syskey and possibly disable it if * user wants it. * This is tricky, and extremely undocumented! * See docs for more info on what's going on when syskey is installed */ #undef LSADATA void handle_syskey(void) { /* This is \SAM\Domains\Account\F */ struct samkeyf { char unknown[0x50]; /* 0x0000 - Unknown. May be machine SID */ char unknown2[0x14]; char syskeymode; /* 0x0064 - Type/mode of syskey in use */ char syskeyflags1[0xb]; /* 0x0065 - More flags/settings */ char syskeyobf[0x30]; /* 0x0070 - This may very well be the obfuscated syskey */ }; /* There may be more, usually 8 null-bytes? */ /* Security\Policy\SecretEncryptionKey\@, only on NT5 */ /* Probably contains some keyinfo for syskey. Second DWORD seems to be syskeymode */ struct secpoldata { int unknown1; /* Some kind of flag? usually 1 */ int syskeymode; /* Is this what we're looking for? */ int unknown2; /* Usually 0? */ char keydata[0x40]; /* Some kind of scrambled keydata? */ }; #ifdef LSADATA /* SYSTEM\CurrentControlSet\Control\Lsa\Data, only on NT5?? */ /* Probably contains some keyinfo for syskey. Byte 0x34 seems to be mode */ struct lsadata { char keydata[0x34]; /* Key information */ int syskeymode; /* Is this what we're looking for? */ }; #endif /* void *fdata; */ struct samkeyf *ff = NULL; struct secpoldata *sf = NULL; /* struct lsadata *ld = NULL; */ int /* len, */ i,secboot, samfmode, secmode /* , ldmode */ ; struct keyval *samf, *secpol /* , *lsad */ ; char *syskeytypes[4] = { "off", "key-in-registry", "enter-passphrase", "key-on-floppy" }; char yn[5]; printf("\n---------------------> SYSKEY CHECK <-----------------------\n"); if (H_SAM < 0) { printf("ERROR: SAM hive not loaded!\n"); return; } samf = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT); if (samf && samf->len > 0x70 ) { ff = (struct samkeyf *)&samf->data; samfmode = ff->syskeymode; } else { samfmode = -1; } secboot = -1; if (H_SYS >= 0) { secboot = get_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT ); } secmode = -1; if (H_SEC >=0) { secpol = get_val2buf(hive[H_SEC], NULL, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_NONE, TPF_VK_EXACT); if (secpol) { /* Will not be found in NT 4, take care of that */ sf = (struct secpoldata *)&secpol->data; secmode = sf->syskeymode; } } #ifdef LSADATA lsad = get_val2buf(hive[H_SYS], NULL, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT); if (lsad && lsad->len >= 0x38) { ld = (struct lsadata *)&lsad->data; ldmode = ld->syskeymode; } else { ldmode = -1; } #endif printf("SYSTEM SecureBoot : %d -> %s\n", secboot, (secboot < 0 || secboot > 3) ? "Not Set (not installed, good!)" : syskeytypes[secboot]); printf("SAM Account\\F : %d -> %s\n", samfmode, (samfmode < 0 || samfmode > 3) ? "Not Set" : syskeytypes[samfmode]); printf("SECURITY PolSecretEncryptionKey: %d -> %s\n", secmode, (secmode < 0 || secmode > 3) ? "Not Set (OK if this is NT4)" : syskeytypes[secmode]); #ifdef LSADATA printf("SYSTEM LsaData : %d -> %s\n\n", ldmode, (ldmode < 0 || ldmode > 3) ? "Not Set (strange?)" : syskeytypes[ldmode]); #endif if (secboot != samfmode && secboot != -1) { printf("WARNING: Mismatch in syskey settings in SAM and SYSTEM!\n"); printf("WARNING: It may be dangerous to continue (however, resetting syskey\n"); printf(" may very well fix the problem)\n"); } if (secboot > 0 || samfmode > 0) { printf("\n***************** SYSKEY IS ENABLED! **************\n"); printf("This installation very likely has the syskey passwordhash-obfuscator installed\n"); printf("It's currently in mode = %d, %s-mode\n",secboot, (secboot < 0 || secboot > 3) ? "Unknown" : syskeytypes[secboot]); if (no_hives < 2) { printf("\nSYSTEM (and possibly SECURITY) hives not loaded, unable to disable syskey!\n"); printf("Please start the program with at least SAM & SYSTEM-hive filenames as arguments!\n\n"); return; } printf("SYSKEY is on! However, DO NOT DISABLE IT UNLESS YOU HAVE TO!\n"); printf("This program can change passwords even if syskey is on, however\n"); printf("if you have lost the key-floppy or passphrase you can turn it off,\n"); printf("but please read the docs first!!!\n"); printf("\n** IF YOU DON'T KNOW WHAT SYSKEY IS YOU DO NOT NEED TO SWITCH IT OFF!**\n"); printf("NOTE: On WINDOWS 2000 and XP it will not be possible\n"); printf("to turn it on again! (and other problems may also show..)\n\n"); printf("NOTE: Disabling syskey will invalidate ALL\n"); printf("passwords, requiring them to be reset. You should at least reset the\n"); printf("administrator password using this program, then the rest ought to be\n"); printf("done from NT.\n"); printf("\nEXTREME WARNING: Do not try this on Vista or Win 7, it will go into endless re-boots\n\n"); fmyinput("\nDo you really wish to disable SYSKEY? (y/n) [n] ",yn,2); if (*yn == 'y') { /* Reset SAM syskey infostruct, fill with zeroes */ if (ff) { ff->syskeymode = 0; for (i = 0; i < 0x3b; i++) { ff->syskeyflags1[i] = 0; } put_buf2val(hive[H_SAM], samf, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT); } /* Reset SECURITY infostruct (if any) */ if (sf) { memset(sf, 0, secpol->len); sf->syskeymode = 0; put_buf2val(hive[H_SEC], secpol, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_BINARY, TPF_VK_EXACT); } #if LSADATA if (ld) { ld->syskeymode = 0; put_buf2val(hive[H_SYS], lsad, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT); } #endif /* And SYSTEM SecureBoot parameter */ put_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT, 0); dirty = 1; syskeyreset = 1; printf("Updating passwordhash-lengths..\n"); list_users(1); printf("* SYSKEY RESET!\nNow please set new administrator password!\n"); } else { syskeyreset = 1; } } else { printf("Syskey not installed!\n"); return; } } /* Interactive user edit */ void useredit(void) { char iwho[100]; int il; printf("\n\n===== chntpw Edit User Info & Passwords ====\n\n"); if (H_SAM < 0) { printf("ERROR: SAM registry file (which contains user data) is not loaded!\n\n"); return; } list_users(1); while (1) { printf("\nSelect: ! - quit, . - list users, 0x - User with RID (hex)\n"); printf("or simply enter the username to change: [%s] ",admuser); il = fmyinput("",iwho,32); if (il == 1 && *iwho == '.') { printf("\n"); list_users(1); continue; } if (il == 1 && *iwho == '!') return; if (il == 0) strcpy(iwho,admuser); find_n_change(iwho); } } void recoveryconsole() { int cmd = 0; int sec = 0; static char *scpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SetCommand"; static char *slpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SecurityLevel"; char yn[5]; if (H_SOF < 0) { printf("\nSOFTWARE-hive not loaded, and there's where RecoveryConsole settings are..\n"); return; } cmd = get_dword(hive[H_SOF],0,scpath,TPF_VK_EXACT); sec = get_dword(hive[H_SOF],0,slpath,TPF_VK_EXACT); if (cmd == -1 && sec == -1) { printf("\nDid not find registry entries for RecoveryConsole.\n(RecoveryConsole is only in Windows 2000 and XP)\n"); return; } printf("\nRecoveryConsole:\n- Extended SET command is \t%s\n", cmd>0 ? "ENABLED (1)" : "DISABLED (0)"); printf("- Administrator password login: %s\n", sec>0 ? "SKIPPED (1)" : "ENFORCED (0)"); fmyinput("\nDo you want to change it? (y/n) [n] ",yn,2); if (*yn == 'y') { cmd ^= 1; sec ^= 1; if (!put_dword(hive[0], 0, scpath, TPF_VK_EXACT, cmd)) printf("Update of SET level failed registry edit\n"); if (!put_dword(hive[0], 0, slpath, TPF_VK_EXACT, sec)) printf("Update of login level failed registry edit\n"); printf("Done!\n"); } } /* Interactive menu system */ void interactive(void) { int il; char inbuf[20]; while(1) { printf("\n\n<>========<> chntpw Main Interactive Menu <>========<>\n\n" "Loaded hives:"); for (il = 0; il < no_hives; il++) { printf(" <%s>",hive[il]->filename); } printf("\n\n"); /* Make menu selection depending on what is loaded but it is still possible to select even if not shown */ if (H_SAM >= 0) { printf(" 1 - Edit user data and passwords\n"); printf(" 2 - List groups\n"); } if (H_SOF >= 0) { printf(" 3 - RecoveryConsole settings\n"); printf(" 4 - Show product key (DigitalProductID)\n"); } #if 0 if (H_SAM >= 0 && H_SYS >= 0 && H_SEC >= 0) { printf(" 8 - Syskey status & change\n"); } #endif printf(" - - -\n" " 9 - Registry editor, now with full write support!\n" " q - Quit (you will be asked if there is something to save)\n" "\n\n"); il = fmyinput("What to do? [1] -> ", inbuf, 10); if (!il) useredit(); if (il) { switch(inbuf[0]) { case '1': useredit(); break; case '2': list_groups(0); break; case '3': recoveryconsole(); break; case '4': cat_dpi(hive[H_SOF],0,"\\Microsoft\\Windows NT\\CurrentVersion\\DigitalProductId"); break; case '8': handle_syskey(); break; case '9': mainloop(); break; case 'q': return; break; } } } } int cmd_usrgrp(char *user, char *grp, int what) { int numgrp; int rid = 0; char s[200]; numgrp = strtol(grp, NULL, 0); printf("numgrp = %d (0x%x)\n", numgrp, numgrp); if ((H_SAM < 0) || (!user)) return(1); if (*user == '0' && *(user+1) == 'x') sscanf(user,"%i",&rid); if (!rid) { /* Look up username */ /* Extract the unnamed value out of the username-key, value is RID */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",user); rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT); if (rid == -1) { printf("User <%s> not found\n",user); return(1); } } printf("Username: %s, RID = %d (0x%0x)\n",user,rid,rid); switch (what) { case 0: return(add_user_to_grp(rid, numgrp)); break; case 1: return(remove_user_from_grp(rid, numgrp)); break; } return(0); } void do_automode(char *automode, char *who, char *grp) { struct sid_binary sid; char *sidstr; #if 1 printf("DEBUG: do_automode start\n"); printf("automode = %s\n",automode); printf("who = %s\n",who); printf("grp = %s\n",grp); #endif switch (*automode) { case 'l': list_users(0); break; case 's': if(get_machine_sid((char *)&sid)) { sidstr = sid_to_string(&sid); puts(sidstr); FREE(sidstr);} break; case 'g': list_groups(1); break; case 'a': cmd_usrgrp(who, grp, 0); break; /* Add user to group */ case 'r': cmd_usrgrp(who, grp, 1); break; /* Remove user from group */ } // printf("DEBUG: do_automode end\n"); } void usage(void) { printf("chntpw: change password of a user in a Windows SAM file,\n" "or invoke registry editor. Should handle both 32 and 64 bit windows and\n" "all version from NT3.x to Win7\n" "chntpw [OPTIONS] [systemfile] [securityfile] [otherreghive] [...]\n" " -h This message\n" " -u Username or RID to change, Administrator is default\n" " -l list all users in SAM file\n" " -i Interactive Menu system\n" " -e Registry editor. Now with full write support!\n" " -d Enter buffer debugger instead (hex editor), \n" " -v Be a little more verbose (for debuging)\n" " -L For scripts, write names of changed files to /tmp/changed\n" " -N No allocation mode. Only same length overwrites possible (very safe mode)\n" " -E No expand mode, do not expand hive file (safe mode)\n" " -A Auto / Noninteractive. Do stuff without asking\n" " -A F reset on first admin user (lowest RID)\n" " -A A reset all admins\n" " -A l list all users, output it parseable\n" " -A g list groups\n" " -A s show machine SID\n" " -A a -u -u Add user to group\n" " -A r -u -g Remove user from group\n" "\nUsernames can be given as name or RID (in hex with 0x first)\n" " group IDs must be given as number, can be hex with 0x first\n" "Example: chntpw -A a -u 0x3aa -g 1000 # Adds user with RID hex 0x3aa to group decimal 1000\n" "\nSee readme file on how to get to the registry files, and what they are.\n" "Source/binary freely distributable under GPL v2 license. See README for details.\n" "NOTE: This program is somewhat hackish! You are on your own!\n" ); } int main(int argc, char **argv) { int dodebug = 0, list = 0, inter = 0,edit = 0,il,d = 0, dd = 0, logchange = 0; int mode = HMODE_INFO; extern int /* opterr, */ optind; extern char* optarg; char *filename,c; char *who = "Administrator"; char *grp = NULL; char iwho[100]; char *automode = ""; FILE *ch; /* Write out names of touched files to this */ char *options = "A:LENidehlvu:g:"; while((c=getopt(argc,argv,options)) > 0) { switch(c) { case 'd': dodebug = 1; break; case 'e': edit = 1; break; case 'L': logchange = 1; break; case 'N': mode |= HMODE_NOALLOC; break; case 'E': mode |= HMODE_NOEXPAND; break; case 'l': list = 1; break; case 'v': mode |= HMODE_VERBOSE; gverbose = 1; break; case 'i': inter = 1; break; case 'u': who = optarg; break; case 'g': grp = optarg; break; case 'A': automode = optarg; mode &= ~HMODE_INFO; break; case 'h': usage(); exit(0); break; default: usage(); exit(1); break; } } if (!*automode) printf("%s\n",chntpw_version); filename=argv[optind]; if (!filename || !*filename) { usage(); exit(1); } do { if (!(hive[no_hives] = openHive(filename, HMODE_RW|mode))) { fprintf(stderr,"%s: Unable to open/read a hive, exiting..\n",argv[0]); exit(1); } switch(hive[no_hives]->type) { case HTYPE_SAM: H_SAM = no_hives; break; case HTYPE_SOFTWARE: H_SOF = no_hives; break; case HTYPE_SYSTEM: H_SYS = no_hives; break; case HTYPE_SECURITY: H_SEC = no_hives; break; } no_hives++; filename = argv[optind+no_hives]; } while (filename && *filename && no_hives < MAX_HIVES); #if 0 printf("user = %s\n",who); printf("automode = %s\n",automode); #endif if (dodebug) { debugit(hive[0]->buffer,hive[0]->size); } else if (*automode) { check_get_samdata(0); do_automode(automode, who, grp); } else if (inter) { check_get_samdata(1); interactive(); } else if (edit) { check_get_samdata(1); mainloop(); } else if (list) { check_get_samdata(1); list_users(1); } else if (who) { check_get_samdata(1); find_n_change(who); } if (list != 1) { if (!*automode) printf("\nHives that have changed:\n # Name\n"); for (il = 0; il < no_hives; il++) { if (hive[il]->state & HMODE_DIRTY) { if (!logchange && !*automode) printf("%2d <%s>",il,hive[il]->filename); if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n"); if (!*automode) printf("\n"); d = 1; } } if (d) { /* Only prompt user if logging of changed files has not been set */ /* Thus we assume confirmations are done externally if they ask for a list of changes */ if (!logchange && !*automode) fmyinput("Write hive files? (y/n) [n] : ",iwho,3); if (*iwho == 'y' || logchange || *automode) { if (logchange) { ch = fopen("/tmp/changed","w"); } for (il = 0; il < no_hives; il++) { if (hive[il]->state & HMODE_DIRTY) { if (!*automode) printf("%2d <%s> - ",il,hive[il]->filename); if (!writeHive(hive[il])) { if (!*automode) printf("OK"); if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n"); if (!*automode) printf("\n"); if (logchange) fprintf(ch,"%s ",hive[il]->filename); dd = 2; } } } if (logchange) { fprintf(ch,"\n"); fclose(ch); } } else { printf("Not written!\n\n"); } } else { if (!*automode) printf("None!\n\n"); } } /* list only check */ return(dd); } chntpw-1.0/cpnt.c0000644000175000017500000000413112273223316012113 0ustar jfsjfs/* * cpnt.c - Copy over file without truncating. * For use on my current floppy, since it's 'cp' insist * on truncating first, and NTFS doesn't like that yet. * * 2003-apr: First version * ***** * * Copyright (c) 1997-2007 Petter Nordahl-Hagen. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See file GPL.txt for the full license. * */ #include #include #include #include #include #include #include #include #include #define BUFSIZE 16384 int main(int argc, char **argv) { void *buf; int sf,df,rb,wb; int going = 1; int e = 0; if (argc != 3) { printf("usage: cpnt \n"); printf(" sorry, only one file at a time yet.\n"); return(1); } #if 0 printf("input : %s\n",argv[1]); printf("output: %s\n",argv[2]); #endif buf = malloc(BUFSIZE); if (!buf) { printf("cpnt: could not allocate buffer\n"); return(1); } sf = open(argv[1],O_RDONLY); if (sf < 0) { e = errno; printf("cpnt: %s: %s\n",argv[1],strerror(e)); return(1); } df = open(argv[2],O_WRONLY|O_CREAT,00666); if (df < 0) { e = errno; printf("cpnt: %s: %s\n",argv[2],strerror(e)); return(1); } while (going) { rb = read(sf,buf,BUFSIZE); if (rb < 0) { e = errno; printf("cpnt: error while reading: %s\n",strerror(e)); going = 0; break; } if (rb == 0) going = 0; wb = write(df,buf,rb); if (wb < 0) { e = errno; printf("cpnt: error while writing: %s\n",strerror(e)); going = 0; } } close(sf); close(df); free(buf); return(e ? 1 : 0); } chntpw-1.0/LGPL.txt0000644000175000017500000006351012273223316012310 0ustar jfsjfs GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! chntpw-1.0/regedit.txt0000600000175000017500000003174012273223316013165 0ustar jfsjfsThe Offline NT Password Editor (c) 1997-2011 Petter Nordahl-Hagen Registry Editor Usermanual/docs See COPYING for copyright & credits. See INSTALL for compile/installation instructions. See README for docs on the passwordpart (or website for bootdisk) Some known limitations as of first half 2011: This release features full basic registry edit with add/del keys and values and resizing values, and also expanding the file. Renaming keys and values is not implemented yet. Import is slow. Export is fast. Program "reged" is used for interactive registry edit (rudimentary command prompt type interface) or to export or import .reg files. Import and export can also be scripted (non-interactive) The "chntpw" program which is for password edits, also contains the editor, but not the import / export. Usage of "reged": reged version 0.1 110504, (c) Petter N Hagen Modes: -x Xport. Where for example is HKEY_LOCAL_MACHINE\SOFTWARE is key to dump (recursively), . or \ or \\ means all keys in hive Only one .reg and one hive file supported at the same time -I Import from .reg file. Where for example is HKEY_LOCAL_MACHINE\SOFTWARE Only one .reg and one hive file supported at the same time -e ... Interactive edit one or more of registry files Options: -L : Log changed filenames to /tmp/changed, also auto-saves -C : Auto-save (commit) changed hives without asking -N : No allocate mode, only allow edit of existing values with same size -E : No expand mode, do not expand hive file (safe mode) -t : Debug trace of allocated blocks -v : Some more verbose messages -x will do export to .reg file. It will create files that seems to be compatible with regedit.exe in Windows. The prefix is the first part of the key names that windows shows/uses, it is not stored in the hive files, so you can actually put in anything unless you have to import in Windows. Current version only handles one set of files at a time (and also only one point to start export from in it) -I does import of .reg files into the specified hive file. It supports .reg files from Windows regedit.exe, they are usually UTF-16 (16 bit characters) and in the few cases I tested it will import with correct characters. Please note that the key and value names always are 8 bit in the registry, but strings the values store are 16 bit characters. Will also work with latin-1 (8 bit) character files, like the export functions creates, but again some character conversions may be wrong. Note that the current versions are pretty slow, since they are not optimized or well written in any way. Around 90000 keys with 120000 values just took around 10 minutes on my pretty fast machine. (I am lazy! You don't write hex reads with one byte at a time do you? :) -e goes into interactive editor (see below for old example) -e can be combined with -I so that the editor is entered before saving -after an import. -N and -E are safe modes, can be used with any mode. -C must be used when importing to auto-save, else it will ask first if import succeeds. The -L option will write changed hive file names to /tmp/changed, I use it for scripts that need to know. ------------------------ Here is an old demo of registry edit, via the chntpw program, but regedit is the same (except some new stuff is in there) You can navigate the registry almost like a filesystem (only difference being that the "files" actually are of a special datatype, instead of just a bytestream) Note that this demo is just some random editing, will likely cause windows to not boot if written back to it. >chntpw -h chntpw version 0.99.0 030111, (c) Petter N Hagen chntpw: change password of a user in a NT SAM file, or invoke registry editor. chntpw [OPTIONS] [systemfile] [securityfile] [otherreghive] [...] -h This message -u Username to change, Administrator is default -l list all users in SAM file -i Interactive. List users (as -l) then ask for username to change -e Registry editor. Now with full write support! -d Enter buffer debugger instead (hex editor), -t Trace. Show hexdump of structs/segments. (deprecated debug function) -L Write names of changed files to /tmp/changed -N No allocation mode. Only (old style) same length overwrites possible (example edit of a SYSTEM-hive) >chntpw -e system chntpw version 0.99.0 030111, (c) Petter N Hagen Hive's name (from header): ROOT KEY at offset: 0x001020 This is probably not a sam HIVE Simple registry editor. ? for help. [1020] > ? Simple registry editor: hive [] - list loaded hives or switch to hive numer n' cd - change key ls | dir [] - show subkeys & values, cat | type - show key value st [] - show struct info nk - add key dk - delete key (must be empty. recursion not supported yet) ed - Edit value nv - Add value dv - Delete value delallv - Delete all values in current key debug - enter buffer hexeditor q - quit (list the contents of the current key) [1020] > l ls of node at offset 0x1024 Node has 6 subkeys and 0 values offs key name [ 11b8] [ dff88] [1c2040] [ de448] [ de998] [ deab8] (now let's make a subkey here) [1020] > nk DemoKey [1020] > l ls of node at offset 0x1024 Node has 7 subkeys and 0 values offs key name [ 11b8] [ dff88] [1c2ef8] [1c2040] [ de448] [ de998] [ deab8] (and delete the key) [1020] > dk DemoKey [1020] > l ls of node at offset 0x1024 Node has 6 subkeys and 0 values offs key name [ 11b8] [ dff88] [1c2040] [ de448] [ de998]