ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/pbcdedit/pbcdedit
(Generate patch)

Comparing pbcdedit/pbcdedit (file contents):
Revision 1.2 by root, Wed Aug 14 20:56:50 2019 UTC vs.
Revision 1.9 by root, Wed Aug 14 22:54:28 2019 UTC

1#!/opt/bin/perl 1#!/usr/bin/perl
2 2
3# 3#
4# PBCDEDIT - Copyright 2019 Marc A. Lehmann <pbcbedit@schmorp.de> 4# PBCDEDIT - Copyright 2019 Marc A. Lehmann <pbcbedit@schmorp.de>
5# 5#
6# SPDX-License-Identifier: GPL-3.0-or-later 6# SPDX-License-Identifier: GPL-3.0-or-later
17# 17#
18# You should have received a copy of the GNU General Public License 18# You should have received a copy of the GNU General Public License
19# along with this program. If not, see <https://www.gnu.org/licenses/>. 19# along with this program. If not, see <https://www.gnu.org/licenses/>.
20# 20#
21 21
22use 5.014; # numerous features 22use 5.014; # numerous features needed
23 23
24our $VERSION = '1.0'; 24our $VERSION = '1.0';
25our $JSON_VERSION = 1; # the versiobn of the json objects generated by this program 25our $JSON_VERSION = 1; # the versiobn of the json objects generated by this program
26 26
27=head1 NAME 27=head1 NAME
41=head1 DESCRIPTION 41=head1 DESCRIPTION
42 42
43This program allows you to create, read and modify Boot Configuration Data 43This program allows you to create, read and modify Boot Configuration Data
44(BCD) stores used by Windows Vista and newer versions of Windows. 44(BCD) stores used by Windows Vista and newer versions of Windows.
45 45
46At this point, it is in relatively early stages of development and has
47received little to no real-world testing.
48
46Compared to other BCD editing programs it offers the following unique 49Compared to other BCD editing programs it offers the following unique
47features: 50features:
48 51
49=over 52=over
50 53
106The reverse of C<export>: Reads a JSON representation of a BCD data store 109The reverse of C<export>: Reads a JSON representation of a BCD data store
107from standard input, and creates or replaces the given BCD data store. 110from standard input, and creates or replaces the given BCD data store.
108 111
109=item edit F<path> instructions... 112=item edit F<path> instructions...
110 113
111#TODO 114Load a BCD data store, apply some instructions to it, and save it again.
115
116See the section L<EDITING BCD DATA STORES>, below, for more info.
117
118=item parse F<path> instructions...
119
120Same as C<edit>, above, except it doesn't save the data store again. Can
121be useful to extract some data from it.
112 122
113=item lsblk 123=item lsblk
114 124
115On a GNU/Linux system, you can get a list of partition device descriptors 125On a GNU/Linux system, you can get a list of partition device descriptors
116using this command - the external C<lsblk> command is required, as well as 126using this command - the external C<lsblk> command is required, as well as
202 "hypervisordebugtype" : 0 212 "hypervisordebugtype" : 0
203 }, 213 },
204 # ... 214 # ...
205 } 215 }
206 216
217=head2 Minimal BCD to boot windows
218
219Experimentally I found the following BCD is the minimum required to
220successfully boot any post-XP version of Windows (suitable C<device> and
221C<osdevice> values, of course):
222
223 {
224 "{bootmgr}" : {
225 "resumeobject" : "{45b547a7-8ca6-4417-9eb0-a257b61f35b4}"
226 },
227
228 "{45b547a7-8ca6-4417-9eb0-a257b61f35b1}" : {
229 "type" : "application::osloader",
230 "description" : "Windows Boot",
231 "device" : "legacypartition=<null>,harddisk,mbr,47cbc08a,1",
232 "osdevice" : "legacypartition=<null>,harddisk,mbr,47cbc08a,1",
233 "path" : "\\Windows\\system32\\winload.exe",
234 "systemroot" : "\\Windows"
235 },
236 }
237
238Note that minimal doesn't mean recommended - Windows itself will add stuff
239to this during or after boot, and you might or might not run into issues
240when installing updates as it might not be able to find the F<bootmgr>.
241
207=head2 The C<meta> key 242=head2 The C<meta> key
208 243
209The C<meta> key is not stored in the BCD data store but is used only 244The C<meta> key is not stored in the BCD data store but is used only
210by PBCDEDIT. It is always generated when exporting, and importing will 245by PBCDEDIT. It is always generated when exporting, and importing will
211be refused when it exists and the version stored inside doesn't store 246be refused when it exists and the version stored inside doesn't store
593many questions, but I can walk you through some actual examples using mroe 628many questions, but I can walk you through some actual examples using mroe
594complex aspects. 629complex aspects.
595 630
596=item locate=<block=vhd,<block=file,<locate=<null>,path,\disk.vhdx>,\disk.vhdx>>,element,path 631=item locate=<block=vhd,<block=file,<locate=<null>,path,\disk.vhdx>,\disk.vhdx>>,element,path
597 632
598#todo 633Just like with C declarations, you best treat device descriptors as
634instructions to find your device and work your way from the inside out:
635
636 locate=<null>,path,\disk.vhdx
637
638First, the innermost device descriptor searches all partitions on the
639system for a file called F<\disk.vhdx>:
640
641 block=file,<see above>,\disk.vhdx
642
643Next, this takes the device locate has found and finds a file called
644F<\disk.vhdx> on it. This is the same file locate was using, but that is
645only because we find the device using the same path as finding the disk
646image, so this is purely incidental, although quite common.
647
648Bext, this file will be opened as a virtual disk:
649
650 block=vhd,<see above>
651
652And finally, inside this disk, another C<locate> will look for a partition
653with a path as specified in the C<path> element, which most likely will be
654F<\Windows\system32\winload.exe>:
655
656 locate=<see above>,element,path
657
658As a result, this will boot the first Windows it finds on the first
659F<disk.vhdx> disk image it can find anywhere.
599 660
600=item locate=<block=vhd,<block=file,<partition=<null>,harddisk,mbr,47cbc08a,242643632128>,\win10.vhdx>>,element,path 661=item locate=<block=vhd,<block=file,<partition=<null>,harddisk,mbr,47cbc08a,242643632128>,\win10.vhdx>>,element,path
601 662
602#todo 663Pretty much the same as the previous case, but witzh a bit of variance. First, look for a specific partition on
664an MBR-partitioned disk:
665
666 partition=<null>,harddisk,mbr,47cbc08a,242643632128
667
668Then open the file F<\win10.vhdx> on that partition:
669
670 block=file,<see above>,\win10.vhdx
671
672Then, again, the file is opened as a virtual disk image:
673
674 block=vhd,<see above>
675
676And again the windows loader (or whatever is in C<path>) will be searched:
677
678 locate=<see above>,element,path
603 679
604=item {b097d2b2-bc00-11e9-8a9a-525400123456}block<1>=ramdisk,<partition=<null>,harddisk,mbr,47cbc08a,242643632128>,0,0,0,\boot.wim 680=item {b097d2b2-bc00-11e9-8a9a-525400123456}block<1>=ramdisk,<partition=<null>,harddisk,mbr,47cbc08a,242643632128>,0,0,0,\boot.wim
605 681
606#todo 682This is quite different. First, it starts with a GUID. This GUID belongs
683to a BCD object of type C<device>, which has additional parameters:
607 684
685 "{b097d2b2-bc00-11e9-8a9a-525400123456}" : {
686 "type" : "device",
687 "description" : "sdi file for ramdisk",
688 "ramdisksdidevice" : "partition=<null>,harddisk,mbr,47cbc08a,1048576",
689 "ramdisksdipath" : "\boot.sdi"
690 },
691
692I will not go into many details, but this specifies a (presumably empty)
693template ramdisk image (F<\boot.sdi>) that is used to initiaolize the
694ramdisk. The F<\boot.wim> file is then extracted into it. As you cna also
695see, this F<.sdi> file resides on a different C<partition>.
696
697Continuitn, as always, form the inside out, first this device descriptor
698finds a specific partition:
699
700 partition=<null>,harddisk,mbr,47cbc08a,242643632128
701
702And then specifies a C<ramdisk> image on this partition:
703
704 block<1>=ramdisk,<see above>,0,0,0,\boot.wim
705
706I don't know what the purpose of the C<< <1> >> flag value is, but it
707seems to be always there on this kind of entry.
708
709If you have some good examples to add here, feel free to mail me.
710
711
712=head1 EDITING BCD DATA STORES
713
714The C<edit> and C<parse> subcommands allow you to read a BCD data store
715and modify it or extract data from it. This is done by exyecuting a series
716of "editing instructions" which are explained here.
717
718=over
719
720=item get I<object> I<element>
721
722Reads the BCD element I<element> from the BCD object I<object> and writes
723it to standard output, followed by a newline. The I<object> can be a GUID
724or a human-readable alias, or the special string C<{default}>, which will
725refer to the default BCD object.
726
727Example: find description of the default BCD object.
728
729 pbcdedit parse BCD get "{default}" description
730
731=item set I<object> I<element> I<value>
732
733Similar to C<get>, but sets the element to the given I<value> instead.
734
735Example: change bootmgr default too
736C<{b097d2ad-bc00-11e9-8a9a-525400123456}>:
737
738 pbcdedit edit BCD set "{bootmgr}" resumeobject "{b097d2ad-bc00-11e9-8a9a-525400123456}"
739
740=item eval I<perlcode>
741
742This takes the next argument, interprets it as Perl code and
743evaluates it. This allows you to do more complicated modifications or
744extractions.
745
746The following variables are predefined for your use:
747
748=over
749
750=item C<$PATH>
751
752The path to the BCD data store, as given to C<edit> or C<parse>.
753
754=item C<$BCD>
755
756The decoded BCD data store.
757
758=item C<$DEFAULT>
759
760The default BCD object name.
761
762=back
763
764The example given for C<get>, above, could be expressed like this with
765C<eval>:
766
767 pbcdedit edit BCD eval 'say $BCD->{$DEFAULT}{description}'
768
769The example given for C<set> could be expresed like this:
770
771 pbcdedit edit BCD eval '$BCD->{$DEFAULT}{resumeobject} = "{b097d2ad-bc00-11e9-8a9a-525400123456}"'
772
773=item do I<path>
774
775Similar to C<eval>, above, but instead of using the argument as perl code,
776it loads the perl code from the given file and executes it. This makes it
777easier to write more complicated or larger programs.
778
779=back
608 780
609=head1 SEE ALSO 781=head1 SEE ALSO
610 782
611For ideas on what you can do, and some introductory material, try 783For ideas on what you can do, and some introductory material, try
612L<http://www.mistyprojects.co.uk/documents/BCDEdit/index.html>. 784L<http://www.mistyprojects.co.uk/documents/BCDEdit/index.html>.
667 839
668# hack used for debugging 840# hack used for debugging
669sub xxd($$) { 841sub xxd($$) {
670 open my $xxd, "| xxd | sed -e 's/^/\Q$_[0]\E: /'"; 842 open my $xxd, "| xxd | sed -e 's/^/\Q$_[0]\E: /'";
671 syswrite $xxd, $_[1]; 843 syswrite $xxd, $_[1];
844}
845
846sub file_load($) {
847 my ($path) = @_;
848
849 open my $fh, "<:raw", $path
850 or die "$path: $!\n";
851 my $size = -s $fh;
852 $size = read $fh, my $buf, $size
853 or die "$path: short read\n";
854
855 $buf
672} 856}
673 857
674# sources and resources used for this: 858# sources and resources used for this:
675# registry: 859# registry:
676# https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md 860# https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md
1027} 1211}
1028 1212
1029# load and parse registry from file 1213# load and parse registry from file
1030sub regf_load($) { 1214sub regf_load($) {
1031 my ($path) = @_; 1215 my ($path) = @_;
1032 open my $regf, "<:raw", $path
1033 or die "$path: $!\n";
1034 my $size = -s $regf;
1035 $size = read $regf, my $buf, $size
1036 or die "$path: short read\n";
1037 1216
1038 regf_decode $buf 1217 regf_decode file_load $path
1039} 1218}
1040 1219
1041# encode and save registry to file 1220# encode and save registry to file
1042sub regf_save { 1221sub regf_save {
1043 my ($path, $hive) = @_; 1222 my ($path, $hive) = @_;
2064 }]] 2243 }]]
2065} 2244}
2066 2245
2067############################################################################# 2246#############################################################################
2068 2247
2248sub bcd_edit_eval {
2249 package pbcdedit;
2250
2251 our ($PATH, $BCD, $DEFAULT);
2252
2253 eval shift;
2254 die "$@" if $@;
2255}
2256
2257sub bcd_edit {
2258 my ($path, $bcd, @insns) = @_;
2259
2260 my $default = $bcd->{"{bootmgr}"}{resumeobject};
2261
2262 # prepare "officially visible" variables
2263 local $pbcdedit::PATH = $path;
2264 local $pbcdedit::BCD = $bcd;
2265 local $pbcdedit::DEFAULT = $default;
2266
2267 while (@insns) {
2268 my $insn = shift @insns;
2269
2270 if ($insn eq "get") {
2271 my $object = shift @insns;
2272 my $elem = shift @insns;
2273
2274 $object = $default if $object eq "{default}";
2275
2276 print $bcd->{$object}{$elem}, "\n";
2277
2278 } elsif ($insn eq "set") {
2279 my $object = shift @insns;
2280 my $elem = shift @insns;
2281 my $value = shift @insns;
2282
2283 $object = $default if $object eq "{default}";
2284
2285 $bcd->{$object}{$elem} = $value;
2286
2287 } elsif ($insn eq "eval") {
2288 bcd_edit_eval shift @insns;
2289
2290 } elsif ($insn eq "do") {
2291 my $path = shift @insns;
2292 my $file = file_load $path;
2293 bcd_edit_eval "#line 1 '$path'\n$file";
2294
2295 } else {
2296 die "$insn: not a recognized instruction for edit/parse\n";
2297 }
2298 }
2299
2300}
2301
2302#############################################################################
2303
2069# json to stdout 2304# json to stdout
2070sub prjson($) { 2305sub prjson($) {
2071 print $json_coder->encode ($_[0]); 2306 print $json_coder->encode ($_[0]);
2072} 2307}
2073 2308
2170 prjson bcd_decode regf_load shift; 2405 prjson bcd_decode regf_load shift;
2171 }, 2406 },
2172 2407
2173 import => sub { 2408 import => sub {
2174 regf_save shift, bcd_encode rdjson; 2409 regf_save shift, bcd_encode rdjson;
2410 },
2411
2412 edit => sub {
2413 my $path = shift;
2414 my $bcd = bcd_decode regf_load $path;
2415 bcd_edit $path, $bcd, @_;
2416 regf_save $path, bcd_encode $bcd;
2417 },
2418
2419 parse => sub {
2420 my $path = shift;
2421 my $bcd = bcd_decode regf_load $path;
2422 bcd_edit $path, $bcd, @_;
2175 }, 2423 },
2176 2424
2177 "export-regf" => sub { 2425 "export-regf" => sub {
2178 prjson regf_load shift; 2426 prjson regf_load shift;
2179 2427

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines