我正在尝试以不确定的方式使用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注释(Field
,Struct
,Ptr
等)和带有数据的字节数组作为注释的输入类。 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;
}
}