3

Currently I have written a code segment using assembly on one page pageA in linux kernel.The code is

SYM_CODE_START(sr_function)
    movq $0, %rdx
    movq $0x7ffff7fc6000, %rsi 
    movq $0x19016bc83000, %rcx
    movq $9, %rdx
    movq $0, %rdx
    jmp goto_fce_func
SYM_CODE_END(sr_function)

At run time , I would like to overwrite the first operad of mov instruction via c pointer, For example, I map the page pageA to kernel space using kmap(pageA).

That means, Now the first instruction movq $0, %rdx has the address for example 0x1000.

I would like to use C pointer to:

  1. change the first operand from value 0 to 0x19016bc83000 for the first mov instruction movq $0, %rdx

  2. change the first operand from value 0x7ffff7fc6000 to 0x19016bc74000 for the secondmov instruction movq $0x7ffff7fc6000, %rsi

  3. change the first operand from value 0x19016bc83000 to 0 for the thirdmov instruction movq $0x19016bc83000, %rcx

Do you know how could i do this?

the Disassembly of this code segment by using objdump -D shows

0000000000001000 <sr_function>:
    1000:   48 c7 c2 00 00 00 00    mov    $0x0,%rdx
    1007:   48 be 00 60 fc f7 ff    movabs $0x7ffff7fc6000,%rsi
    100e:   7f 00 00 
    1011:   48 b9 00 30 c8 6b 01    movabs $0x19016bc83000,%rcx
    1018:   19 00 00 
    101b:   48 c7 c2 09 00 00 00    mov    $0x9,%rdx
    1022:   48 c7 c2 00 00 00 00    mov    $0x0,%rdx
    1029:   e9 de ef ff ff          jmpq   c <goto_fce_func>
Zilog80
  • 2,534
  • 2
  • 15
  • 20
yaoxin jing
  • 55
  • 1
  • 4
  • In disassemble you may find offsets of bytes which contain instruction parameters which you want to modify. Use these offsets at runtime for find bytes and perform modifications. – Tsyvarev Aug 20 '21 at 06:25
  • Be aware that on some architectures code pages may be set non-writable and data pages may be noexec. – stark Aug 20 '21 at 10:36

1 Answers1

6
1000:   48 c7 c2 00 00 00 00    mov    $0x0,%rdx
1007:   48 be 00 60 fc f7 ff    movabs $0x7ffff7fc6000,%rsi
100e:   7f 00 00 
1011:   48 b9 00 30 c8 6b 01    movabs $0x19016bc83000,%rcx

Your first instruction will be very difficult to patch, because you want to patch it with a 64-bit value but it's currently encoded to deal with a 32-bit immediate.

Let's take the instruction - mov $0x0, %rdx and understand its encoding byte-by-byte:

  • 0x48 - this prefix byte specifies that the instruction to follow is 64-bit sized.
  • 0xc7 - this specifies that the instruction is a mov reg, imm32 instruction
  • 0xc2 - this specifies the target register (encoded in the bottom 3 bits) - in this case, rdx (which is register number 2)
  • 0x00, 0x00, 0x00, 0x00 - encode your 32-bit immediate in little endian.

When this happens, the top 32 bits of rdx are zeroed out anyways - but this prevents you from patching the immediate directly, because you need access to all 64 bits.

The simplest solution for this is to patch your assembly instruction to be movabs $0, %rdx - for which the assembler will output a patchable instruction.


And as for the following two movabs instructions - they have a similar encoding, except that their second byte (0xbe for the first and 0xb9 for the second) encode the destination register inside the byte: they belong to the 0xb8 + r opcodes, where the +r encodes the destination register: 0xbe == 0xb8 + 6 and rsi is register number 6, 0xb9 == 0xb8 + 1 and rcx is register number 1.


Once you have three instructions that each have a 64-bit immediate to patch, the patching itself is easy. You need a char* variable that points to the instruction you want to patch - then you want to increment it by the appropriate amount of bytes so that it points to the offset - and then you want to cast it to a uint64_t* - and patch it appropriately.

char* ptr; // points to e.g. the last movabs instructions
ptr += 2; // skip over 0x48 and 0xb9
*((uint64_t*)ptr) = 0; // zero out the entire immediate
Daniel Kleinstein
  • 5,262
  • 1
  • 22
  • 39
  • Okay, thanks, This really helped me. Beyoud the topic, I was wondering, where do you leaned the encoding stuff? Could you please provide me some links so that I could learn more about encoding of assembly? – yaoxin jing Aug 20 '21 at 10:28
  • 1
    Sure - probably the best thorough way to learn is to read Chapter 2 of Volume 2 of Intel's x86 SDM: https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-2a-2b-2c-and-2d-instruction-set-reference-a-z.html - and once you've understood how basic encoding works, this website is a great x86 reference - http://ref.x86asm.net/coder64.html - and in general, reverse engineering x86 assemblies without using a decompiler is a great way to learn more through hands-on experience. – Daniel Kleinstein Aug 20 '21 at 10:58
  • `orq $0, %rdx` also only takes a 32-bit immediate, so I don't see how that'll help. AFAIK the only way to load a 64-bit immediate in one instruction is `movabs`. If you get to modify the original assembly, can you not just do `movabs $0, %rdx`, assuming you can prevent the assembler from "optimizing" it into `mov`? – Nate Eldredge Aug 21 '21 at 22:55
  • @NateEldredge You're right on both accounts :) I'll fix this – Daniel Kleinstein Aug 22 '21 at 05:16