Skip to content

Commit 1bd3386

Browse files
committed
Added function to calculate and encode relative operands
1 parent 73a4492 commit 1bd3386

File tree

8 files changed

+345
-10
lines changed

8 files changed

+345
-10
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.DS_Store
22
.vscode
33
.idea
4+
.vs
45
__pycache__
56

67
/build*

include/Zydis/Encoder.h

+18
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,24 @@ typedef struct ZydisEncoderRequest_
400400
ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstruction(const ZydisEncoderRequest *request,
401401
void *buffer, ZyanUSize *length);
402402

403+
/**
404+
* Encodes instruction with semantics specified in encoder request structure. This function expects
405+
* absolute addresses inside encoder request instead of `EIP`/`RIP`-relative values. Function
406+
* predicts final instruction length prior to encoding and writes back calculated relative operands
407+
* to provided encoder request.
408+
*
409+
* @param request A pointer to the `ZydisEncoderRequest` struct.
410+
* @param buffer A pointer to the output buffer receiving encoded instruction.
411+
* @param length A pointer to the variable containing length of the output buffer. Upon
412+
* successful return this variable receives length of the encoded
413+
instruction.
414+
* @param runtime_address The runtime address of the instruction.
415+
*
416+
* @return A zyan status code.
417+
*/
418+
ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstructionAbsolute(ZydisEncoderRequest *request,
419+
void *buffer, ZyanUSize *length, ZyanU64 runtime_address);
420+
403421
/**
404422
* Converts decoded instruction to encoder request that can be passed to
405423
* `ZydisEncoderEncodeInstruction`.

include/Zydis/Internal/EncoderData.h

+31
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,27 @@ typedef struct ZydisEncodableInstruction_
201201

202202
#pragma pack(pop)
203203

204+
/**
205+
* Contains information used by instruction size prediction algorithm inside
206+
* `ZydisEncoderEncodeInstructionAbsolute`.
207+
*/
208+
typedef struct ZydisEncoderRelInfo_
209+
{
210+
/**
211+
* Sizes of instruction variants. First index is effective address size. Second index is
212+
* desired immediate size (8, 16 and 32 bits respectively).
213+
*/
214+
ZyanU8 size[3][3];
215+
/**
216+
* See `ZydisSizeHint`.
217+
*/
218+
ZyanU8 accepts_scaling_hints;
219+
/**
220+
* True if instruction accepts branch hint prefixes.
221+
*/
222+
ZyanBool accepts_branch_hints;
223+
} ZydisEncoderRelInfo;
224+
204225
/**
205226
* Fetches array of `ZydisEncodableInstruction` structures and its size for given instruction
206227
* mnemonic.
@@ -214,4 +235,14 @@ typedef struct ZydisEncodableInstruction_
214235
ZyanU8 ZydisGetEncodableInstructions(ZydisMnemonic mnemonic,
215236
const ZydisEncodableInstruction **instruction);
216237

238+
/**
239+
* Fetches `ZydisEncoderRelInfo` record for given instruction mnemonic.
240+
*
241+
* @param mnemonic Instruction mnemonic.
242+
*
243+
* @return Pointer to `ZydisEncoderRelInfo` structure or `ZYAN_NULL` if instruction doesn't have
244+
* relative operands.
245+
*/
246+
const ZydisEncoderRelInfo *ZydisGetRelInfo(ZydisMnemonic mnemonic);
247+
217248
#endif /* ZYDIS_INTERNAL_ENCODERDATA_H */

msvc/zydis/Zydis.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,7 @@
922922
<ClInclude Include="..\..\include\Zydis\Defines.h" />
923923
<ClInclude Include="..\..\include\Zydis\Encoder.h" />
924924
<ClInclude Include="..\..\include\Zydis\Formatter.h" />
925+
<ClInclude Include="..\..\include\Zydis\Internal\EncoderData.h" />
925926
<ClInclude Include="..\..\include\Zydis\MetaInfo.h" />
926927
<ClInclude Include="..\..\include\Zydis\Mnemonic.h" />
927928
<ClInclude Include="..\..\include\Zydis\Register.h" />

msvc/zydis/Zydis.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@
194194
<ClInclude Include="..\..\include\Zydis\Segment.h">
195195
<Filter>Header Files</Filter>
196196
</ClInclude>
197+
<ClInclude Include="..\..\include\Zydis\Internal\EncoderData.h">
198+
<Filter>Header Files\Internal</Filter>
199+
</ClInclude>
197200
</ItemGroup>
198201
<ItemGroup>
199202
<ResourceCompile Include="..\..\resources\VersionInfo.rc">

src/Encoder.c

+232-10
Original file line numberDiff line numberDiff line change
@@ -2965,7 +2965,7 @@ static ZyanStatus ZydisFindMatchingDefinition(const ZydisEncoderRequest *request
29652965
}
29662966
}
29672967
else if ((request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) &&
2968-
(definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
2968+
(definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
29692969
{
29702970
continue;
29712971
}
@@ -4275,6 +4275,33 @@ static ZyanStatus ZydisEncoderCheckRequestSanity(const ZydisEncoderRequest *requ
42754275
return ZYAN_STATUS_SUCCESS;
42764276
}
42774277

4278+
/**
4279+
* Encodes instruction with semantics specified in encoder request structure.
4280+
*
4281+
* @param request A pointer to the `ZydisEncoderRequest` struct. Must be validated before
4282+
* calling this function.
4283+
* @param buffer A pointer to the output buffer receiving encoded instruction.
4284+
* @param length A pointer to the variable containing length of the output buffer. Upon
4285+
* successful return this variable receives length of the encoded instruction.
4286+
* @param instruction Internal state of the encoder.
4287+
*
4288+
* @return A zyan status code.
4289+
*/
4290+
static ZyanStatus ZydisEncoderEncodeInstructionInternal(const ZydisEncoderRequest *request,
4291+
void *buffer, ZyanUSize *length, ZydisEncoderInstruction *instruction)
4292+
{
4293+
ZydisEncoderInstructionMatch match;
4294+
ZYAN_CHECK(ZydisFindMatchingDefinition(request, &match));
4295+
ZydisEncoderBuffer output;
4296+
output.buffer = (ZyanU8 *)buffer;
4297+
output.size = *length;
4298+
output.offset = 0;
4299+
ZYAN_CHECK(ZydisBuildInstruction(&match, instruction));
4300+
ZYAN_CHECK(ZydisEmitInstruction(instruction, &output));
4301+
*length = output.offset;
4302+
return ZYAN_STATUS_SUCCESS;
4303+
}
4304+
42784305
/* ============================================================================================== */
42794306
/* Exported functions */
42804307
/* ============================================================================================== */
@@ -4288,17 +4315,212 @@ ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstruction(const ZydisEncoderRequest
42884315
}
42894316
ZYAN_CHECK(ZydisEncoderCheckRequestSanity(request));
42904317

4291-
ZydisEncoderInstructionMatch match;
4292-
ZYAN_CHECK(ZydisFindMatchingDefinition(request, &match));
4293-
ZydisEncoderBuffer output;
4294-
output.buffer = (ZyanU8 *)buffer;
4295-
output.size = *length;
4296-
output.offset = 0;
42974318
ZydisEncoderInstruction instruction;
4298-
ZYAN_CHECK(ZydisBuildInstruction(&match, &instruction));
4299-
ZYAN_CHECK(ZydisEmitInstruction(&instruction, &output));
4319+
return ZydisEncoderEncodeInstructionInternal(request, buffer, length, &instruction);
4320+
}
4321+
4322+
ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstructionAbsolute(ZydisEncoderRequest *request,
4323+
void *buffer, ZyanUSize *length, ZyanU64 runtime_address)
4324+
{
4325+
if (!request || !buffer || !length)
4326+
{
4327+
return ZYAN_STATUS_INVALID_ARGUMENT;
4328+
}
4329+
ZYAN_CHECK(ZydisEncoderCheckRequestSanity(request));
4330+
4331+
const ZydisEncoderRelInfo *rel_info = ZydisGetRelInfo(request->mnemonic);
4332+
ZydisEncoderOperand *op_rip_rel = ZYAN_NULL;
4333+
ZyanBool adjusted_rel = ZYAN_FALSE;
4334+
ZyanU64 absolute_address = 0;
4335+
ZyanU8 mode_index = ZydisGetMachineModeWidth(request->machine_mode) >> 5;
4336+
for (ZyanU8 i = 0; i < request->operand_count; ++i)
4337+
{
4338+
ZydisEncoderOperand *op = &request->operands[i];
4339+
if ((op->type == ZYDIS_OPERAND_TYPE_IMMEDIATE) && rel_info)
4340+
{
4341+
if (adjusted_rel)
4342+
{
4343+
return ZYAN_STATUS_INVALID_ARGUMENT;
4344+
}
4345+
4346+
switch (rel_info->accepts_scaling_hints)
4347+
{
4348+
case ZYDIS_SIZE_HINT_NONE:
4349+
case ZYDIS_SIZE_HINT_OSZ:
4350+
{
4351+
static const ZyanI8 asz_priority[3][3] =
4352+
{
4353+
{ 0, 1, 2 },
4354+
{ 0, 2, 1 },
4355+
{ 0, 2, -1 },
4356+
};
4357+
static const ZyanI8 osz_priority[3][3] =
4358+
{
4359+
{ 0, 1, 2 },
4360+
{ 0, 2, 1 },
4361+
{ 0, 2, 1 },
4362+
};
4363+
ZyanI8 forced_priority_row[3] = { -1, -1, -1 };
4364+
ZyanI8 *priority_row = ZYAN_NULL;
4365+
ZyanU8 extra_length = 0;
4366+
ZyanU8 start_offset = 0;
4367+
if (rel_info->accepts_scaling_hints == ZYDIS_SIZE_HINT_NONE)
4368+
{
4369+
if ((request->branch_type == ZYDIS_BRANCH_TYPE_FAR) ||
4370+
(request->branch_width == ZYDIS_BRANCH_WIDTH_64))
4371+
{
4372+
return ZYAN_STATUS_INVALID_ARGUMENT;
4373+
}
4374+
if ((rel_info->accepts_branch_hints) &&
4375+
(request->prefixes & (ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN |
4376+
ZYDIS_ATTRIB_HAS_BRANCH_TAKEN)))
4377+
{
4378+
extra_length = 1;
4379+
}
4380+
if (request->branch_width == ZYDIS_BRANCH_WIDTH_NONE)
4381+
{
4382+
if (request->branch_type == ZYDIS_BRANCH_TYPE_NEAR)
4383+
{
4384+
start_offset = 1;
4385+
}
4386+
priority_row = (ZyanI8 *)&asz_priority[mode_index];
4387+
}
4388+
else
4389+
{
4390+
forced_priority_row[0] = request->branch_width - 1;
4391+
priority_row = (ZyanI8 *)&forced_priority_row;
4392+
}
4393+
}
4394+
else
4395+
{
4396+
if (request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_NONE)
4397+
{
4398+
priority_row = (ZyanI8 *)&osz_priority[mode_index];
4399+
}
4400+
else
4401+
{
4402+
if (request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_64)
4403+
{
4404+
extra_length = 1;
4405+
forced_priority_row[0] = 2;
4406+
}
4407+
else
4408+
{
4409+
forced_priority_row[0] = request->operand_size_hint - 1;
4410+
}
4411+
priority_row = (ZyanI8 *)&forced_priority_row;
4412+
}
4413+
}
4414+
ZYAN_ASSERT(ZYAN_ARRAY_LENGTH(asz_priority[0]) ==
4415+
ZYAN_ARRAY_LENGTH(osz_priority[0]));
4416+
for (ZyanU8 j = start_offset; j < ZYAN_ARRAY_LENGTH(asz_priority[0]); ++j)
4417+
{
4418+
ZyanI8 size_index = priority_row[j];
4419+
if (size_index < 0)
4420+
{
4421+
break;
4422+
}
4423+
ZyanU8 base_size = rel_info->size[mode_index][size_index];
4424+
if (base_size == 0)
4425+
{
4426+
continue;
4427+
}
4428+
ZyanU8 predicted_size = base_size + extra_length;
4429+
if (runtime_address > ZYAN_UINT64_MAX - predicted_size + 1)
4430+
{
4431+
continue;
4432+
}
4433+
ZyanI64 rel = (ZyanI64)(op->imm.u - (runtime_address + predicted_size));
4434+
ZyanU8 rel_size = ZydisGetSignedImmSize(rel);
4435+
if (rel_size > (8 << size_index))
4436+
{
4437+
continue;
4438+
}
4439+
op->imm.s = rel;
4440+
adjusted_rel = ZYAN_TRUE;
4441+
break;
4442+
}
4443+
break;
4444+
}
4445+
case ZYDIS_SIZE_HINT_ASZ:
4446+
{
4447+
static const ZyanI8 asz_prefix_lookup[3][ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE + 1] =
4448+
{
4449+
{ 0, 0, 1, -1 },
4450+
{ 0, 1, 0, -1 },
4451+
{ 0, -1, 1, 0 },
4452+
};
4453+
ZyanI8 extra_length = asz_prefix_lookup[mode_index][request->address_size_hint];
4454+
if (extra_length < 0)
4455+
{
4456+
return ZYAN_STATUS_INVALID_ARGUMENT;
4457+
}
4458+
ZyanU8 asz_index = (request->address_size_hint == ZYDIS_ADDRESS_SIZE_HINT_NONE)
4459+
? mode_index
4460+
: ZydisGetAszFromHint(request->address_size_hint) >> 5;
4461+
ZYAN_ASSERT((rel_info->size[asz_index][0] != 0) &&
4462+
(rel_info->size[asz_index][1] == 0) &&
4463+
(rel_info->size[asz_index][2] == 0) &&
4464+
!rel_info->accepts_branch_hints);
4465+
ZyanU8 predicted_size = rel_info->size[asz_index][0] + extra_length;
4466+
if (runtime_address > ZYAN_UINT64_MAX - predicted_size + 1)
4467+
{
4468+
return ZYAN_STATUS_INVALID_ARGUMENT;
4469+
}
4470+
ZyanI64 rel = (ZyanI64)(op->imm.u - (runtime_address + predicted_size));
4471+
ZyanU8 rel_size = ZydisGetSignedImmSize(rel);
4472+
if (rel_size > 8)
4473+
{
4474+
return ZYAN_STATUS_INVALID_ARGUMENT;
4475+
}
4476+
op->imm.s = rel;
4477+
adjusted_rel = ZYAN_TRUE;
4478+
break;
4479+
}
4480+
default:
4481+
ZYAN_UNREACHABLE;
4482+
}
4483+
if (!adjusted_rel)
4484+
{
4485+
return ZYAN_STATUS_INVALID_ARGUMENT;
4486+
}
4487+
}
4488+
else if ((op->type == ZYDIS_OPERAND_TYPE_MEMORY) &&
4489+
((op->mem.base == ZYDIS_REGISTER_EIP) ||
4490+
(op->mem.base == ZYDIS_REGISTER_RIP)))
4491+
{
4492+
if (op_rip_rel)
4493+
{
4494+
return ZYAN_STATUS_INVALID_ARGUMENT;
4495+
}
4496+
4497+
absolute_address = op->mem.displacement;
4498+
op->mem.displacement = 0;
4499+
op_rip_rel = op;
4500+
}
4501+
}
4502+
4503+
ZydisEncoderInstruction instruction;
4504+
ZYAN_CHECK(ZydisEncoderEncodeInstructionInternal(request, buffer, length, &instruction));
4505+
if (op_rip_rel)
4506+
{
4507+
ZyanUSize instruction_size = *length;
4508+
if (runtime_address > ZYAN_UINT64_MAX - instruction_size + 1)
4509+
{
4510+
return ZYAN_STATUS_INVALID_ARGUMENT;
4511+
}
4512+
ZyanI64 rip_rel = (ZyanI64)(absolute_address - (runtime_address + instruction_size));
4513+
if (ZydisGetSignedImmSize(rip_rel) > 32)
4514+
{
4515+
return ZYAN_STATUS_INVALID_ARGUMENT;
4516+
}
4517+
ZYAN_ASSERT(instruction.disp_size != 0);
4518+
ZyanU8 disp_offset = (instruction.disp_size >> 3) + (instruction.imm_size >> 3);
4519+
ZYAN_ASSERT(instruction_size > disp_offset);
4520+
ZYAN_MEMCPY((ZyanU8 *)buffer + instruction_size - disp_offset, &rip_rel, sizeof(ZyanI32));
4521+
op_rip_rel->mem.displacement = rip_rel;
4522+
}
43004523

4301-
*length = output.offset;
43024524
return ZYAN_STATUS_SUCCESS;
43034525
}
43044526

src/EncoderData.c

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <Zydis/Internal/EncoderData.h>
2828

2929
#include <Generated/EncoderTables.inc>
30+
#include <Generated/GetRelInfo.inc>
3031

3132
ZyanU8 ZydisGetEncodableInstructions(ZydisMnemonic mnemonic,
3233
const ZydisEncodableInstruction **instruction)

0 commit comments

Comments
 (0)