简体   繁体   中英

How to overwrite operand value of x86 `movq` instruction via c pointer in linux kernel

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 second mov instruction movq $0x7ffff7fc6000, %rsi

  3. change the first operand from value 0x19016bc83000 to 0 for the third mov 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>
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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM