Random (stack) walk Part II - Special note
Now I will dive into more detail and going to make some of the articles not available to people without some request to use them... But I will have a list of what I could unblock and make it availble on demand.
Thanks for cooperation!
As it turned out, I will have to hold this topic off for a bit longer, but I will come back to it when I get a chance.
Random (stack) walk... Pause for a moment!
So far what we achieved in the WSK investigation?
1) We provide a wsk client and register with wsk provider. This registration process could be at a DriverEntry() routine or it could be in a system thread of a subsystem. The pattern is fairly similar to the user mode WSAStartup() invokation. In the kernel socket scenario, the availability of the provider is important, so need extra caution when we try to capture the NPI interface. Imagine a kernel module that starts pretty early when provider infrastructure ( for example, NETIO, and AFD ) is not available. In such a scenario if we try to capture the provider interface it would not be wise to wait infinitely on the call because that can have an adverse effect on the system boot/load process. You can always craft something to avoid this ( home work :).
2) We have a bit better understanding about the wsk sockets and their types: Basic, Listen, Connection oriented etc.
3) We do have an idea of what subset of allowable dispatches are for what type of wsk sockets.
4) We do have an idea that NETIO is sort of a broker, and AFD is the actual provider of socket functionality.
5) Finally we also looked at some of the data structure pertient to wsk programming.
Random (stack) walk. Continues ... wsk provider
So what is a wsk provider ?
Well, at a high level, it is a kernel mode socket provider. And it is mainly a broker service in the NETIO kernel module. When a client try to capture the interface, it provides dispatchtable from the afd.sys.
Currently we can see the following relevant functions of NETIO -
0: kd> x NETIO!wsk*
86f0693c NETIO!WSKLIB_WSK_CLIENT_MODULEID = <no type information>
86f03f18 NETIO!WskRegister = <no type information>
86f03cab NETIO!WskDeregister = <no type information>
86f03ec0 NETIO!WsklibNmrCallbackDetachProvider = <no type information>
86f03df7 NETIO!WsklibNmrCallbackAttachProvider = <no type information>
86f03d73 NETIO!WskQueryProviderCharacteristics = <no type information>
86f03d26 NETIO!WskReleaseProviderNPI = <no type information>
86f03fe5 NETIO!WskCaptureProviderNPI = <no type information>
86f03ee3 NETIO!WsklibNmrCallbackCleanupProviderContext = <no type information>
And after capturing the provider interface we see the socket related calls are in afd.sys
0: kd> dt wskProviderNpi
Local var @ 0x941f0d14 Type _WSK_PROVIDER_NPI
+0x000 Client : 0x8461a210
+0x004 Dispatch : 0x8ae62dd8 _WSK_PROVIDER_DISPATCH
0: kd> dt _WSK_PROVIDER_DISPATCH 0x8ae62dd8
echosrv!_WSK_PROVIDER_DISPATCH
+0x000 Version : 0x101
+0x002 Reserved : 0
+0x004 WskSocket : 0x8ae5947e long afd!WskProAPISocket+0
+0x008 WskSocketConnect : 0x8ae5e3cc long afd!WskProAPISocketConnect+0
+0x00c WskControlClient : 0x8ae5b5da long afd!AfdWskControlClient+0
+0x010 WskGetAddressInfo : 0x8ae68a50 long afd!WskProAPIGetAddressInfo+0
+0x014 WskFreeAddressInfo : 0x8ae68602 void afd!WskProAPIFreeAddressInfo+0
+0x018 WskGetNameInfo : 0x8ae72bf8 long afd!WskProAPIGetNameInfo+0
Random (stack) walk. Continues ... sockets
Under the WSK, socket is an object with some data members and some allowable dispatch functions that can be invoked for a particular instance of a socket. There are different types of sockets:Basic, Listen, Datagram, Connection oriented.Things to note is how the different types of socket have different allowable dispatch pointers. A dispatch pointer is a function or method table that consist of a set of allowable function you can invoke from an instance of a socket. This is a bit of a departure from the native socket interface at userlevel where you can invoke pretty much any functions (socket API) on any socket, but wherther it would work or not is different question.
A pointer to a socket object is returned to a WSK application when the application creates a new socket or when the application accepts an incoming connection. A WSK application passes this pointer to all WSK functions that are specific to a particular socket. The functions are in a function table called Dispatch of the wsk socket instance. An example of an invokation of an allowed function of a socket is -
Status = socket->Dispatch->WskAccept(socket, ... );//this is for incomming connection to a listen socket.
So what is a WSK application? In this terminology, a wsk application is really a wsk client application that uses the services provided by a wsk provider. In order to use the services provided by a wsk provider two essential things needs to be done: (1) Register with WSK provider, (2) Capture provider's NPI (network provider interface).
At this point I would recommend to see the WDK document on WSK, and watch for a new feature added to the wdk compiler to include empty stucture within another structure. This is a neat way to add more dispatch functions on the top of basic dispatch functions that comes with basic type of socket ...
As I mentioned that different types of socket gets different set of dispath functions, here is one example from connection oriented socket.
1: kd> dt socketcontext
Local var @ 0x941f0d18 Type _WSKSAMPLE_SOCKET_CONTEXT*
0x84354350
+0x000 Socket : 0x8463e404 _WSK_SOCKET
+0x004 WorkQueue : 0x8ebf60c0 _WSKSAMPLE_WORK_QUEUE
+0x008 Closing : 0 ''
+0x009 Disconnecting : 0 ''
+0x00a StopListening : 0 ''
+0x00c OpContext : [2] _WSKSAMPLE_SOCKET_OP_CONTEXT
1: kd> dt _WSK_PROVIDER_CONNECTION_DISPATCH 0x84354350
echosrv!_WSK_PROVIDER_CONNECTION_DISPATCH
+0x000 WskControlSocket : 0x8463e404 long +ffffffff8463e404
+0x004 WskCloseSocket : 0x8ebf60c0 long echosrv!WskSampleWorkQueue+0
+0x008 WskBind : (null)
+0x00c WskConnect : 0x8435437c long +ffffffff8435437c
+0x010 WskGetLocalAddress : (null)
+0x014 WskGetRemoteAddress : 0x84354350 long +ffffffff84354350
+0x018 WskSend : 0x8460e130 long +ffffffff8460e130
+0x01c WskReceive : 0x84387008 long +ffffffff84387008
+0x020 WskDisconnect : 0x846d58b8 long +ffffffff846d58b8
+0x024 WskRelease : 0x00000800 long +800
Following typedefs are from wsk.h
The WSK_SOCKET structure defines a socket object for a socket.
typedef struct _WSK_SOCKET {
CONST VOID *Dispatch;
} WSK_SOCKET, *PWSK_SOCKET;
typedef struct _WSK_PROVIDER_BASIC_DISPATCH {
PFN_WSK_CONTROL_SOCKET WskControlSocket;
PFN_WSK_CLOSE_SOCKET WskCloseSocket;
} WSK_PROVIDER_BASIC_DISPATCH, *PWSK_PROVIDER_BASIC_DISPATCH;
The WSK_PROVIDER_CONNECTION_DISPATCH structure specifies the WSK subsystem's table of functions for a connection-oriented socket.
typedef struct _WSK_PROVIDER_CONNECTION_DISPATCH {
WSK_PROVIDER_BASIC_DISPATCH;
PFN_WSK_BIND WskBind;
PFN_WSK_CONNECT WskConnect;
PFN_WSK_GET_LOCAL_ADDRESS WskGetLocalAddress;
PFN_WSK_GET_REMOTE_ADDRESS WskGetRemoteAddress;
PFN_WSK_SEND WskSend;
PFN_WSK_RECEIVE WskReceive;
PFN_WSK_DISCONNECT WskDisconnect;
PFN_WSK_RELEASE_DATA_INDICATION_LIST WskRelease;
} WSK_PROVIDER_CONNECTION_DISPATCH, *PWSK_PROVIDER_CONNECTION_DISPATCH;
The WSK_PROVIDER_DATAGRAM_DISPATCH structure specifies the WSK subsystem's table of functions for a datagram socket.
typedef struct _WSK_PROVIDER_DATAGRAM_DISPATCH {
WSK_PROVIDER_BASIC_DISPATCH;
PFN_WSK_BIND WskBind;
PFN_WSK_SEND_TO WskSendTo;
PFN_WSK_RECEIVE_FROM WskReceiveFrom;
PFN_WSK_RELEASE_DATAGRAM_INDICATION_LIST WskRelease;
PFN_WSK_GET_LOCAL_ADDRESS WskGetLocalAddress;
} WSK_PROVIDER_DATAGRAM_DISPATCH, *PWSK_PROVIDER_DATAGRAM_DISPATCH;
typedef struct _WSK_PROVIDER_LISTEN_DISPATCH {
WSK_PROVIDER_BASIC_DISPATCH;
PFN_WSK_BIND WskBind;
PFN_WSK_ACCEPT WskAccept;
PFN_WSK_INSPECT_COMPLETE WskInspectComplete;
PFN_WSK_GET_LOCAL_ADDRESS WskGetLocalAddress;
} WSK_PROVIDER_LISTEN_DISPATCH, *PWSK_PROVIDER_LISTEN_DISPATCH;
Random (stack) walk. Continues ...
Few inferences ( without looking at online doc or any reference materials ) --
1) miniport register its interrupt handlers with ndis.
2) Ndis itself process interrupt at DPC level. And it dispatches interrupt to the corresponding miniport(s). So miniport does not have DIRQL. How ndis dispatches to miniports is not known yet to me. But the discussion can wait!
3) Having ms scheduler in the path makes fair bit of code processing in the tcpip. So will try to knock off scheduler later, and inject another filter instead to see what processing steps are in the new setup.
4) ???
Next stop to traverse the data payload in this scenario...
I would like to trap the data payload at the WSK level (the higher the level the better it is ). On route comes ntkrpamp, when I set bp on echosrv!WskSampleOpReceive. I don't know what is ntkrpamp, but after couple trail I get the data payload in PWSKSAMPLE_SOCKET_OP_CONTEXT->DataBuffer. Note that the socket has different state, so I made sure where to bp to track the connected state's path. Also since the requestor ( usr level client) is not under kernel debugger, you will see timeout of the client side connection and you will fail to capture the data payload... Rembember, I'm using the "Sight, Sound, and smell of Random Debugging"...
So I know at the highest level that my payload data "hello" is coming from the client. Next I will try to tear out the NDIS_BUFFER_LIST. After that I will go back to the receive path again to fill in the blanks of the "Mechanics of NDIS processing at a conceptual level".
Note that I'm after the structure and organization of this ndis processing stack ... So few questions I would llike to ask myself and find answers to them are ---
1) What is a minimal structure I need to have to create WSK client to WSK povider, ane why so ?
2) What is the ndis6.0 filtering mechanism, and what is, yet 'gain, the minimal structure to legoed with NDIS wrapper.
3) Same questions for a miniport ...