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