I have a problem rewriting NDIS in Go

Home Forums Discussions Support I have a problem rewriting NDIS in Go

Tagged: ,

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #13903
    devman
    Participant

      Hello,

      I was trying to rewrite the NDIS API from C++ code to Go and I faced a problem which I tried to fix but because I don’t get any proper error or result I can’t tell what’s exactly wrong.

      This is part of the code that is working and showing the result correctly, at least to my udnerstanding:

      `

      import (
      “fmt”
      “strings”
      “unsafe”

      “golang.org/x/sys/windows”
      )

      type NdisApi struct {
      hFileHandle windows.Handle
      }

      func NewNdisApi() (*NdisApi, error) {
      devicePath, err := windows.UTF16PtrFromString(“\\\\.\\NDISRD”)
      if err != nil {
      return nil, err
      }

      handle, err := windows.CreateFile(
      devicePath, // Device path
      windows.GENERIC_READ|windows.GENERIC_WRITE,
      0,
      nil,
      windows.OPEN_EXISTING,
      windows.FILE_ATTRIBUTE_NORMAL,
      0)
      if err != nil {
      return nil, err
      }
      return &NdisApi{hFileHandle: handle}, nil
      }

      func (api *NdisApi) DeviceIoControl(dwService uint32, BuffIn unsafe.Pointer, SizeIn uint32, BuffOut unsafe.Pointer, SizeOut uint32, SizeRet *uint32, povlp *windows.Overlapped) error {
      var returnedBytes uint32
      if SizeRet == nil {
      SizeRet = &returnedBytes
      }

      return windows.DeviceIoControl(
      api.hFileHandle,
      dwService,
      (*byte)(BuffIn),
      SizeIn,
      (*byte)(BuffOut),
      SizeOut,
      SizeRet,
      povlp)
      }

      // GetVersion Retrieves the NDISAPI driver version.
      func (api *NdisApi) GetVersion() (*uint64, error) {
      var nDriverAPIVersion uint64

      err := api.DeviceIoControl(
      IOCTL_NDISRD_GET_VERSION,
      unsafe.Pointer(&nDriverAPIVersion),
      uint32(unsafe.Sizeof(nDriverAPIVersion)),
      unsafe.Pointer(&nDriverAPIVersion),
      uint32(unsafe.Sizeof(nDriverAPIVersion)),
      nil,
      nil,
      )

      if err != nil {
      return nil, err
      }

      return &nDriverAPIVersion, err
      }

      func (api *NdisApi) GetTcpipBoundAdaptersInfo() (*TCPAdapterList, error) {
      var tcpAdapterList TCPAdapterList

      err := api.DeviceIoControl(
      IOCTL_NDISRD_GET_TCPIP_INTERFACES,
      unsafe.Pointer(&tcpAdapterList),
      uint32(unsafe.Sizeof(tcpAdapterList)),
      unsafe.Pointer(&tcpAdapterList),
      uint32(unsafe.Sizeof(tcpAdapterList)),
      nil,
      nil,
      )

      if err != nil {
      return nil, err
      }

      return &tcpAdapterList, nil
      }
      `

      Also, I created a test to see if the results are correct:
      `

      package ndis_test

      import (
      “fmt”
      “testing”

      “github.com/lagzap/ndis-go/ndis”

      “github.com/stretchr/testify/assert”
      )

      func TestNdisApi_GetVersion(t *testing.T) {
      api, err := ndis.NewNdisApi()
      if err != nil {
      t.Fatalf(“Failed to create NdisApi: %v”, err)
      }

      version, err := api.GetVersion()
      if err != nil {
      t.Fatalf(“Failed to get version: %v”, err)
      }

      fmt.Printf(“NDIS Driver Version: %v\n”, version)

      assert.NotZero(t, version, “Version should not be zero”)
      assert.NoError(t, err, “There should be no error when getting version”)
      }

      func TestNdisApi_GetTcpipBoundAdaptersInfo(t *testing.T) {
      api, err := ndis.NewNdisApi()
      if err != nil {
      t.Fatalf(“Failed to create NdisApi: %v”, err)
      }

      adaptersInfo, err := api.GetTcpipBoundAdaptersInfo()
      if err != nil {
      t.Fatalf(“Failed to get list: %v”, err)
      }

      for i := range adaptersInfo.AdapterCount {
      nameBytes := adaptersInfo.AdapterNameList[i]
      fmt.Printf(“NDIS List: %v\n”, string(nameBytes[:]))
      }

      assert.NotZero(t, adaptersInfo.AdapterCount, “List should not be zero”)
      assert.NoError(t, err, “There should be no error when getting version”)
      }

      `
      And this is the result:
      === RUN TestNdisApi_GetVersion
      NDIS Driver Version: 0xc00009c928
      — PASS: TestNdisApi_GetVersion (0.00s)
      === RUN TestNdisApi_GetTcpipBoundAdaptersInfo
      NDIS List: \DEVICE\{ED78279B-9E2E-4C66-AEE8-793EFB217C27}
      NDIS List: \DEVICE\{FF9AC243-7969-4F09-BD77-AF6C2C6A65B2}
      NDIS List: \DEVICE\{40FF7482-C93F-4892-9A57-FDB19D966B6E}
      NDIS List: \DEVICE\{12C7B741-5C7B-4147-B9A7-A5A2E71D5E9F}
      NDIS List: \DEVICE\{A88A6FB4-497C-4464-BBA7-F48BC135DD07}
      NDIS List: \DEVICE\{9353CCF4-21B8-44BA-8E2B-CA3F156FD2B1}
      NDIS List: \DEVICE\{B404DFD0-745B-4C91-B5C7-BA78647826B8}
      NDIS List: \DEVICE\{423C1CD9-E2E9-4821-A3A4-36E3EBBA7FA2}
      NDIS List: \DEVICE\{0E2EB66C-DF42-4786-A1BF-23621E04B1A1}
      NDIS List: \DEVICE\{601E27D7-7B79-498D-B121-CF3942AA124A}
      NDIS List: \DEVICE\{8CF058FF-AF7F-418F-8F08-60D745F68CD5}
      NDIS List: \DEVICE\{0C10FCBB-4201-4B6D-848F-6675E672F9BC}
      NDIS List: \DEVICE\{1D8A9E21-80A0-404B-A563-3940C023C60F}
      NDIS List: \DEVICE\{8BE3670D-A47D-4F23-91C4-85547DE5EDAD}
      — PASS: TestNdisApi_GetTcpipBoundAdaptersInfo (0.00s)
      PASS
      ok command-line-arguments 0.015s
      `

      So, if i’m not mistaken this is communicating with the driver correctly because it’s giving me the result I want from these two. So, I went up further and tried to implement simple_packet_filter.go and network_adapter.go to able to do some real stuff

      And this is the rewritten code of InitializeNetworkInterfaces from simple_packet_filter.go:
      `
      func (f *SimplePacketFilter) initializeNetworkInterfaces() error {
      adapters, err := f.api.GetTcpipBoundAdaptersInfo()
      if err != nil {
      return err
      }

      for i := range adapters.AdapterCount {
      name := string(adapters.AdapterNameList[i][:])
      adapterHandle := adapters.AdapterHandle[i]
      currentAddress := adapters.CurrentAddress[i]
      medium := adapters.AdapterMediumList[i]
      mtu := adapters.MTU[i]

      friendlyName := ConvertWindows2000AdapterName(name)

      networkAdapter := NewNetworkAdapter(f.api, adapterHandle, currentAddress, name, friendlyName, medium, mtu)
      f.networkInterfaces = append(f.networkInterfaces, networkAdapter)
      }

      return nil
      }
      `

      and part of network_adapter.go
      `
      func NewNetworkAdapter(api *NdisApi, adapterHandle windows.Handle, macAddr [6]byte, internalName, friendlyName string, medium uint32, mtu uint16) *NetworkAdapter {
      adapter := &NetworkAdapter{
      API: api,
      HardwareAddr: macAddr,
      InternalName: internalName,
      FriendlyName: friendlyName,
      Medium: medium,
      MTU: mtu,
      CurrentMode: AdapterMode{
      AdapterHandle: adapterHandle,
      Flags: 0,
      },
      }
      event, err := windows.CreateEvent(nil, 1, 0, “”)
      if err != nil {
      fmt.Println(err.Error())
      return nil
      }
      adapter.packetEvent = event
      `

      And of course SetPacketEvent():
      `
      func (na *NetworkAdapter) SetPacketEvent() error {
      return na.API.SetPacketEvent(na.CurrentMode.AdapterHandle, na.packetEvent)
      }
      `

      and finally SetPacketEvent on ndis.go
      `
      type AdapterEvent struct {
      AdapterHandle windows.Handle
      Event windows.Handle
      }
      func (api *NdisApi) SetPacketEvent(adapter windows.Handle, win32Event windows.Handle) error {
      var ring0Event windows.Handle

      if IsWindowsNTPlatform() {
      // Windows NT platform
      ring0Event = win32Event
      } else {
      // Windows 9x/ME platform

      }

      adapterEvent := AdapterEvent{
      AdapterHandle: adapter,
      Event: ring0Event,
      }

      err := api.DeviceIoControl(
      IOCTL_NDISRD_SET_EVENT,
      unsafe.Pointer(&adapterEvent),
      uint32(unsafe.Sizeof(adapterEvent)),
      nil,
      0,
      nil, // Bytes Returned
      nil,
      )

      fmt.Printf(“DeviceIoControl failed: %v\n”, err)

      return err
      }
      `

      and I get this error only:
      DeviceIoControl failed: The parameter is incorrect.

      I’m not sure if I’m creating windows event incorrectly or what’s wrong, I even tried this instead of windows.CreateEvent():
      https://github.com/docker/dockercraft/blob/master/vendor/github.com/docker/docker/pkg/system/events_windows.go#L22

      I’ll be happy if you help me solve this so I can develop this

      #13904
      Vadim Smirnov
      Keymaster

        I would recommend paying attention to the following points:

        1. Ensure that all structures used for driver communication are properly packed.

        2. If you are using a 64-bit driver, make sure to build your code for 64-bit as well.

        3. The adapter handle must correspond to the selected network interface.

        4. The event handle should be a native Windows handle.

        5. I suggest building and using the ndisapi.dll C interface in Go instead of reimplementing all the DeviceIoControl calls directly in Go.

        #13907
        devman
        Participant

          I don’t know how I didn’t think of using C interface, thank you, I think I’m going to do that.

          #13908
          devman
          Participant

            I tried to use the C Interface and I think I have same problem and I think it’s related to Handle.
            I think I’m using wrong datatype for Handle, let’s say I’m getting AdapterMode and on I should have a AdapterHandle that I got from TCPAdapterList and for now I used uintptr datatype to store handle, I’m not sure if it’s actualy uintptr or something else. (Because windows.Handle or syscall.Handle actual datatype on go is uintptr).

            I can get AdapterList and store it into a struct which is exactly like C/C++ version but when I want to for example SetEvent or any API that wants AdapterHandle I get error: “The parameter is incorrect.”

            AdapterList (works without error):
            type TCP_AdapterList struct {
            AdapterCount uint32
            AdapterNameList [ADAPTER_LIST_SIZE][ADAPTER_NAME_SIZE]byte
            AdapterHandle [ADAPTER_LIST_SIZE]uintptr
            AdapterMediumList [ADAPTER_LIST_SIZE]uint32
            CurrentAddress [ADAPTER_LIST_SIZE][ETHER_ADDR_LENGTH]byte
            MTU [ADAPTER_LIST_SIZE]uint16
            }
            func (a *NdisApi) GetTcpipBoundAdaptersInfo() (*TCP_AdapterList, error) {
            var adapterList TCP_AdapterList
            _, _, err := a.c.GetTcpipBoundAdaptersInfo.Call(a.handle, uintptr(unsafe.Pointer(&adapterList)))

            if !errors.Is(err, windows.ERROR_SUCCESS) {
            return nil, err
            }

            return &adapterList, nil
            }

            GetAdapterMode (error: “The parameter is incorrect.”):
            type AdapterMode struct {
            AdapterHandle uintptr
            Flags uint32
            }
            func (a *NdisApi) GetAdapterMode(currentMode *AdapterMode) (error) {
            r1, r2, err := a.c.GetAdapterMode.Call(a.handle, uintptr(unsafe.Pointer(currentMode)))
            fmt.Println(r1, r2, err, a.handle)

            if !errors.Is(err, windows.ERROR_SUCCESS) {
            return err
            }

            return nil
            }

            I actually don’t know whether if I’m correct or wrong, I’ll appreciate it if you help me solve this.
            How can I make sure I’m using windows native event handle or if I got the Handle correctly, because I think thats what my problem is about?

            Note that, I’m using 64-bit.

            • This reply was modified 1 month, 3 weeks ago by devman.
            #13910
            devman
            Participant

              Could it be size of Handle difference?
              I mean if uintptr is unsigned int and if I’m on 64bit, I should use uint64 instead of uintptr?

              #13911
              devman
              Participant

                I think I managed to fix it but not with C interface, I used the initial code that I wrote purely in go, I can now ReadPackets but instead of using windows.Handle or uintptr I created an 8 bytes array (type HANDLE [8]byte) for network adapter handle and used it and it’s working

                I’m not sure what did I do wrong maybe go did some type-casting or something…

              Viewing 6 posts - 1 through 6 (of 6 total)
              • You must be logged in to reply to this topic.