Skip to content
This repository was archived by the owner on Jan 27, 2025. It is now read-only.

Commit a75c39f

Browse files
committed
Add relocs
1 parent f4c5818 commit a75c39f

File tree

6 files changed

+175
-63
lines changed

6 files changed

+175
-63
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Author: **[terorie](https://github.com/terorie)**
44

5-
This BN plugin implements a Capstone-powered disassembler and lifter for the Solana derivatives bytecode format.
5+
This BN plugin implements a Capstone-powered disassembler and lifter for the Solana bytecode format.
66

77
Kernel eBPF support is WIP.
88

arch_ebpf.cpp

+130-46
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,6 @@ static bool IsLongIns(const uint8_t* data)
5454
return data[0] == BPF_OPC_LDDW;
5555
}
5656

57-
static int16_t GetOffset(uint32_t x)
58-
{
59-
int16_t ret;
60-
if (x < 0x8000) {
61-
ret = (int16_t)x;
62-
} else {
63-
ret = (int16_t)(0x10000 - x);
64-
}
65-
return ret;
66-
}
67-
6857
class EBPFArchitecture : public Architecture {
6958
private:
7059
BNEndianness endian;
@@ -104,10 +93,10 @@ class EBPFArchitecture : public Architecture {
10493
struct decomp_result res;
10594
struct cs_insn* insn = &(res.insn);
10695

107-
if (maxLen < 4) {
96+
if (maxLen < 8) {
10897
return false;
10998
}
110-
if (ebpf_decompose(data, 16, addr, endian == LittleEndian, &res)) {
99+
if (!ebpf_decompose(data, 16, addr, endian == LittleEndian, &res)) {
111100
goto beach;
112101
}
113102

@@ -130,8 +119,8 @@ class EBPFArchitecture : public Architecture {
130119
result.AddBranch(FalseBranch, addr + 8);
131120
break;
132121
case BPF_INS_CALL:
133-
if (data[1] & 0xF0 == 0x10) {
134-
result.AddBranch(CallDestination, JumpDest(data, addr, endian == LittleEndian));
122+
if ((data[1] & 0xF0) == 0x10) {
123+
result.AddBranch(CallDestination, CallDest(data, addr, endian == LittleEndian));
135124
} else {
136125
result.AddBranch(SystemCall);
137126
}
@@ -159,30 +148,34 @@ class EBPFArchitecture : public Architecture {
159148
struct cs_insn* insn = &(res.insn);
160149
struct cs_detail* detail = &(res.detail);
161150
struct cs_bpf* bpf = &(detail->bpf);
162-
char buf[32];
163151
size_t strlenMnem;
164152

153+
char buf[256];
154+
#define FMT_I64(x) \
155+
do { \
156+
if ((x) >= 0) \
157+
std::snprintf(buf, sizeof(buf), "+%#lx", (x)); \
158+
else \
159+
std::snprintf(buf, sizeof(buf), "-%#lx", -(x)); \
160+
} while (0)
161+
165162
if (len < 8) {
166163
goto beach;
167164
}
168-
if (ebpf_decompose(data, 16, addr, endian == LittleEndian, &res)) {
165+
if (!ebpf_decompose(data, 16, addr, endian == LittleEndian, &res)) {
169166
goto beach;
170167
}
171168

172169
/* mnemonic */
173170
result.emplace_back(InstructionToken, insn->mnemonic);
174171

175172
/* padding between mnemonic and operands */
176-
memset(buf, ' ', 8);
177-
strlenMnem = strlen(insn->mnemonic);
178-
if (strlenMnem < 8)
179-
buf[8 - strlenMnem] = '\0';
180-
else
181-
buf[1] = '\0';
182-
result.emplace_back(TextToken, buf);
173+
result.emplace_back(TextToken, std::string(10 - strlen(insn->mnemonic), ' '));
183174

184175
if (insn->id == BPF_INS_CALL) {
185-
std::sprintf(buf, "%#lx", bpf->operands[0].imm);
176+
int64_t off = (int32_t)bpf->operands[0].imm;
177+
off = off * 8 + 8;
178+
FMT_I64(off);
186179
result.emplace_back(PossibleAddressToken, buf, bpf->operands[0].imm, 8);
187180
len = 8;
188181
return true;
@@ -191,36 +184,29 @@ class EBPFArchitecture : public Architecture {
191184
/* operands */
192185
for (int i = 0; i < bpf->op_count; ++i) {
193186
struct cs_bpf_op* op = &(bpf->operands[i]);
194-
int16_t disp;
187+
int64_t val;
195188

196189
switch (op->type) {
197190
case BPF_OP_REG:
198191
result.emplace_back(RegisterToken, GetRegisterName(op->reg));
199192
break;
200193
case BPF_OP_IMM:
201-
// TODO special snowflake insn
202-
std::sprintf(buf, "%#lx", op->imm);
194+
val = (int32_t)bpf->operands[0].imm;
195+
FMT_I64(val);
203196
result.emplace_back(IntegerToken, buf, op->imm, 8);
204197
break;
205198
case BPF_OP_OFF:
206-
disp = GetOffset(op->off);
207-
if (disp >= 0) {
208-
std::sprintf(buf, "+%#x", disp);
209-
} else {
210-
std::sprintf(buf, "-%#x", -disp);
211-
}
212-
result.emplace_back(CodeRelativeAddressToken, buf, disp, 2);
199+
val = Int16SignExtend(op->off);
200+
FMT_I64(val);
201+
result.emplace_back(CodeRelativeAddressToken, buf, val, 2);
213202
break;
214203
case BPF_OP_MEM:
215204
result.emplace_back(TextToken, "[");
205+
216206
result.emplace_back(RegisterToken, GetRegisterName(op->mem.base));
217-
disp = GetOffset(op->mem.disp);
218-
if (disp >= 0) {
219-
std::sprintf(buf, "+%#x", disp);
220-
} else {
221-
std::sprintf(buf, "-%#x", -disp);
222-
}
223-
result.emplace_back(IntegerToken, buf, disp, 2);
207+
val = Int16SignExtend(op->mem.disp) * 8 + 8;
208+
FMT_I64(val);
209+
result.emplace_back(IntegerToken, buf, val, 2);
224210

225211
result.emplace_back(TextToken, "]");
226212
break;
@@ -236,7 +222,7 @@ class EBPFArchitecture : public Architecture {
236222
}
237223

238224
rc = true;
239-
if (data[0] == BPF_OPC_LDDW) {
225+
if (IsLongIns(data)) {
240226
len = 16;
241227
} else {
242228
len = 8;
@@ -256,13 +242,17 @@ class EBPFArchitecture : public Architecture {
256242
if (len < 8) {
257243
goto beach;
258244
}
259-
if (ebpf_decompose(data, len, addr, endian == LittleEndian, &res)) {
245+
if (!ebpf_decompose(data, len, addr, endian == LittleEndian, &res)) {
260246
il.AddInstruction(il.Undefined());
261247
goto beach;
262248
}
263249

264250
rc = GetLowLevelILForBPFInstruction(this, il, data, addr, &res, endian == LittleEndian);
265-
len = 8;
251+
if (IsLongIns(data)) {
252+
len = 16;
253+
} else {
254+
len = 8;
255+
}
266256

267257
beach:
268258
return rc;
@@ -634,7 +624,98 @@ class SolanaCallingConvention : public CallingConvention {
634624

635625
virtual uint32_t GetIntegerReturnValueRegister() override
636626
{
637-
return PPC_REG_R0;
627+
return BPF_REG_R0;
628+
}
629+
};
630+
631+
class EbpfElfRelocationHandler : public RelocationHandler {
632+
public:
633+
virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override
634+
{
635+
auto info = reloc->GetInfo();
636+
uint64_t* dest64 = (uint64_t*)dest;
637+
uint32_t* dest32 = (uint32_t*)dest;
638+
uint16_t* dest16 = (uint16_t*)dest;
639+
auto swap64 = [&arch](uint32_t x) { return (arch->GetEndianness() == LittleEndian) ? x : bswap64(x); };
640+
auto swap32 = [&arch](uint32_t x) { return (arch->GetEndianness() == LittleEndian) ? x : bswap32(x); };
641+
auto swap16 = [&arch](uint16_t x) { return (arch->GetEndianness() == LittleEndian) ? x : bswap16(x); };
642+
uint64_t target = reloc->GetTarget();
643+
std::vector<Ref<Section>> sections;
644+
uint64_t rela_src;
645+
switch (info.nativeType) {
646+
case R_BPF_64_64:
647+
dest32[1] = swap32((uint32_t)((target + info.addend) & 0xffffffff));
648+
dest32[3] = swap32((uint32_t)((target + info.addend) >> 32));
649+
break;
650+
case R_BPF_64_ABS64:
651+
dest64[0] = swap64(target + info.addend);
652+
break;
653+
case R_BPF_64_ABS32:
654+
case R_BPF_64_NODYLD32:
655+
dest64[0] = swap32((uint32_t)(target + info.addend));
656+
break;
657+
case R_BPF_64_RELATIVE:
658+
// Super weird reloc
659+
sections = view->GetSectionsAt(reloc->GetAddress());
660+
if (!sections.empty() && sections[0]->GetName() == ".text") {
661+
rela_src = 0;
662+
rela_src = swap32(dest32[1]) | ((uint64_t)(swap32(dest32[3])) << 32);
663+
// wtf?
664+
if (rela_src < 0x100000000) {
665+
rela_src += 0x100000000;
666+
}
667+
dest32[1] = swap32((uint32_t)((rela_src) & 0xffffffff));
668+
dest32[3] = swap32((uint32_t)((rela_src) >> 32));
669+
} else {
670+
// i give up
671+
}
672+
sections.clear();
673+
break;
674+
case R_BPF_64_32:
675+
// TODO This isn't documented as pc-rel, but BPF_INS_CALL takes pc-rel immediate
676+
dest32[1] = swap32((uint32_t)((target + info.addend - reloc->GetAddress()) / 8 - 1));
677+
break;
678+
}
679+
return true;
680+
}
681+
682+
virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, std::vector<BNRelocationInfo>& result) override
683+
{
684+
std::set<uint64_t> relocTypes;
685+
for (auto& reloc : result) {
686+
reloc.type = StandardRelocationType;
687+
reloc.size = 8;
688+
reloc.pcRelative = false;
689+
reloc.dataRelocation = false;
690+
switch (reloc.nativeType) {
691+
case R_BPF_NONE:
692+
reloc.type = IgnoredRelocation;
693+
break;
694+
case R_BPF_64_64:
695+
break;
696+
case R_BPF_64_ABS64:
697+
reloc.dataRelocation = true;
698+
break;
699+
case R_BPF_64_ABS32:
700+
case R_BPF_64_NODYLD32:
701+
reloc.dataRelocation = true;
702+
reloc.size = 4;
703+
break;
704+
case R_BPF_64_RELATIVE:
705+
reloc.pcRelative = true; // not really??
706+
break;
707+
case R_BPF_64_32:
708+
reloc.size = 4;
709+
break;
710+
default:
711+
reloc.type = UnhandledRelocation;
712+
relocTypes.insert(reloc.nativeType);
713+
break;
714+
}
715+
}
716+
for (auto& reloc : relocTypes)
717+
LogWarn("Unsupported ELF relocation type: %s", GetRelocationString((ElfBpfRelocationType)reloc));
718+
return true;
638719
}
639720
};
640721

@@ -670,6 +751,9 @@ BINARYNINJAPLUGIN bool CorePluginInit()
670751
ebpf_le->RegisterCallingConvention(conv);
671752
ebpf_le->SetDefaultCallingConvention(conv);
672753

754+
ebpf_le->RegisterRelocationHandler("ELF", new EbpfElfRelocationHandler());
755+
ebpf_be->RegisterRelocationHandler("ELF", new EbpfElfRelocationHandler());
756+
673757
return true;
674758
}
675759
}

disassembler.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ ebpf_release(void)
4848
}
4949
}
5050

51-
extern "C" int
51+
extern "C" bool
5252
ebpf_decompose(const uint8_t* data,
5353
int size,
5454
uint64_t addr,
@@ -81,8 +81,9 @@ ebpf_decompose(const uint8_t* data,
8181
if (insn) {
8282
cs_free(insn, 1);
8383
insn = 0;
84+
return true;
8485
}
85-
return 0;
86+
return false;
8687
}
8788

8889
extern "C" int

disassembler.h

+23-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <capstone/bpf.h>
44
#include <capstone/capstone.h>
5+
#include "util.h"
56

67
//*****************************************************************************
78
// structs and types
@@ -40,7 +41,7 @@ struct decomp_result {
4041
//*****************************************************************************
4142
extern "C" int ebpf_init(void);
4243
extern "C" void ebpf_release(void);
43-
extern "C" int ebpf_decompose(
44+
extern "C" bool ebpf_decompose(
4445
const uint8_t* data, int size, uint64_t addr,
4546
bool lil_end, struct decomp_result* result);
4647
extern "C" int ebpf_disassemble(
@@ -60,19 +61,30 @@ static inline int16_t Int16SignExtend(uint32_t x)
6061

6162
static inline uint64_t JumpDest(const uint8_t* data, uint64_t addr, bool le)
6263
{
63-
// TODO endianness
64-
int16_t off;
65-
if (le)
66-
off = (int16_t)((uint16_t)data[2] | (uint16_t)data[3] << 8);
67-
else
68-
off = (int16_t)((uint16_t)data[3] | (uint16_t)data[2] << 8);
64+
uint16_t raw = *(const uint16_t *)(data + 2);
65+
if (!le)
66+
raw = bswap16(raw);
67+
int16_t off = (int16_t)raw;
6968
return addr + (int64_t)off * 8 + 8;
7069
}
7170

7271
static inline uint64_t JumpDest(struct cs_bpf_op* op, uint64_t addr)
7372
{
74-
uint64_t base = addr + 8;
75-
int16_t off = Int16SignExtend(op->off);
76-
int64_t offset = (int64_t)(off)*8;
77-
return (uint64_t)((int64_t)base + offset);
73+
int64_t off = Int16SignExtend(op->off);
74+
return addr + (int64_t)off * 8 + 8;
75+
}
76+
77+
static inline uint64_t CallDest(const uint8_t* data, uint64_t addr, bool le)
78+
{
79+
uint32_t raw = *(const uint32_t *)(data + 4);
80+
if (!le)
81+
raw = bswap32(raw);
82+
int64_t off = (int32_t)raw;
83+
return addr + (int64_t)off * 8 + 8;
84+
}
85+
86+
static inline uint64_t CallDest(struct cs_bpf_op* op, uint64_t addr)
87+
{
88+
int64_t off = (int32_t)op->imm;
89+
return addr + (int64_t)off * 8 + 8;
7890
}

0 commit comments

Comments
 (0)