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

时间:2018-09-09 03:16:58

标签: java packet-capture packet-sniffers winpcap jnr

如何使用jnr-ffi将以下功能映射到Java?

BOOLEAN PacketRequest(LPADAPTER AdapterObject,BOOLEAN设置,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 个答案:

答案 0 :(得分:0)

首先要进行适当的映射,您应该查看要映射的类型的定义。 PacketRequest函数返回BOOLEAN变量。根据{{​​3}},BOOLEAN被声明为typedef BYTE BOOLEAN;。这意味着,您可以在Java中使用byte类型作为函数类型。但是JNR还支持将boolean类型映射到本地byte或从本地byte PacketRequest (...)映射。所以这两个定义都是正确的:

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

接下来,您需要映射参数。同样,在这里查看定义,您将知道要使用的类型。结果将是:

set

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

最后一个参数的类型为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. }; -一个windows data type description

jnr.ffi.Struct

结构映射比本地类型要复杂一些。您不能在此处使用Java类型。相反,您应该使用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.ffi.Strunc.Unsigned8,因此对于JNR结构,它将映射到jnr.ffi.Struct.BYTE类或jnr.ffi.Struct#array(...)(几乎相同)。

要声明数组字段,应在构造时初始化数组。您需要使用Struct函数来正确执行此操作。该aslo意味着您应该知道数组的大小。该示例如上所示。

为什么要用这种方式定义它? 在初始化期间,array是某种长度可变的指针。在其中初始化的每个内部类字段都保留其自己的空间,从而增加此指针的最大大小。因此,每个字段都是某些内存片段的“视图”,并具有自己与该内存进行交互的方式(公共方法)。但是要制作一个这样的视图数组,您需要用视图实例填充空数组。这就是 private HttpClientHandler HttpClientHandler() { var handler = new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Manual, SslProtocols = SslProtocols.Tls12 }; handler.ClientCertificates.Add(new X509Certificate2("cert.crt")); //TODO: fetch from AWS. return handler; } 函数的作用。