Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bindings/cs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# libhat-sharp

These bindings are not up-to-date, but should be compatible with v0.9.0
160 changes: 119 additions & 41 deletions bindings/java/src/main/java/me/zero/libhat/Hat.java

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions bindings/java/src/main/java/me/zero/libhat/LibhatException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package me.zero.libhat;

import me.zero.libhat.jna.Libhat;

/**
* @author Brady
*/
public class LibhatException extends RuntimeException {

private final int status;

LibhatException(final int status) {
super(Libhat.INSTANCE.libhat_status_to_string(status));
this.status = status;
}

/**
* @return The actual status code representing the error that occurred
*/
public final int status() {
return this.status;
}
}
105 changes: 102 additions & 3 deletions bindings/java/src/main/java/me/zero/libhat/ProcessModule.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,116 @@
package me.zero.libhat;

import com.sun.jna.Pointer;
import me.zero.libhat.jna.Libhat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;

/**
* @author Brady
*/
public final class ProcessModule {
public final class ProcessModule implements AutoCloseable {

@NotNull
@Nullable
Pointer handle;

ProcessModule(@NotNull final Pointer handle) {
this.handle = handle;
this.handle = Objects.requireNonNull(handle);
}

@Override
public void close() {
if (this.handle != Pointer.NULL) {
Libhat.INSTANCE.libhat_free(this.handle);
this.handle = Pointer.NULL;
}
}

/**
* @return The base address of this module, as a {@code long}.
*/
public long getBaseAddress() {
return Libhat.INSTANCE.libhat_module_address(this.checkHandle()).longValue();
}

/**
* Returns the complete memory region for this module. This may include portions which are uncommitted.
* 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
*/
public @NotNull ByteBuffer getModuleData() {
final ByteBuffer data = Libhat.INSTANCE.libhat_module_get_data(this.checkHandle()).toBuffer();
if (data == null) {
throw new IllegalStateException("Module data was unexpectedly empty");
}
return data;
}

/**
* Returns the executable memory region containing machine code for the module. The standard section which
* contains executable code for the current platform will be returned first. If it cannot be identified
* 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.
*/
public @NotNull Optional<ByteBuffer> getExecutableData() {
return Optional.ofNullable(Libhat.INSTANCE.libhat_module_get_executable_data(this.checkHandle()).toBuffer());
}

/**
* Returns the memory region for a named section. On Linux-based platforms, section names are lazily loaded
* from the file that initialized the module, and internally cached for subsequent calls. On systems using the
* Mach-O format, "SEGNAME,SECNAME" is supported for disambiguation. i.e. "__TEXT,__const" vs "__DATA,__const"
*
* @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}
*/
public @NotNull Optional<ByteBuffer> getSectionData(@NotNull final String name) {
Objects.requireNonNull(name);
return Optional.ofNullable(Libhat.INSTANCE.libhat_module_get_section_data(this.checkHandle(), name).toBuffer());
}

/**
* Invokes the callback for each named linker section defined by this module as long as it returns true. The
* returned byte range is not guaranteed to have page aligned begin and end addresses. The returned protections
* are yielded from the section headers, and may not reflect the current virtual protections for the relevant
* memory pages. On Linux-based platforms, section names are lazily loaded from the file that initialized the
* module, and internally cached for subsequent calls.
*
* @param callback The callback to accept each section
* @throws NullPointerException if any arguments are {@code null}
*/
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)
-> callback.test(new Section(name, data.toBuffer(), Protection.fromFlags(prot))), null);
}

/**
* Invokes the callback for each memory segment defined by this module as long as it returns true. Depending on
* the platform, a segment may be represented by multiple linker sections. The returned byte range is not
* guaranteed to have page aligned begin and end addresses. The returned protections are yielded from the
* segment headers, and may not reflect the current virtual protections for the relevant memory pages.
*
* @param callback The callback to accept each segment
* @throws NullPointerException if any arguments are {@code null}
*/
public void forEachSegment(@NotNull final Predicate<@NotNull Segment> callback) {
Objects.requireNonNull(callback);
Libhat.INSTANCE.libhat_module_for_each_segment(this.checkHandle(), (data, prot, ud)
-> callback.test(new Segment(data.toBuffer(), Protection.fromFlags(prot))), null);
}

@NotNull
private Pointer checkHandle() {
return Objects.requireNonNull(this.handle,
"Attempted operation on a ProcessModule that has already been freed");
}
}
30 changes: 30 additions & 0 deletions bindings/java/src/main/java/me/zero/libhat/Protection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package me.zero.libhat;

import org.jetbrains.annotations.NotNull;

import java.util.EnumSet;

/**
* @author Brady
*/
public enum Protection {
READ,
WRITE,
EXECUTE;

private final int bit;

Protection() {
this.bit = (1 << this.ordinal());
}

static @NotNull EnumSet<Protection> fromFlags(final int flags) {
final EnumSet<Protection> set = EnumSet.noneOf(Protection.class);
for (final Protection flag : Protection.values()) {
if ((flags & flag.bit) != 0) {
set.add(flag);
}
}
return set;
}
}
37 changes: 37 additions & 0 deletions bindings/java/src/main/java/me/zero/libhat/ScanHint.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package me.zero.libhat;

import org.jetbrains.annotations.NotNull;

/**
* @author Brady
*/
public enum ScanHint {
/**
* The data being scanned is x86_64 machine code
*/
X86_64,

/**
* Only utilize byte pair based scanning if the signature starts with a byte pair
*/
PAIR0,

/**
* The data being scanned is AArch64 machine code
*/
AARCH64;

private final int bit;

ScanHint() {
this.bit = (1 << this.ordinal());
}

static int toFlags(@NotNull ScanHint... hints) {
int mask = 0;
for (final ScanHint hint : hints) {
mask |= hint.bit;
}
return mask;
}
}
42 changes: 42 additions & 0 deletions bindings/java/src/main/java/me/zero/libhat/Section.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package me.zero.libhat;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.nio.ByteBuffer;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Optional;

/**
* @author Brady
*/
public final class Section {

@NotNull
private final String name;

@Nullable
private final ByteBuffer data;

@NotNull
private final EnumSet<Protection> protection;

Section(@NotNull final String name, @Nullable final ByteBuffer data, @NotNull final EnumSet<Protection> protection) {
this.name = Objects.requireNonNull(name);
this.data = data;
this.protection = Objects.requireNonNull(protection);
}

public @NotNull String getName() {
return this.name;
}

public @NotNull Optional<ByteBuffer> getData() {
return Optional.ofNullable(this.data);
}

public @NotNull EnumSet<Protection> getProtection() {
return this.protection;
}
}
34 changes: 34 additions & 0 deletions bindings/java/src/main/java/me/zero/libhat/Segment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package me.zero.libhat;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.nio.ByteBuffer;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Optional;

/**
* @author Brady
*/
public final class Segment {

@Nullable
private final ByteBuffer data;

@NotNull
private final EnumSet<Protection> protection;

Segment(@Nullable final ByteBuffer data, @NotNull final EnumSet<Protection> protection) {
this.data = data;
this.protection = Objects.requireNonNull(protection);
}

public @NotNull Optional<ByteBuffer> getData() {
return Optional.ofNullable(data);
}

public @NotNull EnumSet<Protection> getProtection() {
return protection;
}
}
Loading
Loading