Skip to content

Commit 59a801d

Browse files
committed
Added small improvements to the binary class.
1 parent 8e04843 commit 59a801d

File tree

5 files changed

+121
-23
lines changed

5 files changed

+121
-23
lines changed

.gitignore

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# build files
22
build/
33

4-
# visual studio code files
5-
.vscode/
4+
# IDE stuff
5+
.vscode/
6+
.cache/

chum/source/binary.cpp

+24-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
#include <Windows.h>
1111
#include <zycore/Format.h>
12-
#include <pe-builder/pe-builder.h>
1312

1413
namespace chum {
1514

@@ -224,7 +223,24 @@ void binary::print(bool const verbose) {
224223

225224
// Create a new PE file from this binary.
226225
bool binary::create(char const* const path) const {
227-
pb::pe_builder pe;
226+
pb::pe_builder pe = {};
227+
if (!create(pe))
228+
return {};
229+
230+
return pe.write(path);
231+
}
232+
233+
// Create a new PE file from this binary.
234+
std::vector<std::uint8_t> binary::create() const {
235+
pb::pe_builder pe = {};
236+
if (!create(pe))
237+
return {};
238+
239+
return pe.write();
240+
}
241+
242+
// Create a new PE file from this binary.
243+
bool binary::create(pb::pe_builder& pe) const {
228244
pe.file_characteristics(IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL);
229245

230246
// We don't want to resize in the middle of adding sections.
@@ -587,7 +603,7 @@ bool binary::create(char const* const path) const {
587603
if (entrypoint_)
588604
pe.entrypoint(sym_to_va[entrypoint_->sym_id.value]);
589605

590-
return pe.write(path);
606+
return true;
591607
}
592608

593609
// Get the entrypoint of this binary, if it exists.
@@ -740,5 +756,10 @@ import_routine* binary::get_or_create_import_routine(
740756
return create_import_module(module_name)->create_routine(routine_name);
741757
}
742758

759+
// Get the underlying Zydis decoder.
760+
ZydisDecoder* binary::decoder() {
761+
return &decoder_;
762+
}
763+
743764
} // namespace chum
744765

chum/source/binary.h

+38-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <vector>
88
#include <tuple>
99

10+
#include <pe-builder/pe-builder.h>
1011
#include <Zydis/Zydis.h>
1112

1213
namespace chum {
@@ -37,6 +38,12 @@ class binary {
3738
// Create a new PE file from this binary.
3839
bool create(char const* path) const;
3940

41+
// Create a new PE file from this binary.
42+
std::vector<std::uint8_t> create() const;
43+
44+
// Create a new PE file from this binary.
45+
bool create(pb::pe_builder& pe) const;
46+
4047
// Get the entrypoint of this binary, if it exists.
4148
basic_block* entrypoint() const;
4249

@@ -96,6 +103,9 @@ class binary {
96103
import_routine* get_or_create_import_routine(
97104
char const* module_name, char const* routine_name);
98105

106+
// Get the underlying Zydis decoder.
107+
ZydisDecoder* decoder();
108+
99109
public:
100110
// Create a new instruction.
101111
template <typename... Args>
@@ -112,6 +122,9 @@ class binary {
112122
template <typename T>
113123
void instr_push_integer(instruction& instr, T const& value) const;
114124

125+
// Encode and push a new instruction.
126+
void instr_push_enc_req(instruction& instr, ZydisEncoderRequest const* enc_req) const;
127+
115128
private:
116129
// ---------------
117130
// Remember to add any new members to the move constructor/assignment operator!
@@ -142,7 +155,7 @@ class binary {
142155

143156
// Create a new instruction.
144157
template <typename... Args>
145-
instruction binary::instr(Args&&... args) const {
158+
inline instruction binary::instr(Args&&... args) const {
146159
instruction new_instr = { 0 };
147160
instr_push_item<0>(new_instr, std::make_tuple(std::forward<Args>(args)...));
148161
return new_instr;
@@ -151,18 +164,20 @@ instruction binary::instr(Args&&... args) const {
151164
// This is a helper function for instr() that serializes a single item
152165
// into the instruction that is currently being built.
153166
template <std::size_t Idx, typename... Args>
154-
void binary::instr_push_item(instruction& instr,
167+
inline void binary::instr_push_item(instruction& instr,
155168
std::tuple<Args...> const& tuple) const {
156169
if constexpr (Idx < sizeof...(Args)) {
157170
// The current item that we are pushing to the instruction.
158171
auto const item = std::get<Idx>(tuple);
159172
using item_type = std::remove_const_t<decltype(item)>;
160173

161-
static_assert(std::is_integral_v<item_type> ||
162-
std::is_same_v<item_type, symbol_id> ||
163-
std::is_same_v<item_type, symbol*> ||
164-
std::is_same_v<item_type, basic_block*> ||
165-
std::is_same_v<item_type, import_routine*> ||
174+
static_assert(std::is_integral_v<item_type> ||
175+
std::is_same_v<item_type, symbol_id> ||
176+
std::is_same_v<item_type, symbol*> ||
177+
std::is_same_v<item_type, basic_block*> ||
178+
std::is_same_v<item_type, import_routine*> ||
179+
std::is_same_v<item_type, ZydisEncoderRequest*> ||
180+
std::is_same_v<item_type, ZydisEncoderRequest> ||
166181
std::is_same_v<item_type, char const*>);
167182

168183
// Integral types should be converted to bytes.
@@ -176,6 +191,10 @@ void binary::instr_push_item(instruction& instr,
176191
instr_push_integer(instr, item->sym_id.value);
177192
else if constexpr (std::is_same_v<item_type, import_routine*>)
178193
instr_push_integer(instr, item->sym_id.value);
194+
else if constexpr (std::is_same_v<item_type, ZydisEncoderRequest*>)
195+
instr_push_enc_req(instr, item);
196+
else if constexpr (std::is_same_v<item_type, ZydisEncoderRequest>)
197+
instr_push_enc_req(instr, &item);
179198
// C-style strings are assumed to be a null-terminated array of bytes.
180199
else if constexpr (std::is_same_v<item_type, char const*>) {
181200
for (std::size_t i = 0; item[i]; ++i)
@@ -190,7 +209,7 @@ void binary::instr_push_item(instruction& instr,
190209
// This is another helper function for serializing integer types, since
191210
// it is done so often.
192211
template <typename T>
193-
void binary::instr_push_integer(instruction& instr, T const& value) const {
212+
inline void binary::instr_push_integer(instruction& instr, T const& value) const {
194213
// We need the integer to be unsigned so that we can safely do bitwise
195214
// operations on it (specifically, right-shift).
196215
auto unsigned_value = static_cast<std::make_unsigned_t<T>>(value);
@@ -202,5 +221,16 @@ void binary::instr_push_integer(instruction& instr, T const& value) const {
202221
}
203222
}
204223

224+
// Encode and push a new instruction.
225+
inline void binary::instr_push_enc_req(instruction& instr,
226+
ZydisEncoderRequest const* const enc_req) const {
227+
std::uint8_t bytes[15] = {};
228+
std::size_t length = sizeof(bytes);
229+
230+
ZydisEncoderEncodeInstruction(enc_req, bytes, &length);
231+
std::memcpy(instr.bytes, bytes, length);
232+
instr.length = length;
233+
}
234+
205235
} // namespace chum
206236

chum/source/disassembler.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -1019,8 +1019,10 @@ std::optional<disassembled_binary> disassemble(char const* const path) {
10191019
disassembler dasm = {};
10201020

10211021
// Initialize the disassembler.
1022-
if (!dasm.initialize(path))
1022+
if (!dasm.initialize(path)) {
1023+
printf("Failed to initialize disassembler!\n");
10231024
return {};
1025+
}
10241026

10251027
// Create a data block for every data section.
10261028
dasm.create_section_data_blocks();
@@ -1031,13 +1033,14 @@ std::optional<disassembled_binary> disassemble(char const* const path) {
10311033
dasm.parse_exceptions();
10321034
dasm.parse_relocs();
10331035

1034-
if (!dasm.disassemble())
1036+
if (!dasm.disassemble()) {
1037+
printf("Failed to disassemble binary!\n");
10351038
return {};
1039+
}
10361040

10371041
dasm.sort_basic_blocks();
10381042

10391043
assert(dasm.verify());
1040-
10411044
return std::move(dasm.bin);
10421045
}
10431046

chum/source/main.cpp

+50-7
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,59 @@ void shuffle_blocks(chum::binary& bin) {
3535
std::shuffle(std::begin(bin.basic_blocks()), std::end(bin.basic_blocks()), rng);
3636
}
3737

38-
int main() {
39-
auto bin = chum::disassemble("hello-world-x64.dll");
40-
if (!bin)
38+
void transform(chum::binary& bin) {
39+
for (auto const bb : bin.basic_blocks()) {
40+
for (auto i = bb->instructions.size(); i > 0; --i) {
41+
auto& instr = bb->instructions[i - 1];
42+
43+
ZydisDecodedInstruction dinstr = {};
44+
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
45+
46+
// Fully decode the current instruction.
47+
if (!ZYAN_SUCCESS(ZydisDecoderDecodeFull(bin.decoder(),
48+
instr.bytes, instr.length, &dinstr, operands)))
49+
continue;
50+
51+
ZydisEncoderRequest req = {};
52+
ZydisEncoderDecodedInstructionToEncoderRequest(&dinstr,
53+
operands, dinstr.operand_count_visible, &req);
54+
55+
// Split a single ADD instruction into 2 ADDs.
56+
if (req.mnemonic == ZYDIS_MNEMONIC_ADD && req.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) {
57+
auto const r = rand();
58+
59+
// First ADD.
60+
req.operands[1].imm.s -= r;
61+
instr = bin.instr(req);
62+
63+
// Second ADD.
64+
req.operands[1].imm.s = r;
65+
bb->insert(bin.instr(req), i);
66+
}
67+
}
68+
}
69+
}
70+
71+
int main(int const argc, char const* const* argv) {
72+
if (argc < 2) {
73+
std::printf("Not enough arguments.\n");
4174
return 0;
75+
}
4276

43-
insert_nops(*bin);
44-
instrument(*bin);
77+
auto bin = chum::disassemble(argv[1]);
78+
if (!bin) {
79+
std::printf("Failed to disassemble binary.\n");
80+
return 0;
81+
}
82+
83+
// insert_nops(*bin);
84+
// instrument(*bin);
4585
shuffle_blocks(*bin);
86+
// transform(*bin);
87+
88+
for (auto const b : bin->create())
89+
std::printf("%.2X", b);
4690

47-
bin->print();
48-
bin->create("output.dll");
91+
return 0;
4992
}
5093

0 commit comments

Comments
 (0)