Skip to content

Commit bcd154b

Browse files
committed
feat(x86): implemented encoder
1 parent 4b58af1 commit bcd154b

8 files changed

Lines changed: 324 additions & 14 deletions

File tree

capstonebundle/capstone.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Capstone* capstone_create(const CapstoneInitData* data, int size) {
55
cs_err err = cs_open(data->arch, data->mode, &h);
66

77
if(err) {
8-
rd_log(RD_LOG_FAIL, "%s", cs_strerror(err));
8+
rd_log(RD_LOGLEVEL_FAIL, "%s", cs_strerror(err));
99
return NULL;
1010
}
1111

x86/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ add_library(${PROJECT_NAME} SHARED)
1313

1414
target_sources(${PROJECT_NAME}
1515
PRIVATE
16+
encoder/encoder.c
17+
encoder/grammar.c
1618
plugin.c
1719
lifter.c
1820
common.c
1921
registers.c
2022
)
2123

24+
target_include_directories(${PROJECT_NAME}
25+
PRIVATE
26+
"${CMAKE_CURRENT_SOURCE_DIR}"
27+
)
28+
2229
target_link_libraries(${PROJECT_NAME}
2330
PRIVATE
2431
redasm::redasm

x86/common.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@
33
#include <Zydis/Zydis.h>
44
#include <redasm/redasm.h>
55

6+
typedef struct X86UserData {
7+
ZydisMachineMode mode;
8+
ZydisStackWidth width;
9+
} X86UserData;
10+
11+
typedef struct X86Processor {
12+
const X86UserData* userdata;
13+
ZydisDecoder decoder;
14+
char buffer[ZYDIS_MAX_INSTRUCTION_LENGTH];
15+
RDLexer* lex;
16+
// const RDCallingConvention** calling_conventions{nullptr};
17+
} X86Processor;
18+
619
typedef struct X86Address {
720
RDAddress value;
821
bool has_value;

x86/encoder/encoder.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "encoder.h"
2+
#include "common.h"
3+
#include "grammar.h"
4+
5+
bool x86_encode(RDContext* ctx, RDAddress address, const char* s,
6+
RDScratchBuffer* buf, RDProcessor* proc) {
7+
if(!s) s = "nop";
8+
9+
const X86Processor* self = (X86Processor*)proc;
10+
11+
X86GrammarData g = {
12+
.ctx = ctx,
13+
.req = {.machine_mode = self->userdata->mode},
14+
.err_buf = buf,
15+
};
16+
17+
if(!x86_encoder_parse(self->lex, s, &g)) return false;
18+
19+
char buffer[ZYDIS_MAX_INSTRUCTION_LENGTH] = {0};
20+
ZyanUSize length = rd_count_of(buffer);
21+
22+
if(!ZYAN_SUCCESS(ZydisEncoderEncodeInstructionAbsolute(
23+
&g.req, buffer, &length, (ZyanU64)address))) {
24+
RD_LOG_FAIL("%s", rd_format_to(buf, "failed to encode '%s'", s));
25+
return false;
26+
}
27+
28+
rd_scratch_append(buf, buffer, (usize)length);
29+
return true;
30+
}

x86/encoder/encoder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#pragma once
2+
3+
#include <redasm/redasm.h>
4+
5+
bool x86_encode(RDContext* ctx, RDAddress address, const char* s,
6+
RDScratchBuffer* scratch, RDProcessor* proc);

x86/encoder/grammar.c

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
#include "grammar.h"
2+
#include <string.h>
3+
4+
static ZydisMnemonic _x86_mnemonic_from_token(const RDToken* tok) {
5+
for(int i = 0; i < ZYDIS_MNEMONIC_MAX_VALUE; i++) {
6+
const char* s = ZydisMnemonicGetString((ZydisMnemonic)i);
7+
8+
if(s && strlen(s) == tok->length &&
9+
!rd_strnicmp(s, tok->value, (int)tok->length))
10+
return (ZydisMnemonic)i;
11+
}
12+
13+
return ZYDIS_MNEMONIC_INVALID;
14+
}
15+
16+
static ZydisRegister _x86_register_from_token(const RDToken* tok) {
17+
for(int i = 0; i < ZYDIS_REGISTER_MAX_VALUE; i++) {
18+
const char* s = ZydisRegisterGetString((ZydisRegister)i);
19+
20+
if(s && strlen(s) == tok->length &&
21+
!rd_strnicmp(s, tok->value, (int)tok->length))
22+
return (ZydisRegister)i;
23+
}
24+
25+
return ZYDIS_REGISTER_NONE;
26+
}
27+
28+
static bool _x86_size_keyword_to_bytes(const RDToken* tok, usize* out) {
29+
static const struct {
30+
const char* name;
31+
usize size;
32+
} SIZES[] = {
33+
{"byte", 1},
34+
{"word", 2},
35+
{"dword", 4},
36+
{"qword", 8},
37+
};
38+
39+
for(usize i = 0; i < rd_count_of(SIZES); i++) {
40+
if(strlen(SIZES[i].name) == tok->length &&
41+
!rd_strnicmp(SIZES[i].name, tok->value, (int)tok->length)) {
42+
*out = SIZES[i].size;
43+
return true;
44+
}
45+
}
46+
47+
return false;
48+
}
49+
50+
static bool _reg_operand_rule(RDLexer* lex, void* userdata) {
51+
X86GrammarData* data = (X86GrammarData*)userdata;
52+
53+
RDToken tok;
54+
if(!rd_lexer_next_expect(lex, RD_TOK_IDENTIFIER, &tok)) return false;
55+
56+
ZydisRegister reg = _x86_register_from_token(&tok);
57+
if(reg == ZYDIS_REGISTER_NONE) return false;
58+
59+
ZydisEncoderOperand* zop = &data->req.operands[data->req.operand_count++];
60+
zop->type = ZYDIS_OPERAND_TYPE_REGISTER;
61+
zop->reg.value = reg;
62+
return true;
63+
}
64+
65+
static bool _imm_operand_rule(RDLexer* lex, void* userdata) {
66+
X86GrammarData* data = (X86GrammarData*)userdata;
67+
68+
RDToken tok;
69+
if(!rd_lexer_next(lex, &tok)) return false;
70+
71+
bool negative = tok.type == RD_TOK_MINUS;
72+
if(negative && !rd_lexer_next(lex, &tok)) return false;
73+
74+
i64 value;
75+
76+
if(tok.type == RD_TOK_NUMBER) {
77+
value = negative ? -(i64)tok.u_value : (i64)tok.u_value;
78+
}
79+
else if(tok.type == RD_TOK_IDENTIFIER) {
80+
const char* name = rd_lexer_token_value(lex, &tok);
81+
RDAddress addr;
82+
if(!rd_get_address(data->ctx, name, &addr)) return false;
83+
value = negative ? -(i64)addr : (i64)addr;
84+
}
85+
else
86+
return false;
87+
88+
ZydisEncoderOperand* zop = &data->req.operands[data->req.operand_count++];
89+
zop->type = ZYDIS_OPERAND_TYPE_IMMEDIATE;
90+
zop->imm.s = value;
91+
return true;
92+
}
93+
94+
static bool _displ_operand_rule(RDLexer* lex, void* userdata) {
95+
X86GrammarData* data = (X86GrammarData*)userdata;
96+
usize size = 0;
97+
RDToken tok;
98+
99+
if(rd_lexer_peek_expect(lex, RD_TOK_IDENTIFIER, &tok) &&
100+
_x86_size_keyword_to_bytes(&tok, &size)) {
101+
rd_lexer_consume(lex);
102+
103+
if(!rd_lexer_next_expect(lex, RD_TOK_IDENTIFIER, &tok) ||
104+
tok.length != 3 || rd_strnicmp("ptr", tok.value, 3))
105+
return false;
106+
}
107+
108+
if(!rd_lexer_next_expect(lex, RD_TOK_OPEN_SQUARE, &tok)) return false;
109+
110+
ZydisRegister base = ZYDIS_REGISTER_NONE, index = ZYDIS_REGISTER_NONE;
111+
u8 scale = 1;
112+
i64 disp = 0;
113+
bool have_disp = false;
114+
115+
// optional base register: "[0x1000]" (absolute) is valid with none at all
116+
if(rd_lexer_peek_expect(lex, RD_TOK_IDENTIFIER, &tok)) {
117+
ZydisRegister reg = _x86_register_from_token(&tok);
118+
119+
if(reg != ZYDIS_REGISTER_NONE) {
120+
rd_lexer_consume(lex);
121+
base = reg;
122+
}
123+
}
124+
125+
for(;;) {
126+
if(!rd_lexer_next(lex, &tok)) return false;
127+
if(tok.type == RD_TOK_CLOSE_SQUARE) break;
128+
129+
bool plus = tok.type == RD_TOK_PLUS, minus = tok.type == RD_TOK_MINUS;
130+
if(!plus && !minus) return false;
131+
if(!rd_lexer_next(lex, &tok)) return false;
132+
133+
if(tok.type == RD_TOK_IDENTIFIER) { // index register, optional "*scale"
134+
ZydisRegister reg = _x86_register_from_token(&tok);
135+
if(reg == ZYDIS_REGISTER_NONE) return false;
136+
index = reg;
137+
138+
if(rd_lexer_peek_expect(lex, RD_TOK_ASTERISK, &tok)) {
139+
rd_lexer_consume(lex);
140+
141+
if(!rd_lexer_next_expect(lex, RD_TOK_NUMBER, &tok))
142+
return false;
143+
144+
scale = (u8)tok.u_value;
145+
}
146+
}
147+
else if(tok.type == RD_TOK_NUMBER) {
148+
disp = minus ? -(i64)tok.u_value : (i64)tok.u_value;
149+
have_disp = true;
150+
}
151+
else
152+
return false;
153+
}
154+
155+
return true;
156+
}
157+
158+
bool x86_encoder_parse(RDLexer* lex, const char* s, X86GrammarData* data) {
159+
rd_lexer_reset(lex, s);
160+
161+
RDToken tok;
162+
if(!rd_lexer_next_expect(lex, RD_TOK_IDENTIFIER, &tok)) {
163+
RD_LOG_FAIL(
164+
"%s", rd_format_to(data->err_buf, "expected mnemonic in '%s'", s));
165+
return false;
166+
}
167+
168+
ZydisMnemonic mnem = _x86_mnemonic_from_token(&tok);
169+
if(mnem == ZYDIS_MNEMONIC_INVALID) {
170+
RD_LOG_FAIL("%s", rd_format_to(data->err_buf,
171+
"unknown mnemonic '%.*s' in '%s'",
172+
(int)tok.length, tok.value, s));
173+
return false;
174+
}
175+
176+
data->req.mnemonic = mnem;
177+
178+
static const RDLexerTryFn OPERAND_ENTRIES[] = {
179+
_reg_operand_rule,
180+
_imm_operand_rule,
181+
_displ_operand_rule,
182+
NULL,
183+
};
184+
185+
RDToken peek;
186+
if(rd_lexer_peek(lex, &peek)) {
187+
if(!rd_lexer_try_any(lex, OPERAND_ENTRIES, data)) {
188+
RD_LOG_FAIL("%s", rd_format_to(data->err_buf,
189+
"expected operand in '%s'", s));
190+
return false;
191+
}
192+
193+
while(rd_lexer_peek_expect(lex, RD_TOK_COMMA, &tok)) {
194+
rd_lexer_consume(lex);
195+
196+
if(data->req.operand_count >= ZYDIS_ENCODER_MAX_OPERANDS) {
197+
RD_LOG_FAIL("%s", rd_format_to(data->err_buf,
198+
"too many operands in '%s'", s));
199+
return false;
200+
}
201+
202+
if(!rd_lexer_try_any(lex, OPERAND_ENTRIES, data)) {
203+
RD_LOG_FAIL("%s", rd_format_to(
204+
data->err_buf,
205+
"expected operand after ',' in '%s'", s));
206+
return false;
207+
}
208+
}
209+
}
210+
211+
if(!rd_lexer_at_end(lex)) {
212+
RD_LOG_FAIL("%s", rd_format_to(data->err_buf,
213+
"unexpected trailing input in '%s'", s));
214+
return false;
215+
}
216+
217+
// clang-format off
218+
usize reg_size = 0;
219+
for(usize i = 0; i < data->req.operand_count; i++) {
220+
if(data->req.operands[i].type == ZYDIS_OPERAND_TYPE_REGISTER) {
221+
reg_size = ZydisRegisterGetWidth(data->req.machine_mode,
222+
data->req.operands[i].reg.value) / 8;
223+
break;
224+
}
225+
}
226+
// clang-format on
227+
228+
for(usize i = 0; i < data->req.operand_count; i++) {
229+
ZydisEncoderOperand* op = &data->req.operands[i];
230+
if(op->type != ZYDIS_OPERAND_TYPE_MEMORY || op->mem.size != 0) continue;
231+
232+
if(!reg_size) {
233+
RD_LOG_FAIL("%s",
234+
rd_format_to(data->err_buf,
235+
"ambiguous memory operand size in '%s'"
236+
"specify byte/word/dword/qword ptr",
237+
s));
238+
return false;
239+
}
240+
241+
op->mem.size = (ZyanU16)reg_size;
242+
}
243+
244+
return true;
245+
}

x86/encoder/grammar.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
3+
#include <Zydis/Zydis.h>
4+
#include <redasm/redasm.h>
5+
6+
typedef struct X86GrammarData {
7+
RDContext* ctx;
8+
ZydisEncoderRequest req;
9+
RDScratchBuffer* err_buf;
10+
} X86GrammarData;
11+
12+
bool x86_encoder_parse(RDLexer* lex, const char* s, X86GrammarData* data);

0 commit comments

Comments
 (0)