diff --git a/bindings/java/src/main/java/me/zero/libhat/Hat.java b/bindings/java/src/main/java/me/zero/libhat/Hat.java index 7822677..98c983c 100644 --- a/bindings/java/src/main/java/me/zero/libhat/Hat.java +++ b/bindings/java/src/main/java/me/zero/libhat/Hat.java @@ -136,14 +136,20 @@ public static OptionalInt findPattern(@NotNull final Signature signature, @NotNu final long start = Pointer.nativeValue(Native.getDirectBufferPointer(buffer)) + buffer.position(); final int count = buffer.remaining(); - final Pointer result = Libhat.INSTANCE.libhat_find_pattern( + final PointerByReference out = new PointerByReference(); + final int status = Libhat.INSTANCE.libhat_find_pattern( Objects.requireNonNull(signature.handle), new Pointer(start), new Libhat.size_t(count), + out, alignment.alignment(), ScanHint.toFlags(hints) ); + if (status != 0) { + throw new LibhatException(status); + } + final Pointer result = out.getValue(); if (result == Pointer.NULL) { return OptionalInt.empty(); } @@ -190,15 +196,20 @@ public static Optional findPattern(@NotNull final Signature signature, Objects.requireNonNull(section); Objects.requireNonNull(alignment); - final Pointer result = Libhat.INSTANCE.libhat_find_pattern_mod( + final PointerByReference out = new PointerByReference(); + final int status = Libhat.INSTANCE.libhat_find_pattern_mod( Objects.requireNonNull(signature.handle), Objects.requireNonNull(module.handle), section, + out, alignment.alignment(), ScanHint.toFlags(hints) ); + if (status != 0) { + throw new LibhatException(status); + } - return Optional.ofNullable(result); + return Optional.ofNullable(out.getValue()); } /** diff --git a/bindings/java/src/main/java/me/zero/libhat/ProcessModule.java b/bindings/java/src/main/java/me/zero/libhat/ProcessModule.java index aec34d0..5b423cb 100644 --- a/bindings/java/src/main/java/me/zero/libhat/ProcessModule.java +++ b/bindings/java/src/main/java/me/zero/libhat/ProcessModule.java @@ -1,6 +1,7 @@ package me.zero.libhat; import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; import me.zero.libhat.jna.Libhat; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,9 +33,15 @@ public void close() { /** * @return The base address of this module, as a {@code long}. + * @throws LibhatException if an internal error occurred */ public long getBaseAddress() { - return Libhat.INSTANCE.libhat_module_address(this.checkHandle()).longValue(); + PointerByReference out = new PointerByReference(); + final int status = Libhat.INSTANCE.libhat_module_address(this.checkHandle(), out); + if (status != 0) { + throw new LibhatException(status); + } + return Pointer.nativeValue(out.getValue()); } /** @@ -42,10 +49,17 @@ public long getBaseAddress() { * To verify whether the region is safe to read, use {@link Hat#isReadable(ByteBuffer)}. * * @return The module data - * @throws IllegalStateException If the module data was empty + * @throws IllegalStateException if the module data was empty + * @throws LibhatException if an internal error occurred */ public @NotNull ByteBuffer getModuleData() { - final ByteBuffer data = Libhat.INSTANCE.libhat_module_get_data(this.checkHandle()).toBuffer(); + final Libhat.Span.ByReference out = new Libhat.Span.ByReference(); + final int status = Libhat.INSTANCE.libhat_module_get_data(this.checkHandle(), out); + if (status != 0) { + throw new LibhatException(status); + } + + final ByteBuffer data = out.toBuffer(); if (data == null) { throw new IllegalStateException("Module data was unexpectedly empty"); } @@ -58,9 +72,16 @@ public long getBaseAddress() { * by name, the first executable region defined by the module will be returned instead. * * @return The module's executable data, or {@link Optional#empty()} if it cannot be found. + * @throws LibhatException if an internal error occurred */ public @NotNull Optional getExecutableData() { - return Optional.ofNullable(Libhat.INSTANCE.libhat_module_get_executable_data(this.checkHandle()).toBuffer()); + final Libhat.Span.ByReference out = new Libhat.Span.ByReference(); + final int status = Libhat.INSTANCE.libhat_module_get_executable_data(this.checkHandle(), out); + if (status != 0) { + throw new LibhatException(status); + } + + return Optional.ofNullable(out.toBuffer()); } /** @@ -71,10 +92,18 @@ public long getBaseAddress() { * @param name The section name * @return The data for the section, or {@link Optional#empty()} if it cannot be found. * @throws NullPointerException if any arguments are {@code null} + * @throws LibhatException if an internal error occurred */ public @NotNull Optional getSectionData(@NotNull final String name) { Objects.requireNonNull(name); - return Optional.ofNullable(Libhat.INSTANCE.libhat_module_get_section_data(this.checkHandle(), name).toBuffer()); + + final Libhat.Span.ByReference out = new Libhat.Span.ByReference(); + final int status = Libhat.INSTANCE.libhat_module_get_section_data(this.checkHandle(), name, out); + if (status != 0) { + throw new LibhatException(status); + } + + return Optional.ofNullable(out.toBuffer()); } /** @@ -86,11 +115,15 @@ public long getBaseAddress() { * * @param callback The callback to accept each section * @throws NullPointerException if any arguments are {@code null} + * @throws LibhatException if an internal error occurred */ public void forEachSection(@NotNull final Predicate<@NotNull Section> callback) { Objects.requireNonNull(callback); - Libhat.INSTANCE.libhat_module_for_each_section(this.checkHandle(), (name, data, prot, ud) + final int status = Libhat.INSTANCE.libhat_module_for_each_section(this.checkHandle(), (name, data, prot, ud) -> callback.test(new Section(name, data.toBuffer(), Protection.fromFlags(prot))), null); + if (status != 0) { + throw new LibhatException(status); + } } /** @@ -101,11 +134,15 @@ public void forEachSection(@NotNull final Predicate<@NotNull Section> callback) * * @param callback The callback to accept each segment * @throws NullPointerException if any arguments are {@code null} + * @throws LibhatException if an internal error occurred */ public void forEachSegment(@NotNull final Predicate<@NotNull Segment> callback) { Objects.requireNonNull(callback); - Libhat.INSTANCE.libhat_module_for_each_segment(this.checkHandle(), (data, prot, ud) + final int status = Libhat.INSTANCE.libhat_module_for_each_segment(this.checkHandle(), (data, prot, ud) -> callback.test(new Segment(data.toBuffer(), Protection.fromFlags(prot))), null); + if (status != 0) { + throw new LibhatException(status); + } } @NotNull diff --git a/bindings/java/src/main/java/me/zero/libhat/jna/Libhat.java b/bindings/java/src/main/java/me/zero/libhat/jna/Libhat.java index 44aa79b..d814c95 100644 --- a/bindings/java/src/main/java/me/zero/libhat/jna/Libhat.java +++ b/bindings/java/src/main/java/me/zero/libhat/jna/Libhat.java @@ -45,7 +45,7 @@ public uintptr_t(long value) { // Structures // ------------------------------------------------------------------------ - @Structure.FieldOrder({ "data", "size" }) + @Structure.FieldOrder({"data", "size"}) class Span extends Structure { public Pointer data; public size_t size; @@ -66,6 +66,7 @@ public final ByteBuffer toBuffer() { } public static class ByValue extends Span implements Structure.ByValue {} + public static class ByReference extends Span implements Structure.ByReference {} } // ------------------------------------------------------------------------ @@ -118,64 +119,66 @@ interface ForEachSegmentCallback extends Callback { int libhat_create_signature(byte[] bytes, byte[] mask, size_t size, PointerByReference signatureOut); /* - * const void* libhat_find_pattern( + * libhat_status libhat_find_pattern( * const libhat_signature* signature, * const void* buffer, * size_t size, + * const void** resultOut, * libhat_alignment align, - * libhat_hint hints + * libhat_hint hints * ); */ - Pointer libhat_find_pattern(Pointer signature, Pointer buffer, size_t size, int align, int hints); + int libhat_find_pattern(Pointer signature, Pointer buffer, size_t size, PointerByReference resultOut, int align, int hints); /* - * const void* libhat_find_pattern_mod( + * libhat_status libhat_find_pattern_mod( * const libhat_signature* signature, * const libhat_module* module, * const char* section, + * const void** resultOut, * libhat_alignment align, * libhat_hint hints * ); */ - Pointer libhat_find_pattern_mod(Pointer signature, Pointer module, String section, int align, int hints); + int libhat_find_pattern_mod(Pointer signature, Pointer module, String section, PointerByReference resultOut, int align, int hints); /* - * uintptr_t libhat_module_address(const libhat_module* module); + * libhat_status libhat_module_address(const libhat_module* module, uintptr_t* out); */ - uintptr_t libhat_module_address(Pointer module); + int libhat_module_address(Pointer module, PointerByReference out); /* - * libhat_span libhat_module_get_data(const libhat_module* module); + * libhat_status libhat_module_get_data(const libhat_module* module, libhat_span* out); */ - Span.ByValue libhat_module_get_data(Pointer module); + int libhat_module_get_data(Pointer module, Span.ByReference data); /* - * libhat_span libhat_module_get_executable_data(const libhat_module* module); + * libhat_status libhat_module_get_executable_data(const libhat_module* module, libhat_span* out); */ - Span.ByValue libhat_module_get_executable_data(Pointer module); + int libhat_module_get_executable_data(Pointer module, Span.ByReference data); /* - * libhat_span libhat_module_get_section_data(const libhat_module* module, const char* name); + * libhat_status libhat_module_get_section_data(const libhat_module* module, const char* name, libhat_span* out); */ - Span.ByValue libhat_module_get_section_data(Pointer module, String name); + int libhat_module_get_section_data(Pointer module, String name, Span.ByReference data); /* - * void libhat_module_for_each_section( + * libhat_status libhat_module_for_each_section( * const libhat_module* module, * libhat_for_each_section_cb callback, * void* user_data * ); */ - void libhat_module_for_each_section(Pointer module, ForEachSectionCallback callback, Pointer userData); + int libhat_module_for_each_section(Pointer module, ForEachSectionCallback callback, Pointer userData); /* - * void libhat_module_for_each_segment( + * libhat_status libhat_module_for_each_segment( * const libhat_module* module, * libhat_for_each_segment_cb callback, * void* user_data * ); */ - void libhat_module_for_each_segment(Pointer module, ForEachSegmentCallback callback, Pointer userData); + int libhat_module_for_each_segment(Pointer module, ForEachSegmentCallback callback, Pointer userData); /* * bool libhat_is_readable(const void* data, size_t size); diff --git a/include/libhat/c/libhat.h b/include/libhat/c/libhat.h index 3fa3b4c..d86135f 100644 --- a/include/libhat/c/libhat.h +++ b/include/libhat/c/libhat.h @@ -36,6 +36,8 @@ typedef enum libhat_status { libhat_err_sig_empty_signature, libhat_err_sig_expected_wildcard, libhat_err_sig_invalid_token_length, + libhat_err_invalid_argument_value, + libhat_err_invalid_argument_type, } libhat_status; typedef enum libhat_alignment { @@ -89,37 +91,39 @@ LIBHAT_API libhat_status libhat_create_signature( const libhat_signature** signatureOut ); -LIBHAT_API const void* libhat_find_pattern( +LIBHAT_API libhat_status libhat_find_pattern( const libhat_signature* signature, const void* buffer, size_t size, + const void** resultOut, libhat_alignment align = libhat_alignment_x1, libhat_hint hints = libhat_hint_none ); -LIBHAT_API const void* libhat_find_pattern_mod( +LIBHAT_API libhat_status libhat_find_pattern_mod( const libhat_signature* signature, const libhat_module* module, const char* section, + const void** resultOut, libhat_alignment align = libhat_alignment_x1, libhat_hint hints = libhat_hint_none ); -LIBHAT_API uintptr_t libhat_module_address(const libhat_module* module); +LIBHAT_API libhat_status libhat_module_address(const libhat_module* module, uintptr_t* out); -LIBHAT_API libhat_span libhat_module_get_data(const libhat_module* module); +LIBHAT_API libhat_status libhat_module_get_data(const libhat_module* module, libhat_span* out); -LIBHAT_API libhat_span libhat_module_get_executable_data(const libhat_module* module); +LIBHAT_API libhat_status libhat_module_get_executable_data(const libhat_module* module, libhat_span* out); -LIBHAT_API libhat_span libhat_module_get_section_data(const libhat_module* module, const char* name); +LIBHAT_API libhat_status libhat_module_get_section_data(const libhat_module* module, const char* name, libhat_span* out); -LIBHAT_API void libhat_module_for_each_section( +LIBHAT_API libhat_status libhat_module_for_each_section( const libhat_module* module, libhat_for_each_section_cb callback, void* user_data ); -LIBHAT_API void libhat_module_for_each_segment( +LIBHAT_API libhat_status libhat_module_for_each_segment( const libhat_module* module, libhat_for_each_segment_cb callback, void* user_data diff --git a/src/c/libhat.cpp b/src/c/libhat.cpp index aa75f95..4b3948a 100644 --- a/src/c/libhat.cpp +++ b/src/c/libhat.cpp @@ -5,15 +5,38 @@ namespace { + constexpr uint32_t object_magic = 0x2C360B8A; + + struct tombstone_type { + static constexpr uint32_t type_id = 0xF27EC9D1; + }; + + template + consteval uint32_t type_id() { + return T::type_id; + } + struct libhat_ffi_object { protected: - using destroy_t = void (*)(const libhat_ffi_object&); - destroy_t destroy; - - explicit libhat_ffi_object(const destroy_t destroy) - : destroy(destroy) {} + uint32_t magic; + mutable std::atomic type; + void (*destroy)(const libhat_ffi_object&); + + template + explicit libhat_ffi_object(std::type_identity) : + magic(object_magic), + type(type_id()), + destroy([](const libhat_ffi_object& object) { + delete static_cast(&object); + }) {} friend void ::libhat_free(const void* object); + + public: + template + [[nodiscard]] bool is_type() const { + return this->magic == object_magic && this->type == type_id(); + } }; template @@ -21,22 +44,27 @@ namespace { template explicit libhat_ffi_wrapper(Args&&... args) : - libhat_ffi_object([](const libhat_ffi_object& object) { - delete static_cast(&object); - }), + libhat_ffi_object(std::type_identity{}), T(std::forward(args)...) {} }; + + template + [[nodiscard]] bool check_type(const T* t) { + return static_cast(t)->is_type(); + } } struct libhat_signature final : libhat_ffi_wrapper { using libhat_ffi_wrapper::libhat_ffi_wrapper; + static constexpr uint32_t type_id = 0xFD19C2B3; }; struct libhat_module final : libhat_ffi_wrapper { using libhat_ffi_wrapper::libhat_ffi_wrapper; + static constexpr uint32_t type_id = 0xBAA5FEEC; }; -static hat::scan_alignment to_cpp_align(const libhat_alignment align) { +static std::optional to_cpp_align(const libhat_alignment align) { switch (align) { case libhat_alignment_x1: return hat::scan_alignment::X1; @@ -45,7 +73,7 @@ static hat::scan_alignment to_cpp_align(const libhat_alignment align) { case libhat_alignment_x16: return hat::scan_alignment::X16; } - exit(EXIT_FAILURE); + return std::nullopt; } static hat::scan_hint to_cpp_hints(const libhat_hint hints) { @@ -72,6 +100,8 @@ LIBHAT_API const char* libhat_status_to_string(const libhat_status status) { STATUS_CASE(libhat_err_sig_empty_signature); STATUS_CASE(libhat_err_sig_expected_wildcard); STATUS_CASE(libhat_err_sig_invalid_token_length); + STATUS_CASE(libhat_err_invalid_argument_value); + STATUS_CASE(libhat_err_invalid_argument_type); } #undef STATUS_CASE return "invalid value for libhat_status"; @@ -101,6 +131,13 @@ LIBHAT_API libhat_status libhat_create_signature( const size_t size, const libhat_signature** signatureOut ) { + if (!signatureOut) { + return libhat_err_invalid_argument_value; + } + if (size && (!bytes || !mask)) { + return libhat_err_invalid_argument_value; + } + hat::signature signature{}; bool containsByte = false; signature.reserve(size); @@ -117,61 +154,145 @@ LIBHAT_API libhat_status libhat_create_signature( return libhat_success; } -LIBHAT_API const void* libhat_find_pattern( +LIBHAT_API libhat_status libhat_find_pattern( const libhat_signature* signature, const void* buffer, const size_t size, + const void** resultOut, const libhat_alignment align, const libhat_hint hints ) { + if (!signature || (!buffer && size) || !resultOut) { + return libhat_err_invalid_argument_value; + } + + if (!check_type(signature)) { + return libhat_err_invalid_argument_type; + } + + const auto cpp_align = to_cpp_align(align); + if (!cpp_align) { + return libhat_err_invalid_argument_value; + } + const auto begin = static_cast(buffer); const auto end = static_cast(buffer) + size; - const auto result = hat::find_pattern(begin, end, *signature, to_cpp_align(align), to_cpp_hints(hints)); - return result.has_result() ? result.get() : nullptr; + const auto result = hat::find_pattern(begin, end, *signature, *cpp_align, to_cpp_hints(hints)); + *resultOut = result.has_result() ? result.get() : nullptr; + return libhat_success; } -LIBHAT_API const void* libhat_find_pattern_mod( +LIBHAT_API libhat_status libhat_find_pattern_mod( const libhat_signature* signature, const libhat_module* module, const char* section, + const void** resultOut, const libhat_alignment align, const libhat_hint hints ) { - const auto result = hat::find_pattern(*signature, section, *module, to_cpp_align(align), to_cpp_hints(hints)); - return result.has_result() ? result.get() : nullptr; + if (!signature || !module || !section || !resultOut) { + return libhat_err_invalid_argument_value; + } + + if (!check_type(signature) || !check_type(module)) { + return libhat_err_invalid_argument_type; + } + + const auto cpp_align = to_cpp_align(align); + if (!cpp_align) { + return libhat_err_invalid_argument_value; + } + + const auto result = hat::find_pattern(*signature, section, *module, *cpp_align, to_cpp_hints(hints)); + *resultOut = result.has_result() ? result.get() : nullptr; + return libhat_success; } -LIBHAT_API uintptr_t libhat_module_address(const libhat_module* module) { - return module->address(); +LIBHAT_API libhat_status libhat_module_address(const libhat_module* module, uintptr_t* out) { + if (!module || !out) { + return libhat_err_invalid_argument_value; + } + + if (!check_type(module)) { + return libhat_err_invalid_argument_type; + } + + *out = module->address(); + return libhat_success; } -LIBHAT_API libhat_span libhat_module_get_data(const libhat_module* module) { +LIBHAT_API libhat_status libhat_module_get_data(const libhat_module* module, libhat_span* out) { + if (!module || !out) { + return libhat_err_invalid_argument_value; + } + + if (!check_type(module)) { + return libhat_err_invalid_argument_type; + } + const auto data = module->get_module_data(); - return {data.data(), data.size()}; + *out = {data.data(), data.size()}; + return libhat_success; } -LIBHAT_API libhat_span libhat_module_get_executable_data(const libhat_module* module) { +LIBHAT_API libhat_status libhat_module_get_executable_data(const libhat_module* module, libhat_span* out) { + if (!module || !out) { + return libhat_err_invalid_argument_value; + } + + if (!check_type(module)) { + return libhat_err_invalid_argument_type; + } + const auto data = module->get_executable_data(); - return {data.data(), data.size()}; + *out = {data.data(), data.size()}; + return libhat_success; } -LIBHAT_API libhat_span libhat_module_get_section_data(const libhat_module* module, const char* name) { +LIBHAT_API libhat_status libhat_module_get_section_data(const libhat_module* module, const char* name, libhat_span* out) { + if (!module || !name || !out) { + return libhat_err_invalid_argument_value; + } + + if (!check_type(module)) { + return libhat_err_invalid_argument_type; + } + const auto data = module->get_section_data(name); - return {data.data(), data.size()}; + *out = {data.data(), data.size()}; + return libhat_success; } -LIBHAT_API void libhat_module_for_each_section(const libhat_module* module, const libhat_for_each_section_cb callback, void* user_data) { +LIBHAT_API libhat_status libhat_module_for_each_section(const libhat_module* module, const libhat_for_each_section_cb callback, void* user_data) { + if (!module || !callback) { + return libhat_err_invalid_argument_value; + } + + if (!check_type(module)) { + return libhat_err_invalid_argument_type; + } + std::string buffer; module->for_each_section([=, &buffer](auto name, auto data, auto prot) { buffer.assign(name); return callback(buffer.c_str(), {data.data(), data.size()}, static_cast(prot), user_data); }); + return libhat_success; } -LIBHAT_API void libhat_module_for_each_segment(const libhat_module* module, const libhat_for_each_segment_cb callback, void* user_data) { +LIBHAT_API libhat_status libhat_module_for_each_segment(const libhat_module* module, const libhat_for_each_segment_cb callback, void* user_data) { + if (!module || !callback) { + return libhat_err_invalid_argument_value; + } + + if (!check_type(module)) { + return libhat_err_invalid_argument_type; + } + module->for_each_segment([=](auto data, auto prot) { return callback({data.data(), data.size()}, static_cast(prot), user_data); }); + return libhat_success; } LIBHAT_API bool libhat_is_readable(const void* data, size_t size) { @@ -208,10 +329,24 @@ LIBHAT_API const libhat_module* libhat_module_at(const void* address) { } LIBHAT_API void libhat_free(const void* object) { - if (object) { - const auto p = static_cast(object); - p->destroy(*p); + if (!object) { + return; + } + + const auto& p = *static_cast(object); + if (p.magic != object_magic) { + std::terminate(); + } + + constexpr auto tombstone = type_id(); + if (p.type.exchange(tombstone, std::memory_order_relaxed) == tombstone) { + // Object not within its lifetime, already deleted. This could be indicative of a larger + // issue in user code, so it's best to not just ignore it. Not going to make this return + // a status code, just terminate for now. + std::terminate(); } + + p.destroy(p); } } // extern "C"