22
33#include " blook/disassembly.h"
44
5+ #include " blook/hook.h"
56#include " blook/memo.h"
67#include " blook/process.h"
78#include " zasm/formatter/formatter.hpp"
89#include < algorithm>
910#include < cstdint>
1011#include < utility>
1112
13+
1214#include < iostream>
1315
1416namespace blook {
@@ -22,7 +24,7 @@ Pointer::Pointer(std::shared_ptr<Process> proc, size_t offset)
2224 : _offset(offset), proc(std::move(proc)) {}
2325
2426MemoryPatch
25- Pointer::reassembly (std::function<void (zasm::x86::Assembler)> func) {
27+ Pointer::reassembly (std::function<void (zasm::x86::Assembler& )> func) {
2628 using namespace zasm ;
2729 Program program (utils::compileMachineMode ());
2830 x86::Assembler b (program);
@@ -153,6 +155,45 @@ MemoryRange Pointer::range_size(std::size_t size) {
153155 return MemoryRange{*this , size};
154156}
155157
158+ std::expected<MemoryRange, std::string>
159+ Pointer::try_range_next_instr (int num_of_instructions) {
160+ if (num_of_instructions <= 0 ) {
161+ return MemoryRange{*this , 0 };
162+ }
163+
164+ try {
165+ auto disasm = this ->range_size (num_of_instructions * 15 ).disassembly ();
166+ size_t total_size = 0 ;
167+ int count = 0 ;
168+
169+ for (const auto &instr : disasm) {
170+ if (count >= num_of_instructions) {
171+ break ;
172+ }
173+ total_size += instr->getLength ();
174+ count++;
175+ }
176+
177+ if (count < num_of_instructions) {
178+ return std::unexpected (
179+ std::format (" Could only find {} instructions, requested {}" , count,
180+ num_of_instructions));
181+ }
182+
183+ return MemoryRange{*this , total_size};
184+ } catch (const std::exception &e) {
185+ return std::unexpected (
186+ std::format (" Failed to get instruction range: {}" , e.what ()));
187+ }
188+ }
189+
190+ MemoryRange Pointer::range_next_instr (int num_of_instructions) {
191+ auto result = try_range_next_instr (num_of_instructions);
192+ if (!result) {
193+ throw std::runtime_error (result.error ());
194+ }
195+ return *result;
196+ }
156197
157198MemoryRange::MemoryRange (std::shared_ptr<Process> proc, void *offset,
158199 size_t size)
@@ -224,7 +265,7 @@ MemoryRange::find_one_remote(std::vector<uint8_t> pattern) const {
224265 return res.ptr ;
225266}
226267MemoryPatch Pointer::reassembly_thread_pause () {
227- return reassembly ([](zasm::x86::Assembler a) {
268+ return reassembly ([](zasm::x86::Assembler& a) {
228269#if defined(BLOOK_ARCHITECTURE_X86_64) || defined(BLOOK_ARCHITECTURE_X86_32)
229270 // EB FE: jmp $0
230271 a.db (0xeb );
@@ -234,7 +275,155 @@ MemoryPatch Pointer::reassembly_thread_pause() {
234275#endif
235276 });
236277}
278+
279+ std::expected<MemoryPatch, std::string> Pointer::try_reassembly_with_trampoline (
280+ std::function<void (zasm::x86::Assembler&)> func) {
281+ using namespace zasm ;
282+
283+ try {
284+ // Calculate min_bytes based on the jump instruction size
285+ // We need enough space for push/ret trick
286+ // For x64: push imm32 (6) + mov [rsp+4], imm32 (8) + ret (1) = 15 bytes
287+ // For x86: push imm32 (5) + ret (1) = 6 bytes
288+ size_t min_bytes;
289+ if constexpr (utils::compileArchitecture () == utils::Architecture::x86_64) {
290+ min_bytes = 15 ;
291+ } else {
292+ min_bytes = 6 ;
293+ }
294+
295+ // Step 1: Create trampoline for original code
296+ Trampoline trampoline = Trampoline::make (*this , min_bytes, true );
297+
298+ Pointer new_mem = this ->malloc_rx_near_this (1024 );
299+ if (new_mem.data () == nullptr ) {
300+ return std::unexpected (" Failed to allocate memory for user code" );
301+ }
302+
303+ Program user_program (utils::compileMachineMode ());
304+ x86::Assembler user_asm (user_program);
305+
306+ func (user_asm);
307+
308+ auto trampoline_addr = (size_t )trampoline.pTrampoline .data ();
309+ auto new_mem_addr = (size_t )new_mem.data ();
310+
311+ if constexpr (utils::compileArchitecture () == utils::Architecture::x86_64) {
312+ user_asm.jmp (Imm64 (trampoline_addr));
313+ } else {
314+ user_asm.jmp (Imm32 (trampoline_addr));
315+ }
316+
317+ Serializer user_serializer;
318+ if (auto err = user_serializer.serialize (user_program, new_mem_addr);
319+ err != ErrorCode::None) {
320+ return std::unexpected (std::format (" Failed to serialize user code: {} {}" ,
321+ err.getErrorName (),
322+ err.getErrorMessage ()));
323+ }
324+
325+ {
326+ ScopedSetMemoryRWX protector (new_mem, user_serializer.getCodeSize ());
327+ new_mem.write_bytearray (
328+ std::span<const uint8_t >((const uint8_t *)user_serializer.getCode (),
329+ user_serializer.getCodeSize ()));
330+ }
331+
332+ Program hook_program (utils::compileMachineMode ());
333+ x86::Assembler hook_asm (hook_program);
334+
335+ auto orig_addr = (size_t )this ->data ();
336+
337+ if constexpr (utils::compileArchitecture () == utils::Architecture::x86_64) {
338+ hook_asm.jmp (Imm64 (new_mem_addr));
339+ } else {
340+ hook_asm.jmp (Imm32 (new_mem_addr));
341+ }
342+
343+ // Serialize hook code
344+ Serializer hook_serializer;
345+ if (auto err = hook_serializer.serialize (hook_program, orig_addr);
346+ err != ErrorCode::None) {
347+ return std::unexpected (std::format (" Failed to serialize hook code: {} {}" ,
348+ err.getErrorName (),
349+ err.getErrorMessage ()));
350+ }
351+
352+ const auto hook_size = hook_serializer.getCodeSize ();
353+ if (hook_size > min_bytes) {
354+ return std::unexpected (
355+ std::format (" Hook code size ({}) exceeds minimum bytes ({})" ,
356+ hook_size, min_bytes));
357+ }
358+
359+ // Create patch with padding
360+ std::vector<uint8_t > patch_data (min_bytes);
361+ std::memcpy (patch_data.data (), hook_serializer.getCode (), hook_size);
362+
363+ return MemoryPatch{*this , patch_data};
364+
365+ } catch (const std::exception &e) {
366+ return std::unexpected (
367+ std::format (" Failed to create trampoline reassembly: {}" , e.what ()));
368+ }
369+ }
370+
371+ MemoryPatch Pointer::reassembly_with_trampoline (
372+ std::function<void (zasm::x86::Assembler&)> func) {
373+ auto result = try_reassembly_with_trampoline (func);
374+ if (!result) {
375+ throw std::runtime_error (result.error ());
376+ }
377+ return std::move (*result);
378+ }
379+
237380bool Pointer::is_valid () const {
238381 return proc != nullptr && proc->check_valid ((void *)_offset);
239382}
383+
384+ std::expected<MemoryPatch, std::string>
385+ MemoryRange::try_reassembly_with_padding (
386+ std::function<void (zasm::x86::Assembler&)> func) {
387+ using namespace zasm ;
388+ Program program (utils::compileMachineMode ());
389+ x86::Assembler b (program);
390+ func (b);
391+
392+ const auto code_size = utils::estimateCodeSize (program);
393+
394+ // Check if code is too large
395+ if (code_size > _size) {
396+ return std::unexpected (
397+ std::format (" Generated code size ({}) exceeds available space ({})" ,
398+ code_size, _size));
399+ }
400+
401+ std::vector<uint8_t > data (_size);
402+
403+ Serializer serializer;
404+ if (auto err = serializer.serialize (program, this ->_offset );
405+ err != zasm::ErrorCode::None)
406+ return std::unexpected (std::format (" JIT Serialization failure: {} {}" ,
407+ err.getErrorName (),
408+ err.getErrorMessage ()));
409+
410+ // Copy generated code
411+ std::memcpy (data.data (), serializer.getCode (), code_size);
412+
413+ // Fill remaining space with NOPs
414+ if (code_size < _size) {
415+ std::memset (data.data () + code_size, 0x90 , _size - code_size);
416+ }
417+
418+ return MemoryPatch{*this , data};
419+ }
420+
421+ MemoryPatch MemoryRange::reassembly_with_padding (
422+ std::function<void (zasm::x86::Assembler&)> func) {
423+ auto result = try_reassembly_with_padding (func);
424+ if (!result) {
425+ throw std::runtime_error (result.error ());
426+ }
427+ return std::move (*result);
428+ }
240429} // namespace blook
0 commit comments