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