Skip to content

Commit 00ca635

Browse files
committed
Replace Capstone with Zydis
Fix #11 This change also refactors E9Tool so that it is no longer so dependent on a specific disassembler. E9Tool now uses its own register and instruction representation, including: - A compressed Instr representation - A detailed InstrInfo representation The compressed representation is necessary for disassembling large binaries without running out of memory. The plugin interface has also been updated to remove the Capstone dependency.
1 parent 60f0358 commit 00ca635

File tree

12 files changed

+5014
-1781
lines changed

12 files changed

+5014
-1781
lines changed

Makefile

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#CC=clang
22
#CXX=clang++
33

4-
CXXFLAGS = -std=c++14 -Wall -Wno-reorder -fPIC -pie
4+
CXXFLAGS = -std=c++11 -Wall -Wno-reorder -fPIC -pie
55

66
E9PATCH_OBJS=\
77
src/e9patch/e9alloc.o \
@@ -21,7 +21,8 @@ E9TOOL_SRC=\
2121
src/e9tool/e9metadata.cpp \
2222
src/e9tool/e9parser.cpp \
2323
src/e9tool/e9tool.cpp \
24-
src/e9tool/e9types.cpp
24+
src/e9tool/e9types.cpp \
25+
src/e9tool/e9x86_64.cpp
2526

2627
release: CXXFLAGS += -O2 -D NDEBUG
2728
release: $(E9PATCH_OBJS)
@@ -35,16 +36,17 @@ debug: $(E9PATCH_OBJS)
3536
e9tool.o: $(E9TOOL_SRC)
3637
$(CXX) $(CXXFLAGS) -c src/e9tool/e9tool.cpp
3738

38-
tool: CXXFLAGS += -O2 -I src/e9tool/ -I capstone/include/ -Wno-unused-function
39+
tool: CXXFLAGS += -O2 -I src/e9tool/ -I zydis/include/ \
40+
-I zydis/dependencies/zycore/include/ -Wno-unused-function
3941
tool: e9tool.o
40-
$(CXX) $(CXXFLAGS) e9tool.o -o e9tool capstone/libcapstone.a \
42+
$(CXX) $(CXXFLAGS) e9tool.o -o e9tool libZydis.a \
4143
-Wl,--export-dynamic -ldl
4244
strip e9tool
4345

44-
tool.debug: CXXFLAGS += -O0 -g -I src/e9tool/ -I capstone/include/ \
45-
-Wno-unused-function
46+
tool.debug: CXXFLAGS += -O0 -g -I src/e9tool/ -I zydis/include/ \
47+
-I zydis/dependencies/zycore/include/ -Wno-unused-function
4648
tool.debug: e9tool.o
47-
$(CXX) $(CXXFLAGS) e9tool.o -o e9tool capstone/libcapstone.a \
49+
$(CXX) $(CXXFLAGS) e9tool.o -o e9tool libZydis.a \
4850
-Wl,--export-dynamic -ldl
4951

5052
loader:

build.sh

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,14 @@ else
1515
OFF=
1616
fi
1717

18-
VERSION=e3f106739a6ae78d47772dff31062d644ea21078
19-
2018
while [ $# -ge 1 ]
2119
do
2220
case "$1" in
23-
--capstone)
24-
VERSION="$2"
25-
shift
26-
;;
2721
--help|-h)
2822
echo "usage: $0 [OPTIONS]"
2923
echo
3024
echo "OPTIONS:"
3125
echo
32-
echo " --capstone VERSION"
33-
echo " Build using Capstone VERSION"
3426
echo " --help, -h"
3527
echo " Print this message"
3628
echo
@@ -44,25 +36,36 @@ do
4436
shift
4537
done
4638

47-
TARGET=`readlink capstone`
48-
if [ "$TARGET" != "capstone-$VERSION" ]
39+
TARGET=`readlink zydis`
40+
if [ ! -d zydis ]
4941
then
50-
if [ ! -f capstone-$VERSION.zip ]
51-
then
52-
echo -e "${GREEN}$0${OFF}: downloading capstone.zip..."
53-
wget -O capstone-$VERSION.zip \
54-
https://github.com/aquynh/capstone/archive/$VERSION.zip
55-
fi
56-
57-
echo -e "${GREEN}$0${OFF}: extracting capstone-$VERSION.zip..."
58-
unzip capstone-$VERSION.zip
59-
rm -rf capstone
60-
ln -s capstone-$VERSION capstone
42+
echo -e "${GREEN}$0${OFF}: cloning Zydis..."
43+
git clone --recursive 'https://github.com/zyantific/zydis.git'
6144

62-
echo -e "${GREEN}$0${OFF}: building capstone-$VERSION..."
63-
cd capstone
64-
CAPSTONE_ARCHS="x86" ./make.sh
65-
cd ..
45+
echo -e "${GREEN}$0${OFF}: building Zydis..."
46+
cat << EOF > zydis/include/ZydisExportConfig.h
47+
#ifndef ZYDIS_EXPORT_H
48+
#define ZYDIS_EXPORT_H
49+
#define ZYDIS_EXPORT
50+
#define ZYDIS_NO_EXPORT
51+
#define ZYDIS_DEPRECATED __attribute__ ((__deprecated__))
52+
#define ZYDIS_DEPRECATED_EXPORT ZYDIS_EXPORT ZYDIS_DEPRECATED
53+
#define ZYDIS_DEPRECATED_NO_EXPORT ZYDIS_NO_EXPORT ZYDIS_DEPRECATED
54+
#define ZYDIS_NO_DEPRECATED
55+
#endif
56+
EOF
57+
cat << EOF > zydis/include/ZycoreExportConfig.h
58+
#ifndef ZYCORE_EXPORT_H
59+
#define ZYCORE_EXPORT_H
60+
#define ZYCORE_EXPORT
61+
#define ZYCORE_NO_EXPORT
62+
#define ZYCORE_DEPRECATED __attribute__ ((__deprecated__))
63+
#define ZYCORE_DEPRECATED_EXPORT ZYCORE_EXPORT ZYCORE_DEPRECATED
64+
#define ZYCORE_DEPRECATED_NO_EXPORT ZYCORE_NO_EXPORT ZYCORE_DEPRECATED
65+
#define ZYCORE_NO_DEPRECATED
66+
#endif
67+
EOF
68+
make -f Makefile.zydis
6669
fi
6770

6871
echo -e "${GREEN}$0${OFF}: building e9patch and e9tool..."

doc/e9patch-programming-guide.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -512,14 +512,14 @@ The E9Tool plugin API is very simple and consists of just four functions:
512512

513513
1. `e9_plugin_init_v1(FILE *out, const ELF *in, ...)`:
514514
Called once before the binary is disassembled.
515-
2. `e9_plugin_instr_v1(FILE *out, const ELF *in, ...)`:
516-
Called once for each disassembled instruction.
517-
3. `e9_plugin_match_v1(FILE *out, const ELF *in, ...)`:
518-
Called once for each matching.
519-
4. `e9_plugin_patch_v1(FILE *out, const ELF *in, ...)`:
515+
2. `e9_plugin_match_v1(FILE *out, const ELF *in, ...)`:
516+
Called once for each match location.
517+
3. `e9_plugin_patch_v1(FILE *out, const ELF *in, ...)`:
520518
Called for each patch location.
521-
5. `e9_plugin_fini_v1(FILE *out, const ELF *in, ...)`:
519+
4. `e9_plugin_fini_v1(FILE *out, const ELF *in, ...)`:
522520
Called once after all instructions have been patched.
521+
5. `e9_plugin_event_v1(FILE *out, const ELF *in, ...)`:
522+
Called once for each event (see the `Event` enum).
523523

524524
Note that each function is optional, and the plugin can choose not to
525525
define it. However, The plugin must define at least one of these functions
@@ -534,16 +534,24 @@ Each function takes at least two arguments, namely:
534534

535535
Some functions take additional arguments, including:
536536

537-
* `handle`: Capstone handle.
538-
* `offset`: File offset of instruction (if applicable).
539-
* `I`: Instruction (if applicable).
537+
* `event`: An `Event` enum value.
538+
* `Is`: An array of all disassembled instructions.
539+
* `size`: The number of elements in `Is`.
540+
* `idx`: The index (into `Is`) of the instruction being
541+
matched/patched.
542+
* `info`: The detailed information about the instruction being
543+
matched/patched.
540544
* `context`: An optional plugin-defined context returned by the
541545
`e9_plugin_init_v1()` function.
542546

547+
Note that the `info` structure is temporary and will be immediately destroyed
548+
after the API call returns.
549+
In constrast, the `Is` array is persistent and will remain valid between calls.
550+
543551
Some API function return a value, including:
544552

545553
* `e9_plugin_init_v1()` returns an optional `context` that will be
546-
passed to all other API calls.
554+
passed to all other API calls.
547555
* `e9_plugin_match_v1()` returns an integer value of the plugin's
548556
choosing. This integer value can be matched using by the `--match`
549557
E9Tool command-line option, else the value will be ignored.
@@ -564,11 +572,6 @@ the `e9_plugin_init_v1()` function will do the following tasks
564572
4. Load ELF binaries into the virtual address space
565573
5. Create and return the context (if necessary)
566574

567-
The `e9_plugin_instr_v1()` function will do the following:
568-
569-
1. Analyze or remember the instruction (if necessary)
570-
2. Setup additional trampolines (if necessary)
571-
572575
The `e9_plugin_match_v1()` function will do the following:
573576

574577
1. Return a value to be used in a matching.
@@ -598,7 +601,7 @@ The syntax is as follows:
598601

599602
* `--action`: Selects the E9Tool "action" command-line option.
600603
This tells E9Tool what patching/instrumentation action to do.
601-
* `plugin(myPlugin).patchh()`: Specifies that instrument the program using the
604+
* `plugin(myPlugin).patch()`: Specifies that instrument the program using the
602605
`myPlugin.so` plugin.
603606

604607
If `myPlugin.so` exports the `e9_plugin_match_v1()` function, it is also

examples/plugins/example.cpp

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ using namespace e9frontend;
5252
/*
5353
* Initialize the counters and the trampoline.
5454
*/
55-
extern void *e9_plugin_init_v1(FILE *out, const e9frontend::ELF *elf)
55+
extern void *e9_plugin_init_v1(FILE *out, const ELF *elf)
5656
{
5757
/*
5858
* This example uses 3 counters (one for calls/jumps/returns).
@@ -147,57 +147,44 @@ extern void *e9_plugin_init_v1(FILE *out, const e9frontend::ELF *elf)
147147
/*
148148
* We match all control-flow transfer instructions.
149149
*/
150-
extern intptr_t e9_plugin_match_v1(FILE *out, const e9frontend::ELF *elf,
151-
csh handle, off_t offset, const cs_insn *I, void *context)
150+
extern intptr_t e9_plugin_match_v1(FILE *out, const ELF *elf, const Instr *Is,
151+
size_t size, size_t idx, const InstrInfo *info, void *context)
152152
{
153-
const cs_detail *detail = I->detail;
154-
for (uint8_t i = 0; i < detail->groups_count; i++)
153+
switch (info->mnemonic)
155154
{
156-
switch (detail->groups[i])
157-
{
158-
case CS_GRP_CALL:
159-
case CS_GRP_JUMP:
160-
case CS_GRP_RET:
161-
return 1;
162-
default:
163-
break;
164-
}
155+
case MNEMONIC_RET:
156+
return 1;
157+
case MNEMONIC_JB: case MNEMONIC_JBE: case MNEMONIC_JCXZ:
158+
case MNEMONIC_JECXZ: case MNEMONIC_JKNZD: case MNEMONIC_JKZD:
159+
case MNEMONIC_JL: case MNEMONIC_JLE: case MNEMONIC_JMP:
160+
case MNEMONIC_JNB: case MNEMONIC_JNBE: case MNEMONIC_JNL:
161+
case MNEMONIC_JNLE: case MNEMONIC_JNO: case MNEMONIC_JNP:
162+
case MNEMONIC_JNS: case MNEMONIC_JNZ: case MNEMONIC_JO:
163+
case MNEMONIC_JP: case MNEMONIC_JRCXZ: case MNEMONIC_JS:
164+
case MNEMONIC_JZ:
165+
return 2;
166+
case MNEMONIC_CALL:
167+
return 3;
168+
default:
169+
return 0;
165170
}
166-
167-
return 0;
168171
}
169172

170173
/*
171174
* Patch the selected instructions.
172175
*/
173-
extern void e9_plugin_patch_v1(FILE *out, const e9frontend::ELF *elf,
174-
csh handle, off_t offset, const cs_insn *I, void *context)
176+
extern void e9_plugin_patch_v1(FILE *out, const ELF *elf, const Instr *Is,
177+
size_t size, size_t idx, const InstrInfo *info, void *context)
175178
{
176-
Metadata metadata[2];
177-
const cs_detail *detail = I->detail;
178-
intptr_t counter = -1;
179-
for (uint8_t i = 0; counter < 0 && i < detail->groups_count; i++)
180-
{
181-
switch (detail->groups[i])
182-
{
183-
case CS_GRP_CALL:
184-
counter = COUNTERS + 0 * sizeof(size_t);
185-
break;
186-
case CS_GRP_JUMP:
187-
counter = COUNTERS + 1 * sizeof(size_t);
188-
break;
189-
case CS_GRP_RET:
190-
counter = COUNTERS + 2 * sizeof(size_t);
191-
break;
192-
default:
193-
break;
194-
}
195-
}
196-
if (counter < 0)
179+
intptr_t counter = e9_plugin_match_v1(out, elf, Is, size, idx, info,
180+
context);
181+
if (counter == 0)
197182
return;
198-
183+
counter = COUNTERS + (counter - 1) * sizeof(size_t);
184+
199185
// We instantiate the trampoline template with $counter pointing to
200186
// the counter corresponding to the instruction type:
187+
Metadata metadata[2];
201188
metadata[0].name = "counter";
202189
std::string buf;
203190
buf += "{\"rel32\":";
@@ -209,6 +196,6 @@ extern void e9_plugin_patch_v1(FILE *out, const e9frontend::ELF *elf,
209196
metadata[1].data = nullptr;
210197

211198
// Send a "patch" E9Patch API message.
212-
sendPatchMessage(out, "cflimit", offset, metadata);
199+
sendPatchMessage(out, "cflimit", Is[idx].offset, metadata);
213200
}
214201

0 commit comments

Comments
 (0)