@@ -14,6 +14,19 @@ use std::{convert::TryFrom, path::Path};
14
14
pub const KERNEL_SIZE : & str = "0x2000000" ;
15
15
pub const KERNEL_PARAM_SIZE : & str = "0x1000" ;
16
16
17
+ // Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#signature-image-only,
18
+ // file offset specified at offset 0x3c,
19
+ // size of PE signature is 4: "PE\0\0"
20
+ const IMAGE_PE_OFFSET : usize = 0x003c ;
21
+ const PE_SIGNATURE_SIZE : u32 = 4 ;
22
+ const IMGAE_BEGIN_CHECKSUM_ADDR : usize = 0x0000 ;
23
+ const IMGAE_BEGIN_CHECKSUM_SIZE : usize = 0x00da ;
24
+ const IMGAE_CERT_TABLE_ADDR : usize = 0x00de ;
25
+ const IMGAE_CERT_TABLE_SIZE : usize = 0x004c ;
26
+ const IMGAE_HEADERS_ADDR : usize = 0x0132 ;
27
+ const IMGAE_HEADERS_SIZE : usize = 0x00ce ;
28
+ const IMAGE_PROTOCOL_ADDR : usize = 0x0206 ;
29
+
17
30
fn kernel ( path : & str , size : & str ) -> Result < String > {
18
31
let path = Path :: new ( path) . to_path_buf ( ) ;
19
32
let siz = parse :: < u64 > ( size) ?;
@@ -22,6 +35,11 @@ fn kernel(path: &str, size: &str) -> Result<String> {
22
35
bail ! ( "File size should be less than `kernel-size`" ) ;
23
36
}
24
37
let buf = std:: fs:: read ( path) ?;
38
+ let protocol = ( ( buf[ IMAGE_PROTOCOL_ADDR as usize + 1 ] as u16 ) << 8 )
39
+ | buf[ IMAGE_PROTOCOL_ADDR as usize ] as u16 ;
40
+ if protocol < 0x206 {
41
+ bail ! ( "Protocol version should be 2.06+" ) ;
42
+ }
25
43
padding_digest ( buf, siz as usize )
26
44
}
27
45
@@ -31,6 +49,111 @@ fn param(param: &str, size: &str) -> Result<String> {
31
49
padding_digest ( param, siz)
32
50
}
33
51
52
+ fn qemu ( path : & str , size : & str ) -> Result < String > {
53
+ let path = Path :: new ( path) . to_path_buf ( ) ;
54
+ let siz = parse :: < u64 > ( size) ?;
55
+ let file_size = std:: fs:: metadata ( & path) ?. len ( ) ;
56
+ if file_size > siz {
57
+ bail ! ( "File size should be less than `kernel-size`" ) ;
58
+ }
59
+ let buf = std:: fs:: read ( path) ?;
60
+ let protocol = ( ( buf[ IMAGE_PROTOCOL_ADDR as usize + 1 ] as u16 ) << 8 )
61
+ | buf[ IMAGE_PROTOCOL_ADDR as usize ] as u16 ;
62
+ if protocol < 0x206 {
63
+ bail ! ( "Protocol version should be 2.06+" ) ;
64
+ }
65
+ qemu_patch ( buf)
66
+ }
67
+
68
+ fn qemu_patch ( mut buf : Vec < u8 > ) -> Result < String > {
69
+ // refer to https://github.com/qemu/qemu/blob/f48c205fb42be48e2e47b7e1cd9a2802e5ca17b0/hw/i386/x86.c#L999
70
+ // patching type_of_loader @0x210
71
+ buf[ 0x210 ] = 0xb0 ;
72
+
73
+ // refer to https://github.com/qemu/qemu/blob/f48c205fb42be48e2e47b7e1cd9a2802e5ca17b0/hw/i386/x86.c#L1003
74
+ // patching loadflags @0x211
75
+ buf[ 0x211 ] = 0x81 ;
76
+
77
+ // refer to https://github.com/qemu/qemu/blob/9c74490bff6c8886a922008d0c9ce6cae70dd17e/hw/i386/x86.c#L1004
78
+ // patching heap_end_ptr @0x224 cmdline_addr - real_addr - 0x200 = 0xfe00
79
+ buf[ 0x224 ] = 0x00 ;
80
+ buf[ 0x225 ] = 0xfe ;
81
+
82
+ // refer to https://github.com/qemu/qemu/blob/9c74490bff6c8886a922008d0c9ce6cae70dd17e/hw/i386/x86.c#L962
83
+ // patching cmd_line_ptr @0x228 cmdline_addr = 0x20000
84
+ buf[ 0x228 ] = 0x00 ;
85
+ buf[ 0x229 ] = 0x00 ;
86
+ buf[ 0x22A ] = 0x02 ;
87
+ buf[ 0x22B ] = 0x00 ;
88
+
89
+ let mut hasher = sha2:: Sha384 :: new ( ) ;
90
+ let ( number_of_region_entry, regions_base, regions_size) = get_image_regions ( & buf) ;
91
+
92
+ for index in 0 ..number_of_region_entry {
93
+ hasher. update ( & buf[ regions_base[ index] ..regions_base[ index] + regions_size[ index] ] ) ;
94
+ }
95
+
96
+ let res = hasher. finalize ( ) ;
97
+ Ok ( hex:: encode ( res) )
98
+ }
99
+
100
+ fn get_image_regions ( buf : & [ u8 ] ) -> ( usize , Vec < usize > , Vec < usize > ) {
101
+ // These 3 regions are known.
102
+ let mut number_of_region_entry = 3 ;
103
+ let mut regions_base = vec ! [
104
+ IMGAE_BEGIN_CHECKSUM_ADDR ,
105
+ IMGAE_CERT_TABLE_ADDR ,
106
+ IMGAE_HEADERS_ADDR ,
107
+ ] ;
108
+ let mut regions_size = vec ! [
109
+ IMGAE_BEGIN_CHECKSUM_SIZE ,
110
+ IMGAE_CERT_TABLE_SIZE ,
111
+ IMGAE_HEADERS_SIZE ,
112
+ ] ;
113
+
114
+ // Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image,
115
+ // After the signature of an image file is COFF File Header, size is 20 bytes.
116
+ // the NumberOfSections' offset is 2 and size is 2 bytes.
117
+ let size_of_coff_file_header: u32 = 20 ;
118
+
119
+ let coff_file_header_offset = ( ( buf[ IMAGE_PE_OFFSET + 3 ] as u32 ) << 24 )
120
+ | ( ( buf[ IMAGE_PE_OFFSET + 2 ] as u32 ) << 16 )
121
+ | ( ( buf[ IMAGE_PE_OFFSET + 1 ] as u32 ) << 8 )
122
+ | ( buf[ IMAGE_PE_OFFSET ] as u32 ) + PE_SIGNATURE_SIZE ;
123
+
124
+ let number_of_pecoff_entry = ( ( buf[ coff_file_header_offset as usize + 3 ] as u16 ) << 8 )
125
+ | buf[ coff_file_header_offset as usize + 2 ] as u16 ;
126
+ number_of_region_entry += number_of_pecoff_entry as usize ;
127
+
128
+ // the SizeOfOptionalHeader's offset is 16 and size is 2 bytes
129
+ let size_of_optional_header = ( ( buf[ coff_file_header_offset as usize + 17 ] as u16 ) << 8 )
130
+ | buf[ coff_file_header_offset as usize + 16 ] as u16 ;
131
+
132
+ // Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers
133
+ // Size Of each Section is 40 bytes
134
+ // SizeOfRawData Offset: 16 Size:4
135
+ // PointerToRawData Offset: 20 Size:4
136
+ let mut p = ( coff_file_header_offset
137
+ + size_of_coff_file_header
138
+ + size_of_optional_header as u32 ) as usize ;
139
+ for _i in 0 ..number_of_pecoff_entry {
140
+ p += 16 ;
141
+ let size = ( ( buf[ p + 3 ] as u32 ) << 24 )
142
+ | ( ( buf[ p + 2 ] as u32 ) << 16 )
143
+ | ( ( buf[ p + 1 ] as u32 ) << 8 )
144
+ | buf[ p] as u32 ;
145
+ p += 4 ;
146
+ let base = ( ( buf[ p + 3 ] as u32 ) << 24 )
147
+ | ( ( buf[ p + 2 ] as u32 ) << 16 )
148
+ | ( ( buf[ p + 1 ] as u32 ) << 8 )
149
+ | buf[ p] as u32 ;
150
+ regions_base. push ( base as usize ) ;
151
+ regions_size. push ( size as usize ) ;
152
+ p += 20 ;
153
+ }
154
+ ( number_of_region_entry, regions_base, regions_size)
155
+ }
156
+
34
157
fn padding_digest ( mut buf : Vec < u8 > , len : usize ) -> Result < String > {
35
158
let diff = len - buf. len ( ) ;
36
159
@@ -56,7 +179,8 @@ fn main() {
56
179
. required ( false )
57
180
. default_value ( KERNEL_SIZE )
58
181
. action ( ArgAction :: Set ) ,
59
- ) ,
182
+ )
183
+ . arg ( arg ! ( -q --"qemu" "QEMU Kernel Direct Boot patch string" ) . required ( false ) ) ,
60
184
)
61
185
. subcommand (
62
186
command ! ( "param" )
@@ -78,7 +202,12 @@ fn main() {
78
202
Some ( ( "kernel" , args) ) => {
79
203
let path = args. get_one :: < String > ( "kernel" ) . unwrap ( ) ;
80
204
let siz = args. get_one :: < String > ( "size" ) . unwrap ( ) ;
81
- kernel ( path, siz)
205
+ // let qflag = args.get_one::<String>("qemu").unwrap();
206
+ if args. get_flag ( "qemu" ) {
207
+ qemu ( path, siz)
208
+ } else {
209
+ kernel ( path, siz)
210
+ }
82
211
}
83
212
Some ( ( "param" , args) ) => {
84
213
let parameter = args. get_one :: < String > ( "parameter" ) . unwrap ( ) ;
0 commit comments