我正在尝试以不确定的方式使用BridJ:

我有一个64位应用程序。我通过调试器接口获得了一个包含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标志,但是它似乎并没有满足我的要求。
我不将本机库与BridJ一起使用,而只是使用BridJ来读取调试器API获得的字节数组。

最佳答案

我通过编写自己的代码以将带有C结构的字节数组读取到Java对象中来解决了这个问题。无论主机应用程序使用什么字长,此代码都是针对32位输入编写的。

例:

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


Bridj32类包含此实现。它以BridJ注释(FieldStructPtr等)和带有数据的字节数组作为注释的输入类。 In提供包含来自输​​入数组的数据的已解析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;
    }
}

10-04 20:53