我正在尝试以某种方式使用 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 注释(Field
、Struct
、Ptr
等)和带有数据的字节数组作为输入类。给出包含输入数组数据的已解析 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;
}
}
我是一名优秀的程序员,十分优秀!