Packet.dll 获取 MAC 地址 (JNR-FFI)

Posted

技术标签:

【中文标题】Packet.dll 获取 MAC 地址 (JNR-FFI)【英文标题】:Packet.dll get mac address (JNR-FFI) 【发布时间】:2018-09-09 03:16:57 【问题描述】:

如何用jnr-ffi将下面的函数映射到java?

BOOLEAN PacketRequest(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData);

示例(C):https://github.com/patmarion/winpcap/blob/master/WpcapSrc_4_1_3/Examples/PacketDriver/GetMacAddress/GetMacAddress.c

public interface NativeMappings 

    public static class PPACKET_OID_DATA extends Struct 

        public final UnsignedLong Oid = new UnsignedLong();
        public final UnsignedLong Length = new UnsignedLong();
        public final byte[] Data = new byte[6];

        public PPACKET_OID_DATA(Runtime runtime) 
           super(runtime);
        

    

    Pointer PacketOpenAdapter(String AdapterName);

    int PacketRequest(Pointer AdapterObject, int set, @Out PPACKET_OID_DATA OidData);

    void PacketCloseAdapter(Pointer lpAdapter);

    public static class Main 
        public static void main(String[] args) 
            NativeMappings mappings = LibraryLoader.create(NativeMappings.class).load("Packet");
            Runtime runtime = Runtime.getRuntime(mappings);
            Pointer adapterObject = mappings.PacketOpenAdapter("\\Device\\NPF_53152A2F-39F7-458E-BD58-24D17099256A");
            PPACKET_OID_DATA oid_data = new PPACKET_OID_DATA(runtime);
            oid_data.Oid.set(0x01010102L);
            oid_data.Length.set(6L);
            int status = mappings.PacketRequest(adapterObject, 0, oid_data);
            if (status == 0) 
                System.out.println("Fail.");
             else 
                System.out.println("Success.");
            
            mappings.PacketCloseAdapter(adapterObject);
        
    


【问题讨论】:

【参考方案1】:

首先要进行正确的映射,您应该查看要映射的类型的定义。 PacketRequest 函数返回 BOOLEAN 变量。根据windows data type description,BOOLEAN 被声明为typedef BYTE BOOLEAN;。这意味着,您可以使用 byte 类型作为 java 中的函数类型。但是 JNR 也支持将 boolean 类型映射到本机 byte 或从本机 byte 映射。所以两个定义都是正确的:

byte PacketRequest (...) boolean PacketRequest (...)

接下来,您需要映射参数。同样在这里,看看定义,你就会知道要使用什么类型。结果是:

boolean PacketRequest(Pointer AdapterObject, boolean set, PPACKET_OID_DATA OidData);

set 参数被错误地声明为int。此外,最后一个字段的 @Out 注释告诉 JNR 传递一个空结构而不将值复制到本机内存。因此不会传递任何预设值。

最后一个参数有PPACKET_OID_DATA类型 - 一个structure。

struct _PACKET_OID_DATA 
   ULONG Oid;                   ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h
                                ///< for a complete list of valid codes.
   ULONG Length;                ///< Length of the data field
   UCHAR Data[1];               ///< variable-lenght field that contains the information passed to or received 
                                ///< from the adapter.
; 

结构映射比原生类型要复杂一些。您不能在此处使用 java 类型。相反,您应该使用jnr.ffi.Struct 内部类来定义结构字段。此规则包括数组定义。您的结构的正确定义如下所示:

class PPACKET_OID_DATA extends Struct 

    public final UnsignedLong Oid = new UnsignedLong();
    public final UnsignedLong Length = new UnsignedLong();
    public final Unsigned8[] Data = array(new Unsigned8[6]);

    public PPACKET_OID_DATA(Runtime runtime) 
        super(runtime);
    


注意这个UCHAR 数组定义。这种类型本身定义为unsigned char,因此对于JNR 结构,它将映射到jnr.ffi.Strunc.Unsigned8 类或jnr.ffi.Struct.BYTE(几乎相同)。

要声明一个数组字段,您应该在构造时初始化数组。您需要使用 jnr.ffi.Struct#array(...) 函数来正确执行此操作。这也意味着您应该知道数组的大小。示例如上所示。

我们为什么要这样定义? 在初始化期间,Struct 是某种可变长度的指针。在其中初始化的每个内部类字段都保留自己的空间,从而增加了该指针的最大大小。因此,每个字段都是某个内存片段的“视图”,具有与该内存交互的自己的方式(公共方法)。但是要制作这样的视图数组,您需要用视图实例填充空数组。这正是array 函数的作用。

【讨论】:

可能问题出在您传递给PacketOpenAdapter 的字符串上。库需要什么编码? 你能检查adapterObject是否为NULL吗?如果是这样,您应该更改字符串编码。 LPADAPTER PacketOpenAdapter(LPTSTR AdapterName) ***.com/questions/321413/lpcstr-lpctstr-and-lptstr LPTSTR 的 jnr 本机类型是什么? 如果它为空,请尝试Pointer PacketOpenAdapter(@Encoding("ASCII") String AdapterName);Pointer PacketOpenAdapter(@Encoding("UTF-16") String AdapterName); 声明而不是提供的声明。 使用 UTF-16 时出现错误:PC=0x6c0e5166、pid=3712、tid=0x000007d0 处的 EXCEPTION_ACCESS_VIOLATION (0xc0000005)

以上是关于Packet.dll 获取 MAC 地址 (JNR-FFI)的主要内容,如果未能解决你的问题,请参考以下文章

校园网小蝴蝶运行显示:缺少packet.dll文件

JNR 采用指针参数的回调/闭包

__stdcall 与 JNR

JNR-FFI如何从指针读取结构数组

如何像 python ctypes 那样指定 JNR 指针

jnr-ffi:有没有从头文件到java代码的生成工具