1use super::{Architecture, DecodedInstruction};
4
5pub struct X64;
7
8impl Architecture for X64 {
9 const JMP_REL_SIZE: usize = 5;
11
12 const JMP_ABS_SIZE: usize = 14;
14
15 const PTR_SIZE: usize = 8;
16 const CODE_ALIGNMENT: usize = 16;
17
18 const MIN_HOOK_SIZE: usize = 5;
20
21 fn encode_jmp_rel(source: usize, target: usize) -> Option<Vec<u8>> {
22 let offset = (target as i64) - (source as i64) - 5;
24
25 if offset < i32::MIN as i64 || offset > i32::MAX as i64 {
27 return None;
28 }
29
30 let mut bytes = Vec::with_capacity(5);
31 bytes.push(0xE9); bytes.extend_from_slice(&(offset as i32).to_le_bytes());
33 Some(bytes)
34 }
35
36 fn encode_jmp_abs(target: usize) -> Vec<u8> {
37 let mut bytes = Vec::with_capacity(14);
40 bytes.extend_from_slice(&[0xFF, 0x25, 0x00, 0x00, 0x00, 0x00]);
41 bytes.extend_from_slice(&(target as u64).to_le_bytes());
42 bytes
43 }
44
45 fn encode_call_rel(source: usize, target: usize) -> Option<Vec<u8>> {
46 let offset = (target as i64) - (source as i64) - 5;
47
48 if offset < i32::MIN as i64 || offset > i32::MAX as i64 {
49 return None;
50 }
51
52 let mut bytes = Vec::with_capacity(5);
53 bytes.push(0xE8); bytes.extend_from_slice(&(offset as i32).to_le_bytes());
55 Some(bytes)
56 }
57
58 fn encode_nop_sled(size: usize) -> Vec<u8> {
59 let mut bytes = Vec::with_capacity(size);
61 let mut remaining = size;
62
63 while remaining > 0 {
64 match remaining {
65 1 => {
66 bytes.push(0x90); remaining -= 1;
68 }
69 2 => {
70 bytes.extend_from_slice(&[0x66, 0x90]); remaining -= 2;
72 }
73 3 => {
74 bytes.extend_from_slice(&[0x0F, 0x1F, 0x00]); remaining -= 3;
76 }
77 4 => {
78 bytes.extend_from_slice(&[0x0F, 0x1F, 0x40, 0x00]); remaining -= 4;
80 }
81 5 => {
82 bytes.extend_from_slice(&[0x0F, 0x1F, 0x44, 0x00, 0x00]); remaining -= 5;
84 }
85 6 => {
86 bytes.extend_from_slice(&[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00]); remaining -= 6;
88 }
89 7 => {
90 bytes.extend_from_slice(&[0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00]); remaining -= 7;
92 }
93 _ => {
94 bytes.extend_from_slice(&[0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]);
96 remaining -= 8;
97 }
98 }
99 }
100
101 bytes
102 }
103
104 fn find_instruction_boundary(code: &[u8], required_size: usize) -> Option<usize> {
105 let mut offset = 0;
106
107 while offset < required_size && offset < code.len() {
108 let insn = decode_instruction_x64(&code[offset..])?;
109 offset += insn.length;
110 }
111
112 if offset >= required_size {
113 Some(offset)
114 } else {
115 None
116 }
117 }
118
119 fn relocate_instruction(
120 instruction: &[u8],
121 old_address: usize,
122 new_address: usize,
123 ) -> Option<Vec<u8>> {
124 if instruction.is_empty() {
125 return None;
126 }
127
128 let decoded = decode_instruction_x64(instruction)?;
129
130 if !decoded.is_relative {
132 return Some(instruction[..decoded.length].to_vec());
133 }
134
135 match instruction[0] {
137 0xE8 => {
139 if instruction.len() < 5 {
140 return None;
141 }
142 let orig_offset = i32::from_le_bytes(instruction[1..5].try_into().ok()?);
143 let orig_target = (old_address as i64 + 5 + orig_offset as i64) as usize;
144 let new_offset = (orig_target as i64 - new_address as i64 - 5) as i32;
145
146 let mut bytes = vec![0xE8];
147 bytes.extend_from_slice(&new_offset.to_le_bytes());
148 Some(bytes)
149 }
150
151 0xE9 => {
153 if instruction.len() < 5 {
154 return None;
155 }
156 let orig_offset = i32::from_le_bytes(instruction[1..5].try_into().ok()?);
157 let orig_target = (old_address as i64 + 5 + orig_offset as i64) as usize;
158 let new_offset = (orig_target as i64 - new_address as i64 - 5) as i32;
159
160 let mut bytes = vec![0xE9];
161 bytes.extend_from_slice(&new_offset.to_le_bytes());
162 Some(bytes)
163 }
164
165 0xEB => {
167 if instruction.len() < 2 {
168 return None;
169 }
170 let orig_offset = instruction[1] as i8;
171 let orig_target = (old_address as i64 + 2 + orig_offset as i64) as usize;
172
173 let new_offset = (orig_target as i64 - new_address as i64 - 2) as i64;
175 if new_offset >= i8::MIN as i64 && new_offset <= i8::MAX as i64 {
176 Some(vec![0xEB, new_offset as u8])
177 } else {
178 let new_offset = (orig_target as i64 - new_address as i64 - 5) as i32;
180 let mut bytes = vec![0xE9];
181 bytes.extend_from_slice(&new_offset.to_le_bytes());
182 Some(bytes)
183 }
184 }
185
186 0x0F if instruction.len() >= 2 && (0x80..=0x8F).contains(&instruction[1]) => {
188 if instruction.len() < 6 {
189 return None;
190 }
191 let orig_offset = i32::from_le_bytes(instruction[2..6].try_into().ok()?);
192 let orig_target = (old_address as i64 + 6 + orig_offset as i64) as usize;
193 let new_offset = (orig_target as i64 - new_address as i64 - 6) as i32;
194
195 let mut bytes = vec![0x0F, instruction[1]];
196 bytes.extend_from_slice(&new_offset.to_le_bytes());
197 Some(bytes)
198 }
199
200 b if (0x70..=0x7F).contains(&b) => {
202 if instruction.len() < 2 {
203 return None;
204 }
205 let orig_offset = instruction[1] as i8;
206 let orig_target = (old_address as i64 + 2 + orig_offset as i64) as usize;
207
208 let new_offset = (orig_target as i64 - new_address as i64 - 6) as i32;
210 let long_opcode = 0x80 + (b - 0x70);
211 let mut bytes = vec![0x0F, long_opcode];
212 bytes.extend_from_slice(&new_offset.to_le_bytes());
213 Some(bytes)
214 }
215
216 _ => relocate_rip_relative_x64(instruction, old_address, new_address),
218 }
219 }
220
221 fn needs_relocation(instruction: &[u8]) -> bool {
222 if instruction.is_empty() {
223 return false;
224 }
225
226 match instruction[0] {
227 0xE8 | 0xE9 | 0xEB => true,
229 0x70..=0x7F => true,
231 0x0F if instruction.len() >= 2 && (0x80..=0x8F).contains(&instruction[1]) => true,
233 _ => has_rip_relative_addressing(instruction),
235 }
236 }
237}
238
239fn decode_instruction_x64(code: &[u8]) -> Option<DecodedInstruction> {
241 if code.is_empty() {
242 return None;
243 }
244
245 let mut offset = 0;
246
247 while offset < code.len() {
249 match code[offset] {
250 0x26 | 0x2E | 0x36 | 0x3E | 0x64 | 0x65 | 0x66 | 0x67 | 0xF0 | 0xF2 | 0xF3 => {
251 offset += 1;
252 }
253 _ => break,
254 }
255 }
256
257 if offset >= code.len() {
258 return None;
259 }
260
261 let has_rex = (0x40..=0x4F).contains(&code[offset]);
263 if has_rex {
264 offset += 1;
265 }
266
267 if offset >= code.len() {
268 return None;
269 }
270
271 let opcode = code[offset];
272 offset += 1;
273
274 let (length, is_relative, relative_target) = match opcode {
276 0x50..=0x5F | 0x90..=0x9F | 0xC3 | 0xCC | 0xCB | 0xCF => {
278 (offset, false, None)
279 }
280
281 0x68 => (offset + 4, false, None), 0x6A => (offset + 1, false, None), 0xC2 => (offset + 2, false, None),
287
288 0xE8 | 0xE9 => {
290 if code.len() < offset + 4 {
291 return None;
292 }
293 let rel = i32::from_le_bytes(code[offset..offset + 4].try_into().ok()?);
294 let target = (code.as_ptr() as usize + offset + 4).wrapping_add(rel as usize);
295 (offset + 4, true, Some(target))
296 }
297
298 0xEB => {
300 if code.len() < offset + 1 {
301 return None;
302 }
303 let rel = code[offset] as i8;
304 let target = (code.as_ptr() as usize + offset + 1).wrapping_add(rel as usize);
305 (offset + 1, true, Some(target))
306 }
307
308 0x70..=0x7F => {
310 if code.len() < offset + 1 {
311 return None;
312 }
313 let rel = code[offset] as i8;
314 let target = (code.as_ptr() as usize + offset + 1).wrapping_add(rel as usize);
315 (offset + 1, true, Some(target))
316 }
317
318 0xB8..=0xBF if has_rex && (code[offset - 2] & 0x08) != 0 => {
320 (offset + 8, false, None)
321 }
322
323 0xB8..=0xBF => (offset + 4, false, None),
325
326 0xB0..=0xB7 => (offset + 1, false, None),
328
329 0x0F => {
331 if offset >= code.len() {
332 return None;
333 }
334 let op2 = code[offset];
335 offset += 1;
336
337 match op2 {
338 0x80..=0x8F => {
340 if code.len() < offset + 4 {
341 return None;
342 }
343 let rel = i32::from_le_bytes(code[offset..offset + 4].try_into().ok()?);
344 let target = (code.as_ptr() as usize + offset + 4).wrapping_add(rel as usize);
345 (offset + 4, true, Some(target))
346 }
347 0x90..=0x9F => decode_modrm_instruction(code, offset, has_rex),
349 0x40..=0x4F => decode_modrm_instruction(code, offset, has_rex),
351 0xB6 | 0xB7 | 0xBE | 0xBF => decode_modrm_instruction(code, offset, has_rex),
353 _ => decode_modrm_instruction(code, offset, has_rex),
355 }
356 }
357
358 0x80 | 0x83 | 0xC0 | 0xC1 => {
360 let (len, is_rel, target) = decode_modrm_instruction(code, offset, has_rex);
361 (len + 1, is_rel, target) }
363
364 0x81 | 0xC7 => {
366 let (len, is_rel, target) = decode_modrm_instruction(code, offset, has_rex);
367 (len + 4, is_rel, target) }
369
370 0x6B => {
372 let (len, is_rel, target) = decode_modrm_instruction(code, offset, has_rex);
373 (len + 1, is_rel, target)
374 }
375
376 0x69 => {
378 let (len, is_rel, target) = decode_modrm_instruction(code, offset, has_rex);
379 (len + 4, is_rel, target)
380 }
381
382 0xC6 => {
384 let (len, is_rel, target) = decode_modrm_instruction(code, offset, has_rex);
385 (len + 1, is_rel, target)
386 }
387
388 0x00..=0x3F | 0x84..=0x8F | 0xD0..=0xD3 | 0xF6..=0xF7
390 | 0xFE..=0xFF | 0x63 | 0x8D => {
391 decode_modrm_instruction(code, offset, has_rex)
392 }
393
394 0xCC => (offset, false, None),
396
397 0xCD => (offset + 1, false, None),
399
400 0x04 | 0x0C | 0x14 | 0x1C | 0x24 | 0x2C | 0x34 | 0x3C => (offset + 1, false, None), 0x05 | 0x0D | 0x15 | 0x1D | 0x25 | 0x2D | 0x35 | 0x3D => (offset + 4, false, None), _ => decode_modrm_instruction(code, offset, has_rex),
406 };
407
408 Some(DecodedInstruction {
409 length,
410 is_relative,
411 relative_target,
412 })
413}
414
415fn decode_modrm_instruction(code: &[u8], offset: usize, _has_rex: bool) -> (usize, bool, Option<usize>) {
417 if offset >= code.len() {
418 return (offset, false, None);
419 }
420
421 let modrm = code[offset];
422 let mod_field = (modrm >> 6) & 0x03;
423 let rm = modrm & 0x07;
424
425 let mut len = offset + 1; let is_rip_relative = mod_field == 0 && rm == 5;
429
430 match mod_field {
431 0b00 => {
432 if rm == 4 {
433 len += 1;
435 if len <= code.len() && (code[len - 1] & 0x07) == 5 {
436 len += 4; }
438 } else if rm == 5 {
439 len += 4; }
441 }
442 0b01 => {
443 if rm == 4 {
444 len += 1; }
446 len += 1; }
448 0b10 => {
449 if rm == 4 {
450 len += 1; }
452 len += 4; }
454 0b11 => {
455 }
457 _ => {}
458 }
459
460 (len.min(code.len()), is_rip_relative, None)
461}
462
463fn has_rip_relative_addressing(instruction: &[u8]) -> bool {
465 if instruction.is_empty() {
466 return false;
467 }
468
469 let mut offset = 0;
470
471 while offset < instruction.len() {
473 match instruction[offset] {
474 0x26 | 0x2E | 0x36 | 0x3E | 0x64 | 0x65 | 0x66 | 0x67 | 0xF0 | 0xF2 | 0xF3 => {
475 offset += 1;
476 }
477 _ => break,
478 }
479 }
480
481 if offset < instruction.len() && (0x40..=0x4F).contains(&instruction[offset]) {
483 offset += 1;
484 }
485
486 if offset >= instruction.len() {
487 return false;
488 }
489
490 let opcode = instruction[offset];
492 offset += 1;
493
494 if opcode == 0x0F && offset < instruction.len() {
495 offset += 1; }
497
498 if offset >= instruction.len() {
500 return false;
501 }
502
503 let modrm = instruction[offset];
504 let mod_field = (modrm >> 6) & 0x03;
505 let rm = modrm & 0x07;
506
507 mod_field == 0 && rm == 5
509}
510
511fn relocate_rip_relative_x64(
513 instruction: &[u8],
514 old_address: usize,
515 new_address: usize,
516) -> Option<Vec<u8>> {
517 if !has_rip_relative_addressing(instruction) {
518 return Some(instruction.to_vec());
520 }
521
522 let decoded = decode_instruction_x64(instruction)?;
523 let mut result = instruction[..decoded.length].to_vec();
524
525 let mut modrm_offset = 0;
527
528 while modrm_offset < result.len() {
530 match result[modrm_offset] {
531 0x26 | 0x2E | 0x36 | 0x3E | 0x64 | 0x65 | 0x66 | 0x67 | 0xF0 | 0xF2 | 0xF3 => {
532 modrm_offset += 1;
533 }
534 _ => break,
535 }
536 }
537
538 if modrm_offset < result.len() && (0x40..=0x4F).contains(&result[modrm_offset]) {
540 modrm_offset += 1;
541 }
542
543 if modrm_offset < result.len() {
545 modrm_offset += 1;
546 if result[modrm_offset - 1] == 0x0F && modrm_offset < result.len() {
547 modrm_offset += 1;
548 }
549 }
550
551 if modrm_offset >= result.len() {
553 return Some(result);
554 }
555
556 let disp_offset = modrm_offset + 1;
558 if disp_offset + 4 > result.len() {
559 return Some(result);
560 }
561
562 let orig_disp = i32::from_le_bytes(result[disp_offset..disp_offset + 4].try_into().ok()?);
564
565 let insn_end = old_address + decoded.length;
567 let orig_target = (insn_end as i64 + orig_disp as i64) as usize;
568
569 let new_insn_end = new_address + decoded.length;
571 let new_disp = (orig_target as i64 - new_insn_end as i64) as i32;
572
573 let target_diff = orig_target as i64 - new_insn_end as i64;
575 if target_diff < i32::MIN as i64 || target_diff > i32::MAX as i64 {
576 return None;
578 }
579
580 result[disp_offset..disp_offset + 4].copy_from_slice(&new_disp.to_le_bytes());
582
583 Some(result)
584}
585
586#[cfg(test)]
587mod tests {
588 use super::*;
589
590 #[test]
591 fn test_encode_jmp_rel_near() {
592 let bytes = X64::encode_jmp_rel(0x1000, 0x1100).unwrap();
594 assert_eq!(bytes.len(), 5);
595 assert_eq!(bytes[0], 0xE9);
596 let offset = i32::from_le_bytes(bytes[1..5].try_into().unwrap());
598 assert_eq!(offset, 0xFB);
599 }
600
601 #[test]
602 fn test_encode_jmp_rel_far() {
603 let result = X64::encode_jmp_rel(0x0000_0000_0000_1000, 0x0000_0001_0000_0000);
605 assert!(result.is_none());
606 }
607
608 #[test]
609 fn test_encode_jmp_abs() {
610 let bytes = X64::encode_jmp_abs(0xDEADBEEF12345678);
611 assert_eq!(bytes.len(), 14);
612 assert_eq!(&bytes[0..6], &[0xFF, 0x25, 0x00, 0x00, 0x00, 0x00]);
613 let addr = u64::from_le_bytes(bytes[6..14].try_into().unwrap());
614 assert_eq!(addr, 0xDEADBEEF12345678);
615 }
616
617 #[test]
618 fn test_nop_sled() {
619 for size in 1..=16 {
620 let bytes = X64::encode_nop_sled(size);
621 assert_eq!(bytes.len(), size);
622 }
623 }
624
625 #[test]
626 fn test_decode_push_rbp() {
627 let code = [0x55];
629 let decoded = decode_instruction_x64(&code).unwrap();
630 assert_eq!(decoded.length, 1);
631 assert!(!decoded.is_relative);
632 }
633
634 #[test]
635 fn test_decode_mov_rbp_rsp() {
636 let code = [0x48, 0x89, 0xE5];
638 let decoded = decode_instruction_x64(&code).unwrap();
639 assert_eq!(decoded.length, 3);
640 assert!(!decoded.is_relative);
641 }
642
643 #[test]
644 fn test_decode_sub_rsp_imm8() {
645 let code = [0x48, 0x83, 0xEC, 0x28];
647 let decoded = decode_instruction_x64(&code).unwrap();
648 assert_eq!(decoded.length, 4);
649 assert!(!decoded.is_relative);
650 }
651
652 #[test]
653 fn test_find_instruction_boundary() {
654 let code = [0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x28];
656
657 let boundary = X64::find_instruction_boundary(&code, 5).unwrap();
659 assert!(boundary >= 5);
660 assert!(boundary <= 8);
661 }
662}