java - 如何在 64 位应用程序中以 32 位模式使用 BridJ?

标签 java jnaerator bridj

我正在尝试以某种方式使用 BridJ,但不确定它是否受支持:

我有一个 64 位应用程序。我通过调试器接口(interface)获取包含 32 位应用程序内存的字节数组。

我想使用 BridJ 将此字节数组的内容解析为 C 数据类型。我通过使用 Pointer.pointerToBytes().as(my_struct.class).get() 来完成此操作。该技术适用于 32 位应用程序。但在 64 位应用程序中则不然,因为 BridJ 使用主机应用程序的字大小(64 位),而我想使用字节数组的字大小(32 位)。

这可以吗?是否可以以某种方式手动切换 BridJ 的字大小,以便 BridJ 使用 32 位字,即使它在 64 位应用程序中运行?

我正在尝试做的事情的更长示例

byte[] objData = debugger.readMemory(remoteStructAddr, BridJ.sizeOf(c_struct.class));
// pointerToBytes only works as expected when objData has the same
// word size as the host system
c_struct s = Pointer.pointerToBytes(objData).as(c_struct.class).get();
int structMem = s.member();

// offsetOfField only works as expected when objData has the same
// word size as the host system
byte[] namePtr = debugger.readMemory(removePtr + StructObject.offsetOfField(new c_struct(), "name"), 4); 
String name = debugger.readString(namePtr);

更多详细信息

  • 我使用JNAerator生成 BridJ 类
  • 我尝试使用 JNAerator -arch 标志,但它似乎没有达到我想要的效果。
  • 我不将 native 库与 BridJ 一起使用,我只是使用 BridJ 读取通过调试器 API 获取的字节数组。

最佳答案

我通过编写自己的代码来解决这个问题,用于将具有 C 结构的字节数组读取到 Java 对象中。此代码是针对 32 位输入编写的,无论主机应用程序使用的字大小如何。

示例:

some_struct s = Bridj32.readObject(some_struct.class, 
    new byte[] { 0x01, 0x23, 0x45, 0x67 });

Bridj32 类包含此方法的实现。它采用用 BridJ 注释(FieldStructPtr 等)和带有数据的字节数组作为输入类。给出包含输入数组数据的已解析 Java 对象作为输出。

Bridj32 最棘手的部分是它实现了 C 结构打包算法。

Bridj32的代码:

import static java.util.stream.Collectors.toMap;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

import org.bridj.BridJ;
import org.bridj.Pointer;
import org.bridj.SizeT;
import org.bridj.StructIO;
import org.bridj.StructObject;
import org.bridj.ann.Array;
import org.bridj.ann.Bits;
import org.bridj.ann.Field;
import org.bridj.ann.Ptr;
import org.bridj.ann.Struct;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Primitives;

/**
 * Code for using {@link BridJ} in 32 bit mode, even in a 64 bit application.
 * Reads byte arrays into objects, gets offset of fields and gets the size of structs.
 * <p>
 * Assumes little-endian byte order for all input.
 * <p>
 * Input struct  types must be annotated with BridJ annotations, as if they where generated
 * with Jnaerator.
 */
public class Bridj32 {
    private static final Map<Class<?>, StructDescription> structCache = new ConcurrentHashMap<>();

    private static class StructDescription {
        public final int byteSize;
        public final int largestMemberSize;
        public final List<MemberDescription> members;
        @SuppressWarnings("unused")
        public final Class<? extends StructObject> structClass;

        public StructDescription(Class<? extends StructObject> structClass) {
            this.structClass = structClass;

            Map<Integer, Method> memMap = structFieldMethods(structClass)
                .collect(toMap(m -> m.getAnnotation(Field.class).value(), e -> e));

            Struct structAnn = structClass.getAnnotation(Struct.class);
            if (structAnn != null && structAnn.pack() > 1) {
                throw new UnsupportedOperationException("Packed structs are not supported. Struct: " + structClass);
            }

            ImmutableList.Builder<MemberDescription> mems = ImmutableList.builder();

            int offset = 0;
            int maxLargestMem = 0;

            for (Entry<Integer, Method> e : memMap.entrySet()) {
                Method memberMethod = e.getValue();

                int alignSize = alignSize(memberMethod);
                maxLargestMem = Math.max(maxLargestMem, alignSize);
                int size = calcByteSize(memberMethod);
                offset += alignPadSize(offset, alignSize);

                mems.add(new MemberDescription(memberMethod, e.getKey(), offset, size));

                offset += size;
            }

            largestMemberSize = maxLargestMem;

            members = mems.build();
            byteSize = offset;
        }

        public MemberDescription findMember(String memName) {
            for (MemberDescription desc : members) {
                if (desc.method.getName().equals(memName)) {
                    return desc;
                }
            }
            throw new NoSuchElementException(memName);
        }

    }

    static int alignPadSize(int size, int alignment) {
        int p = size % alignment;
        if (p == 0) return 0;
        else return alignment - p;
    }

    private static class MemberDescription {
        Method method;
        int index;
        int byteOffset;
        @SuppressWarnings("unused")
        int byteSize;

        public MemberDescription(Method method, int index, int byteOffset, int byteSize) {
            this.method = method;
            this.index = index;
            this.byteOffset = byteOffset;
            this.byteSize = byteSize;
        }

        public Class<?> getType() {
            return method.getReturnType();
        }
    }


    /**
     * @return The size of the C struct that corresponds to the input class
     *         argument. This size includes trailing padding in the struct.
     */
    public static int paddedSizeOf(Class<?> cls) {
        StructDescription desc = getStructDescription(cls);
        return desc.byteSize + alignPadSize(desc.byteSize, desc.largestMemberSize); 
    }

    private static Stream<Method> structFieldMethods(Class<? extends StructObject> structClass) {
        return Arrays.stream(structClass.getMethods())
            .filter(m -> m.getAnnotation(Field.class) != null)
            .filter(m -> m.getParameterCount() == 0);
    }

    private static StructDescription getStructDescription(Class<?> cls) {
        StructDescription result = structCache.get(cls);
        if (result == null) {
            @SuppressWarnings("unchecked")
            Class<? extends StructObject> structCls = (Class<? extends StructObject>) cls;
            result = new StructDescription(structCls);
            structCache.put(cls, result);
        }
        return result;
    }

    @SuppressWarnings("unused")
    private static int nrFields(Class<? extends StructObject> structClass) {
        int maxFieldNr = -1;
        for (Method f : structClass.getMethods()) {
            Field fieldAnn = f.getAnnotation(Field.class);
            if (fieldAnn != null) {
                maxFieldNr = Math.max(maxFieldNr, fieldAnn.value());
            }
        }
        return maxFieldNr + 1;
    }

    /**
     * @return The size of the C struct that corresponds to the input class
     *         argument. This size does not include trailing padding
     *         in the struct.
     */
    public static int sizeOf(Class<? extends StructObject> structClass) {
        return getStructDescription(structClass).byteSize;
    }

    private static int calcByteSize(Method memMeth) {
        Array arrayAnn = memMeth.getAnnotation(Array.class);
        int mult = arrayAnn == null
            ? 1 : (int) Arrays.stream(arrayAnn.value()).reduce(1, (a, b) -> a * b);

        int sizeSingle = calcByteSizeSingle(memMeth);

        return mult * sizeSingle;
    }

    private static int alignSize(Method memMeth) {
        if (StructObject.class.isAssignableFrom(memMeth.getReturnType())) {
            return getStructDescription(memMeth.getReturnType()).largestMemberSize;
        } else {
            return primitiveByteSize(memMeth);
        }
    }

    private static int primitiveByteSize(Method memMeth) {
        Class<?> cls = Primitives.wrap(memMeth.getReturnType());

        if (memMeth.getAnnotation(Ptr.class) != null) return 4;

        if (memMeth.getAnnotation(Bits.class) != null) {
            throw new UnsupportedOperationException("Bit fields are not supported. Method: " + memMeth);
        }

        if (cls == Boolean.class) return 1;
        if (cls == Byte.class   ) return 1;
        if (cls == Short.class  ) return 2;
        if (cls == Integer.class) return 4;
        if (cls == Long.class   ) return 8;
        if (cls == Float.class  ) return 4;
        if (cls == Double.class ) return 8;

        if (cls == String.class ) return 4;

        if (cls == Pointer.class) return 4;
        if (cls == SizeT.class  ) return 4;

        throw new IllegalArgumentException("Unknown type: " + cls);
    }

    private static int calcByteSizeSingle(Method memMeth) {
        Class<?> cls = memMeth.getReturnType();
        if (StructObject.class.isAssignableFrom(cls)) {
            return paddedSizeOf(cls);
        } else {
            return primitiveByteSize(memMeth);
        }
    }

    /**
     * Reads one object (and all its members) from the bytes array.
     */
    public static <T extends StructObject> T readObject(Class<T> structClass, byte[] bytes) {
        ByteBuffer buff = ByteBuffer.wrap(bytes, 0, bytes.length).order(ByteOrder.LITTLE_ENDIAN);
        return readObject(structClass, buff, 0);
    }

    private static <T extends StructObject> T readObject(Class<T> structClass, ByteBuffer buff, int offset) {
        StructDescription desc = getStructDescription(structClass);
        Preconditions.checkArgument(buff.capacity() - offset >= desc.byteSize);
        StructIO io = StructIO.getInstance(structClass);

        T struct;
        try {
            struct = structClass.getConstructor().newInstance();
        } catch (ReflectiveOperationException exc) {
            throw new RuntimeException(exc);
        }

        for (MemberDescription memDesc : desc.members) {
            setField(memDesc, struct, io, buff, offset);
        }

        return struct;
    }

    public static int offsetOfField(Class<? extends StructObject> structClass, String memName) {
        return getStructDescription(structClass).findMember(memName).byteOffset;
    }

    private static void setField(MemberDescription desc, StructObject struct, StructIO io, ByteBuffer bytes, int structOffset) {
        Class<?> cls = Primitives.wrap(desc.getType());
        int offset = desc.byteOffset + structOffset;
        int ix = desc.index;

             if (cls == Boolean.class) io.setBooleanField(struct, ix, bytes.get(offset) != 0);
        else if (cls == Byte.class   ) io.setByteField   (struct, ix, bytes.get(offset));
        else if (cls == Short.class  ) io.setShortField  (struct, ix, bytes.getShort(offset));
        else if (cls == Integer.class) io.setIntField    (struct, ix, bytes.getInt(offset));
        else if (cls == Long.class   ) io.setLongField   (struct, ix, bytes.getLong(offset));
        else if (cls == Float.class  ) io.setFloatField  (struct, ix, bytes.getFloat(offset));
        else if (cls == Double.class ) io.setDoubleField (struct, ix, bytes.getDouble(offset));

        else if (cls == String.class) throw new UnsupportedOperationException();

        else if (SizeT.class.isAssignableFrom(cls)) {
            io.setSizeTField(struct, offset, Integer.toUnsignedLong(bytes.getInt(offset)));
        } else if (Pointer.class.isAssignableFrom(cls)) {
            @SuppressWarnings("deprecation")
            Pointer<?> p = Pointer.pointerToAddress(Integer.toUnsignedLong(bytes.getInt(offset)));
            io.setPointerField(struct, ix, p);
        } else if (StructObject.class.isAssignableFrom(cls)) {
            @SuppressWarnings("unchecked")
            StructObject o = readObject((Class<? extends StructObject>) cls, bytes, offset);
            io.setNativeObjectField(struct, ix, o);
        }
    }

    /**
     * Reads all objects of type objCls form arrData and returns them. The number of objects
     * depend on the binary size of objCls objects. 
     * <p>
     * If the size of arrData is not evenly devisible by the size of objCls
     *  
     */
    public static <T extends StructObject> List<T> readAllObjects(Class<T> objCls, byte[] arrData) {
        int strideSize = paddedSizeOf(objCls);
        List<T> result = new ArrayList<>();
        ByteBuffer buff = ByteBuffer.wrap(arrData).order(ByteOrder.LITTLE_ENDIAN);

        for (int offset = 0; offset < arrData.length; offset += strideSize) {
            result.add(readObject(objCls, buff, offset));
        }
        return result;
    }
}

关于java - 如何在 64 位应用程序中以 32 位模式使用 BridJ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49706267/

相关文章:

java - 如何将指针引用地址传递给 JNA 中的结构

java - 使用 JNA 时 Mac 和 Windows 之间的 FTDI 库平台差异

java - BridJ - Pointer.pointerToAddress(long peer) 已被弃用

java - 在运行时将 Groovy 脚本导入另一个 Groovy 脚本

java - 用于生成 Java JNA 包装器的 jnaerator 的 SimpleMeaningfulExample 将不起作用

java - JNAerator - 使用由 typedef 生成的接口(interface)

java - 在 Android 上使用 BridJ 时出现 UsatisfiedLinkError

java - 如何启用 map 旋转?

java - 尝试使用 Spring 以正确的顺序销毁 bean

java - 无法找到以下类错误(最新版本)