Closed injertao closed 1 year ago
You could also calculate the absolute address and relocate it manually
// pseudocode
get_abs(addr_instrn, offset_to_disp)
return addr_instrn + *(int32_t*)(addr_instrn + offset_to_disp) + sizeof(int32_t) + offset_to_disp;
With this knowledge relocating it should be a piece of cake.
@oberrich Thank you for your answer, but it doesn't seem to be what I want. E8 E132BA01 == > E8 3B320234 I want to get 3B320234 through E132BA01
You have to calculate the absolute address and make it relative to its new location. The numbers after the E8
opcode are rip-relative displacements, see https://www.felixcloutier.com/x86/call.html
@oberrich Yes, I understand that an absolute address is required, but when it is in a new runtime address, how should I obtain the relative code
I want to transfer "call Dumped. exe+2BA3240" from "Dumped. exe+FFFF5A" to the new address "0x00620000". What shall I do? thanks
When I use ZydisEncoderEncodeInstruction, how do I make runtime-address work? Can you see what I mean?
I haven't used the encoder myself but from taking a quick look at it maybe a combination of ZydisEncoderDecodedInstructionToEncoderRequest
and ZydisEncoderEncodeInstruction
could work.
With the information I have provided before it's trivial to do even without the encoder.
You can also just calculate the absolute address and rewrite the call to use an absolute address like so:
movabs rax, 0xdeadbeef
call rax
or even something like this if you dont know which register is safe to use:
call [addr_data]
jmp addr_data_end
addr_data: 0xdeadbeef
addr_data_end:
...
Thank you. It seems that this is the only way. This is a problem that has existed before. I think the 4.0 update can solve it
Seems like you are looking for ZydisEncoderEncodeInstructionAbsolute
. It accepts absolute addresses and converts them to RIP-relative ones: https://doc.zydis.re/v4.0.0/html/group__encoder#ga8c631aa30e550914d953ca7e1dea4db8
@mappzor It doesn't seem to work as expected Old Code and runtime-address: 210E848A - 8B 05 DC4A9D01 - mov eax,[22ABCF6C] New Code and runtime-address: 1DE12636B3A - A1 6CCFAB2200000000 - mov eax,[22ABCF6C]
ZydisEncoderEncodeInstructionAbsolute(&req, encoded_instruction, &encoded_length, 0x1DE12636B3A) return ZYAN_STATUS_INVALID_ARGUMENT
Can you show how encoder request gets constructed? Did you initialize encoded_length
correctly?
@mappzor
// Initialize decoder context ZydisDecoder decoder; ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); // Initialize formatter. Only required when you actually plan to do instruction // formatting ("disassembling"), like we do here ZydisFormatter formatter; ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); // Loop over the instructions in our buffer. // The runtime-address (instruction pointer) is chosen arbitrary here in order to better // visualize relative addressing ZyanUSize offset = 0x10; const ZyanUSize length = sizeof(code4); ZydisDecodedInstruction instruction; ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; ULONG_PTR newRuntimeAddress = (ULONG_PTR)newData; while (ZYAN_SUCCESS(ZydisDecoderDecodeFull(&decoder, code4 + offset, length - offset, &instruction, operands))) { { ZydisEncoderRequest req; ExpectSuccess(ZydisEncoderDecodedInstructionToEncoderRequest(&instruction, operands, instruction.operand_count_visible, &req)); ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH]; ZyanUSize encoded_length = sizeof(encoded_instruction); if (ZYAN_FAILED(ZydisEncoderEncodeInstructionAbsolute(&req, encoded_instruction, &encoded_length, ULONG_PTR(code4 + offset)))) { puts("123"); } newRuntimeAddress += encoded_length; } offset += instruction.length; }
@mappzor It has changed from 6 bytes to 9 bytes. How can I predict the size and data of 9 bytes
I'm a bit confused. Earlier you've mentioned failed encoding with ZYAN_STATUS_INVALID_ARGUMENT
. Now you mention changed instruction size (which is perfectly fine). So which one is the case here?
@mappzor Sorry, my English is not very good. In fact, I just want to decode and write to a new runtime-address and calculate the new byte size
210E848A - 8B 05 DC4A9D01 - mov eax,[22ABCF6C]
1DE12636B3A - A1 6CCFAB2200000000 - mov eax,[22ABCF6C]
I want to calculate the byte 8B 05 DC4A9D01
of 210E848A
into A1 6CCFAB2200000000
and write it into 1DE12636B3A
, so that the program can operate normally
Algorithm looks as follows:
ZydisEncoderDecodedInstructionToEncoderRequest
ZydisEncoderEncodeInstructionAbsolute
.
ZYDIS_OPERAND_TYPE_IMMEDIATE
check if decoded operand has is_relative
=> use ZydisCalcAbsoluteAddress
to convert to absoluteZYDIS_OPERAND_TYPE_MEMORY
check if base address is RIP/EIP
=> use ZydisCalcAbsoluteAddress
to convert to absoluteZydisEncoderEncodeInstructionAbsolute
ZydisEncoderEncodeInstruction
encodes immediates/displacements as supplied inside encoder request without any modifications:
memset(&req, 0, sizeof(req));
req.machine_mode = ZYDIS_MACHINE_MODE_LONG_64;
req.mnemonic = ZYDIS_MNEMONIC_JMP;
req.operand_count = 1;
req.operands[0].type = ZYDIS_OPERAND_TYPE_IMMEDIATE;
req.operands[0].imm.u = 0x11223344;
// Produces: E9 44 33 22 11
ZydisEncoderEncodeInstructionAbsolute
expects absolute addresses in place of immediates/displacements, so 0x11223344 gets interpreted as desired destination for the jump. For example:
ZydisEncoderEncodeInstructionAbsolute
with base address 0 will produce E9 3F 33 22 11
because 0 (base address) + 5 (instruction length) + 0x1122333F = 0x11223344ZydisEncoderEncodeInstructionAbsolute
with base address 0x00400000 will produce E9 3F 33 E2 10
because 0x00400000 (base address) + 5 (instruction length) + 0x10E2333F = 0x11223344@mappzor I still can't seem to get what I need
I don't even know the difference between ZydisEncoderEncodeInstructionAbsolute
and ZydisEncoderEncodeInstruction
I just want to get 9 bytes of data based on the original 6 bytes
For example:
210E848A - 8B 05 DC4A9D01 - mov eax,[22ABCF6C]
1DE12636B3A - A1 6CCFAB2200000000 - mov eax,[22ABCF6C]
8B 05 DC4A9D01
= > A1 6CCFAB2200000000
Sorry, I may be stupid, or you don't fully understand what I mean
@mappzor
operands[i].encoding = ZYDIS_OPERAND_ENCODING_JIMM32; instruction.raw.imm->size = 32; instruction.raw.imm->offset = 2; instruction.meta.branch_type = ZYDIS_BRANCH_TYPE_NEAR; operands[i].size = 32; operands[i].element_size = 32; operands[i].encoding = ZYDIS_OPERAND_ENCODING_DISP16_32_64; operands[i].mem.base = ZYDIS_REGISTER_NONE;
I found that these parameters need to be changed before using
ZydisEncoderEncodeInstructionAbsolute
, but whether there is a function that can intelligently change it, long jump or short jump, relative or absolute
Please avoid deleting and heavy editing existing comments. This makes discussion really hard to follow.
I don't even know the difference between ZydisEncoderEncodeInstructionAbsolute and ZydisEncoderEncodeInstruction
See my previous comment for detailed explanation. It's useful for relocating rip-relative memory operands and jumps in a generic manner.
8B 05 DC4A9D01 = > A1 6CCFAB2200000000
If you want to force movabs
you need to set base register to none (instead of RIP) and displacement to absolute address (use ZydisCalcAbsoluteAddress
). That's a special case you will have to handle manually. Other instructions cannot be handled in the same way.
I found that these parameters need to be changed before using ZydisEncoderEncodeInstructionAbsolute, but whether there is a function that can intelligently change it, long jump or short jump, relative or absolute
Set branch_type
to ZYDIS_BRANCH_TYPE_NONE
. See documentation for details: https://doc.zydis.re/v4.0.0/html/structZydisEncoderRequest__#a0fc13a5a26ccb4d406021418c65b5556
This is the target assembly
210E848A - 8B 05 DC4A9D01 - mov eax,[22ABCF6C]
After I modify the parameters as follows:
if (operands[i].type == ZYDIS_OPERAND_TYPE_MEMORY) { ZyanU64 absolute_address = NULL; ZydisCalcAbsoluteAddress(&instruction, &operands[i],0x210E848A, &absolute_address); operands[i].mem.disp.value = absolute_address; operands[i].mem.base = ZYDIS_REGISTERNONE; } I got it. It seems that everything is normal, but `encoded Length
is equal to 6, not 10
1CEE6570000 - 67 A1 6CCFAB2200000000 - mov eax,[22ABCF6C]`
@mappzor
if (IsIMM)
{
operands[i].encoding = ZYDIS_OPERAND_ENCODING_JIMM64;
instruction.raw.imm->size = 64;
instruction.raw.imm->offset = 4;
instruction.meta.branch_type = ZYDIS_BRANCH_TYPE_FAR;
operands[i].size = 64;
operands[i].element_size = 64;
}
Whether there are parameters similar to ZYDIS_BRANCH_TYPE_NONE
in encoding
Don't change anything in decoded instruction and operands. You are supposed to decode -> ZydisEncoderDecodedInstructionToEncoderRequest
-> customize encoder request -> encode.
@mappzor
ZyanUSize offset = 0x10;
const ZyanUSize length = sizeof(code4);
ZydisDecodedInstruction instruction;
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
ULONG_PTR newRuntimeAddress = (ULONG_PTR)newData;
while (ZYAN_SUCCESS(ZydisDecoderDecodeFull(&decoder, code4 + offset, length - offset,
&instruction, operands)))
{
{
for (size_t i = 0; i < instruction.operand_count_visible; i++)
{
if (operands[i].type == ZYDIS_OPERAND_TYPE_MEMORY)
{
ZyanU64 absolute_address = NULL;
ZydisCalcAbsoluteAddress(&instruction, &operands[i],0x210E848A, &absolute_address);
operands[i].mem.disp.value = absolute_address;
operands[i].mem.base = ZYDIS_REGISTER_NONE;
}
else if (operands[i].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && operands[i].imm.is_relative)
{
ZyanU64 absolute_address = NULL;
ZydisCalcAbsoluteAddress(&instruction, &operands[i], newRuntimeAddress, &absolute_address);
operands[i].imm.value.s = absolute_address;
}
}
ZydisEncoderRequest req;
ExpectSuccess(ZydisEncoderDecodedInstructionToEncoderRequest(&instruction, operands,
instruction.operand_count_visible, &req));
req.branch_type = ZYDIS_BRANCH_TYPE_NONE;
ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH];
ZyanUSize encoded_length = sizeof(encoded_instruction);
if (ZYAN_FAILED(ZydisEncoderEncodeInstructionAbsolute(&req, encoded_instruction, &encoded_length, 0x210E848A)))
{
puts("123");
}
newRuntimeAddress += encoded_length;
}
offset += instruction.length;
}
}
Before ZydisEncoderDecodedInstructionToEncoderRequest
, I only modified operators
. Is that right?
By analogy, before ZydisEncoderEncodeInstructionAbsolute
, I should only modify req
, right?
If I understand correctly, then the encoded_length
I get is still 6 instead of 10,
But I found an interesting thing,
operands[i].mem.disp.value = absolute_ address;
I'm here absolute_Address+=0x100000000
, you can correctly get the code I want, encoded_Length
is 9
It seems that in the ZydisEncoderEncodeInstructionAbsolute
, the length of the calculation instruction is based on the size of operands [i]. mem.disp.value
. Sometimes, though the value is less than 0xFFFFFFFF, I still want to get an 8-byte absolute address code
Before ZydisEncoderDecodedInstructionToEncoderRequest, I only modified operators. Is that right?
No, don't do this. Apply all modifications to encoder request ONLY (req.operands
).
It seems that in the ZydisEncoderEncodeInstructionAbsolute, the length of the calculation instruction is based on the size of operands [i]. mem.disp.value. Sometimes, though the value is less than 0xFFFFFFFF, I still want to get an 8-byte absolute address code
If you want to change rip-relative movs to movabs
you need to do that manually (see my previous comment). Encoder will never do that for you automatically.
@mappzor
No, don't do this. Apply all modifications to encoder request ONLY (req.operands).
while (ZYAN_SUCCESS(ZydisDecoderDecodeFull(&decoder, code4 + offset, length - offset,
&instruction, operands)))
{
ZydisEncoderRequest req;
ExpectSuccess(ZydisEncoderDecodedInstructionToEncoderRequest(&instruction, operands,
instruction.operand_count_visible, &req));
req.branch_type = ZYDIS_BRANCH_TYPE_NONE;
for (size_t i = 0; i < req.operand_count; i++)
{
if (req.operands[i].type == ZYDIS_OPERAND_TYPE_MEMORY)
{
ZyanU64 absolute_address = NULL;
ZydisCalcAbsoluteAddress(&instruction, &operands[i], 0x210E848A, &absolute_address);
req.operands[i].mem.displacement = absolute_address;
req.operands[i].mem.base = ZYDIS_REGISTER_NONE;
}
else if (operands[i].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && operands[i].imm.is_relative)
{
ZyanU64 absolute_address = NULL;
ZydisCalcAbsoluteAddress(&instruction, &operands[i], newRuntimeAddress, &absolute_address);
req.operands[i].imm.s = absolute_address;
}
}
ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH];
ZyanUSize encoded_length = sizeof(encoded_instruction);
if (ZYAN_FAILED(ZydisEncoderEncodeInstructionAbsolute(&req, encoded_instruction, &encoded_length, 0x210E848A)))
{
puts("123");
}
newRuntimeAddress += encoded_length;
offset += instruction.length;
}
If you want to change rip-relative movs to movabs you need to do that manually (see my previous comment). Encoder will never do that for you automatically.
After I modify some parameters, is it impossible for the program to automatically modify them for me?
210E848A - 8B 05 DC4A9D01 - mov eax,[22ABCF6C]
I want to expand it to 9 bytes or 10 bytes, because in the new runtime-address
, its relative address may be greater than 4 bytes
Your code does exactly what you expect. In this case (address in first 4GB of address space) encoder is able to deal with it just with 6 bytes but code will behave correctly for any address.
@mappzor
I have an unreasonable trick,
req.operands[i].mem.displacement = absolute_address;
When I add a piece of code
req.operands[i].mem. displacement += 0x100000000
ZydisEncoderEncodeInstructionAbsolute
can return the result I expect, 9 bytes, and then I execute:
req.operands[i].mem. displacement -= 0x100000000
@flobernd @mappzor
I think ZydisGetUnsignedImmSize
is not perfect in obtaining size, perhaps imm<=ZYAN_ UINT32_ MAX
, but I need an 8-byte slot and need to return 64 bytes. When it comes to absolute addresses, 4 bytes obviously cannot meet my requirements
Like this:1DE12636B3A - A1 6CCFAB2200000000 - mov eax,[22ABCF6C]
0x22ABCF6C <= ZYAN_ UINT32_ MAX
but,6CCFAB2200000000 == 64
The size should be calculated according to absolute_address(0x1DE12636B3A)
and absolute_address(0x22ABCF6C)
I need an 8-byte slot
No, you don't because 0x22ABCF6C fits in 4 bytes.
The size should be calculated according to absolute_address(0x1DE12636B3A) and absolute_address(0x22ABCF6C)
And it is. Try passing 0x1DE12636B3A and you will get a 9-byte instruction. Encoder picks the shortest encoding that can do the job.
No, you don't because 0x22ABCF6C fits in 4 bytes.
I'm sure I need 8 bytes. Although 0x22ABCF6C is 4 bytes, when the runtime address is greater than 0xFFFFFFFF, the relative addressing is obviously greater than 0x7FFFFFFF,
And it is. Try passing 0x1DE12636B3A and you will get a 9-byte instruction. Encoder picks the shortest encoding that can do the job.
1DE12636B3A - A1 6CCFAB2200000000 - mov eax,[22ABCF6C]
You've misunderstood.,Obviously, 1DE12636B3A
and 22ABCF6C
are not of the same type
You have changed runtime address instead of displacement, so I don't know what were you expecting with that change. You are using absolute mov instead of rip-relative, so runtime address doesn't matter. It seems you don't really understand the difference between the two and tried to bruteforce your way through this. Encoder will pick correct, shortest encoding for the requested mov variant:
memset(&req, 0, sizeof(req));
req.machine_mode = ZYDIS_MACHINE_MODE_LONG_64;
req.mnemonic = ZYDIS_MNEMONIC_MOV;
req.operand_count = 2;
req.operands[0].type = ZYDIS_OPERAND_TYPE_REGISTER;
req.operands[0].reg.value = ZYDIS_REGISTER_EAX;
req.operands[1].type = ZYDIS_OPERAND_TYPE_MEMORY;
req.operands[1].mem.size = 4;
req.operands[1].mem.displacement = 0x22ABCF6C;
// Produces: 67 A1 6C CF AB 22
req.machine_mode = ZYDIS_MACHINE_MODE_LONG_64;
req.mnemonic = ZYDIS_MNEMONIC_MOV;
req.operand_count = 2;
req.operands[0].type = ZYDIS_OPERAND_TYPE_REGISTER;
req.operands[0].reg.value = ZYDIS_REGISTER_EAX;
req.operands[1].type = ZYDIS_OPERAND_TYPE_MEMORY;
req.operands[1].mem.size = 4;
req.operands[1].mem.displacement = 0x1DE12636B3A;
// Produces: A1 3A 6B 63 12 DE 01 00 00
For further information about adressing modes consult Intel Manual:
You won't progress without understanding key difference between those adressing modes. It's essential for binary rewriting and require careful handling of different possible cases. It seems to me that your problem is more about understanding the ISA itself rather than Zydis.
@mappzor Thank you very much for your answer. I'd better try to use violence
ZyanU8 ZydisGetUnsignedImmSize(ZyanU64 imm, ZyanU64 new_runtime_address)
{
if (new_runtime_address != NULL)
{
imm = imm > new_runtime_address ? imm - new_runtime_address : new_runtime_address - imm;
}
if (imm <= ZYAN_UINT8_MAX)
{
return 8;
}
if (imm <= ZYAN_UINT16_MAX)
{
return 16;
}
if (imm <= ZYAN_UINT32_MAX)
{
return 32;
}
return 64;
}
You are very nice
Dumped.exe+FFFF5A - E8 E132BA01 - call Dumped.exe+2BA3240 ==>00620000 - E8 3B320234 - call Dumped.exe+2BA3240 E8 E132BA01 == > E8 3B320234
I hope it can overload the in the new runtime-address And calculate relative addressing Please tell me which example to refer to Thanks.