Archive for juillet 10th, 2007

Trip in da TCP/IP

Je suis de retour, plus mad que jamais, et cette fois-ci, vous allez souffrir ! En effet, vous allez découvrir le voyage effectué par un packet TCP au sein du windows. À partir de l’appel à la fonction connect, jusqu’a la dernière instruction assembleur faisant appel à la carte réseau. Évidemment si j’ai fait cela, c’était surtout pour mieux comprendre comment sont mises en places les différences couches réseau du Windows. Préparez-vous à une aventure dont vous ne reviendrez pas indemne !


L’API Winsock respecte les sockets BDS et est dans implémenté dans libraire ws2_32.dll. Cette dll se base sur différents SPI (Service Provider Interface), on a par exemple le NameSpace SPI (NSP), qui permet d’effectuer des requêtes DNS avec des fonctions comme gethostbyname et surtout le Winsock Provider (WSP) qui va send les demandes aux couches inférieures. Ces 2 providers sont fourni par mswsock.dll qui est vraiment le coeur de l’API Winsock servant à envoyer les requêtes au noyau à travers un ZwDeviceIoControlFile comme on peut le voir avec la call stack suivante :

ntdll.dll!KiFastSystemCall
ntdll.dll!ZwDeviceIoControlFile+0xc
mswsock.dll!QueryNbtWins+0x50
mswsock.dll!TryNbt+0x22
mswsock.dll!NSPLookupServiceNext+0x57e
WS2_32.dll!NSPROVIDER::NSPLookupServiceNext+0x17
WS2_32.dll!NSPROVIDERSTATE::LookupServiceNext+0x1c
WS2_32.dll!NSQUERY::LookupServiceNext+0xae
WS2_32.dll!WSALookupServiceNextW+0x78
WS2_32.dll!WSALookupServiceNextA+0x63
WS2_32.dll!getxyDataEnt+0xa1
WS2_32.dll!gethostbyname+0xb4
rezo.exe+0x2e6
kernel32.dll!BaseProcessStart+0x23

Depuis que j’ai ouvert ce blog, je fearais l’exploration de l’implémentation réseau sous Windows, après avoir passé pas mal de temps dessus, je crois qu’au final j’avais raison de pas m’y aventurer. Je voulais en fait, suivre le parcourt d’un packet, à partir du moment ou l’on appel la fonction send(), jusqu’à l’arrivé sur la carte réseau. Pour cela j’ai employé une méthode appelée le brutal tracing, l’avantage de cette technique c’est quelle est super simple à mettre en oeuvre, à l’aide de IDA et de WinDbg on regarde les différentes fonctions des lib/drv et puis on trace pour vérifier qu’on ne s’est pas trompé.

Alors première étape, se coder un petit programme de base. Un simple client qui send un « GET / HTTP/1.1″ suffira. Le but, suivre le parcourt de nos datas après l’appel avec la fonction send(). Voici le code (très simple) du client :

#include <Winsock2.h>
#include <stdio.h>    

//compil with vc6

//fuck d4 CRT sh1t
#pragma comment (linker, "/nodefaultlib:libc.lib")
#pragma comment (linker, "/entry:\"main\"")
#pragma comment (lib, "msvcrt.lib")
#pragma comment (lib, "ws2_32.lib")

ULONG main()
{
    WORD Ver;
    WSADATA Wsa;
    SOCKET Socket;
    SOCKADDR_IN Sin;
    char SendBuff[]=
    "GET / HTTP/1.1rn"
    "rn";

    Ver=MAKEWORD(2, 2);
    if(WSAStartup(Ver, &Wsa))
    {
        printf("Error with WSAStartupn");
        return 0;
    }

    Socket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(Socket==INVALID_SOCKET)
    {
        printf("Error with socket : %dn", WSAGetLastError());
        WSACleanup();
        return 0;
    }

    RtlZeroMemory(&Sin, sizeof(SOCKADDR_IN));

    // Create a sockaddr_in object and set its values.
    Sin.sin_family=AF_INET;
    Sin.sin_addr.s_addr=inet_addr("209.85.135.103"); //IP google
    Sin.sin_port=htons(80);

    if(connect(Socket, (SOCKADDR*)&Sin, sizeof(SOCKADDR_IN))==SOCKET_ERROR)
    {
        printf("Error with connect failed : %dn", WSAGetLastError());
        closesocket(Socket);
        return 0;
    }

    if(send(Socket, SendBuff, sizeof(SendBuff), 0)!=0)
    {
        printf("Error with send : %dn", WSAGetLastError());
        closesocket(Socket);
        return 0;
    }

    //recv(Socket, Buff, sizeof(Buff), 0);

    //printf("%s", Buff);

    closesocket(Socket);
    WSACleanup();
    return 0;
}

Ok, on est partit. Déja en tracant la fonction send on peut voir un enormous call :

71AB42CF    53              PUSH EBX
71AB42D0    8D4D FC         LEA ECX,DWORD PTR SS:[EBP-4]
71AB42D3    51              PUSH ECX
71AB42D4    FF75 F8         PUSH DWORD PTR SS:[EBP-8]
71AB42D7    8D4D 08         LEA ECX,DWORD PTR SS:[EBP+8]
71AB42DA    57              PUSH EDI
71AB42DB    57              PUSH EDI
71AB42DC    FF75 14         PUSH DWORD PTR SS:[EBP+14]
71AB42DF    8945 F0         MOV DWORD PTR SS:[EBP-10],EAX
71AB42E2    8B45 0C         MOV EAX,DWORD PTR SS:[EBP+C]
71AB42E5    51              PUSH ECX
71AB42E6    6A 01           PUSH 1
71AB42E8    8D4D F0         LEA ECX,DWORD PTR SS:[EBP-10]
71AB42EB    51              PUSH ECX
71AB42EC    FF75 08         PUSH DWORD PTR SS:[EBP+8]
71AB42EF    8945 F4         MOV DWORD PTR SS:[EBP-C],EAX
71AB42F2    8B46 0C         MOV EAX,DWORD PTR DS:[ESI+C]
71AB42F5    FF50 64         CALL NEAR DWORD PTR DS:[EAX+64]

Le problème c’est qu’on ne peut connaître la destination du call en dead-listing, alors on sort le Olly et on voit qu’on tombe dans la dll mswsock. De plus, il serait tout de même sympa de savoir sur quoi pointe eax, en dumpant la zone mémoire pointée on peut voir :

00146C18  02 00 00 00 00 00 A5 71 D9 B5 A6 71 98 69 A5 71  .....¥qÙµ¦q˜i¥q
00146C28  0F 7F A6 71 A1 4D A5 71 54 AE A6 71 4D 77 A6 71  ¦q¡M¥qT®¦qMw¦q
00146C38  5F 40 A5 71 D0 53 A5 71 B3 AE A6 71 29 67 A5 71  _@¥qÐS¥q³®¦q)g¥q
00146C48  EF 64 A5 71 21 B0 A6 71 31 B1 A6 71 49 3D A5 71  ïd¥q!°¦q1±¦qI=¥q
00146C58  E5 5F A5 71 D5 AD A6 71 DB 5C A5 71 90 A3 A6 71  å_¥qÕ­¦qÛ¥q£¦q
00146C68  5D 76 A5 71 42 43 A5 71 93 9A A6 71 45 31 A5 71  ]v¥qBC¥q“š¦qE1¥q
00146C78  58 2D A5 71 47 58 A5 71 71 9C A6 71 85 2E A5 71  X-¥qGX¥qqœ¦q….¥q
00146C88  BB 51 A5 71 D1 98 A6 71 15 46 A5 71 0E 94 A5 71  »Q¥qј¦qF¥q”¥q

Youpi une jolie table de fonctions. A ce moment-là je me souvenu que j’avais fait un post appelé « Winsock Reversing » et que cette table était en fait initialisé par la fonction WSPStartup qui la remplissait des diverses fonctions du Winsock Provider. Bien évidemment si on regarde le nom des fonctions de cette table, on trouve à l’offet 0×64, l’adresse de la fonction WSPSend.

kd> dds @eax l 20
001476f8  00000001
001476fc  71a50000 mswsock!_imp__GetUserNameA
 (mswsock+0x0)
00147700  71a6b5d9 mswsock!WSPAccept
00147704  71a56998 mswsock!WSPAddressToString
00147708  71a67f0f mswsock!WSPAsyncSelect
0014770c  71a54da1 mswsock!WSPBind
00147710  71a6ae54 mswsock!WSPCancelBlockingCall
00147714  71a6774d mswsock!WSPCleanup
00147718  71a5405f mswsock!WSPCloseSocket
0014771c  71a553d0 mswsock!WSPConnect
00147720  71a6aeb3 mswsock!WSPDuplicateSocket
00147724  71a56729 mswsock!WSPEnumNetworkEvents
00147728  71a564ef mswsock!WSPEventSelect
0014772c  71a6b021 mswsock!WSPGetOverlappedResult
00147730  71a6b131 mswsock!WSPGetPeerName
00147734  71a53d49 mswsock!WSPGetSockName
00147738  71a55fe5 mswsock!WSPGetSockOpt
0014773c  71a6add5 mswsock!WSPGetQOSByName
00147740  71a55cdb mswsock!WSPIoctl
00147744  71a6a390 mswsock!WSPJoinLeaf
00147748  71a5765d mswsock!WSPListen
0014774c  71a54342 mswsock!WSPRecv
00147750  71a69a93 mswsock!WSPRecvDisconnect
00147754  71a53145 mswsock!WSPRecvFrom
00147758  71a52d58 mswsock!WSPSelect
0014775c  71a55847 mswsock!WSPSend <----
00147760  71a69c71 mswsock!WSPSendDisconnect
00147764  71a52e85 mswsock!WSPSendTo
00147768  71a551bb mswsock!WSPSetSockOpt
0014776c  71a698d1 mswsock!WSPShutdown
00147770  71a54615 mswsock!WSPSocket

En fait, cette implémentation permet d’avoir des LSP (Layer Service Provider), par exemple, si vous voulez rajouter une couche SSL au dessous des Winsock et au dessus du provider TCP il suffit de crée un LSP avec la fonction WSCInstallProvider. Jonathan Levin nous expose cette méthode permettant de faire du « socket hijacking » dans une conf de la REcon 2005 « The Dark Side of Winsock » qui vous pouvez trouver ici.

En fait lors de l’initialisation,la fonction WSAStartup va parcourir dans l’ordre des dwCatalogEntryId le catalogue des providers puis sélectionner ceux qui correspondent à notre type socket et les chainer entre eux. Voici le catalogue des providers par défaut :

Winsock 32-bit Catalog:
=======================
1001 - MSAFD Irda [IrDA]
1002 - MSAFD Tcpip [TCP/IP]
1003 - MSAFD Tcpip [UDP/IP]
1004 - MSAFD Tcpip [RAW/IP]
1005 - RSVP UDP Service Provider
1006 - RSVP TCP Service Provider
1007 - MSAFD NetBIOS [DeviceNetBT_Tcpip_{F9B0E229-79AD-49F4-AFA5-789B26A7A433}] SEQPACKET 0
1008 - MSAFD NetBIOS [DeviceNetBT_Tcpip_{F9B0E229-79AD-49F4-AFA5-789B26A7A433}] DATAGRAM 0
1009 - MSAFD NetBIOS [DeviceNetBT_Tcpip_{7F588485-D8FD-4384-94F8-20934493A8AC}] SEQPACKET 1
1010 - MSAFD NetBIOS [DeviceNetBT_Tcpip_{7F588485-D8FD-4384-94F8-20934493A8AC}] DATAGRAM 1
1011 - MSAFD NetBIOS [DeviceNetBT_Tcpip_{8500C835-C738-4E3B-910B-2B41B8C628F5}] SEQPACKET 2
1012 - MSAFD NetBIOS [DeviceNetBT_Tcpip_{8500C835-C738-4E3B-910B-2B41B8C628F5}] DATAGRAM 2

Pour réaliser cette souplesse les fonctions sont donc appelées à travers une table nommée WSPPROC_TABLE, ainsi on peut remplacer les adresses des API par les notre et donc manipuler les sockets tranquillement :] Vous pouvez retrouver un exemple d’implémentation ici.

Bon tout ca c’est bien joli. Mais au final send() appel la fonction WSPSend qui va enfin faire la requête au noyau.

kd> kv
0012fd0c 71a55908 00000060 00000038 00000000 ntdll!ZwDeviceIoControlFile+0xc (FPO: [10,0,0])
0012fd98 71ab42f8 00000060 0012fdd0 00000001 mswsock!WSPSend+0x16c (FPO: [Non-Fpo])
0012fde0 00400342 00000060 0012ff94 00000013 WS2_32!send+0x82 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ffc0 7c816d4f 00310039 00310037 7ffd8000 rezo+0x342
0012fff0 00000000 00400220 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])

Le prototype de la fonction NtDeviceIoControlFile est le suivant :

NTSTATUS
  ZwDeviceIoControlFile(
    IN HANDLE  FileHandle,
    IN HANDLE  Event,
    IN PIO_APC_ROUTINE  ApcRoutine,
    IN PVOID  ApcContext,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    IN ULONG  IoControlCode,
    IN PVOID  InputBuffer,
    IN ULONG  InputBufferLength,
    OUT PVOID  OutputBuffer,
    IN ULONG  OutputBufferLength
    );

mswsock.dll!WSPSend
[...]
71A558E8    53              PUSH EBX <-OutputBufferLength
71A558E9    53              PUSH EBX <-OutputBuffer
71A558EA    6A 10           PUSH 10  <-InputBufferLength
71A558EC    8D45 B0         LEA EAX,DWORD PTR SS:[EBP-50]
71A558EF    50              PUSH EAX <-InputBuffer
71A558F0    68 1F200100     PUSH 1201F <-IoControlCode
71A558F5    FF75 D8         PUSH DWORD PTR SS:[EBP-28] <-IoStatusBloc
71A558F8    FF75 D4         PUSH DWORD PTR SS:[EBP-2C] <-ApcContext
71A558FB    FF75 DC         PUSH DWORD PTR SS:[EBP-24] <-ApcRoutine
71A558FE    56              PUSH ESI <-Event
71A558FF    FF75 08         PUSH DWORD PTR SS:[EBP+8] <-FileHandle
71A55902    FF15 E010A571   CALL NEAR DWORD PTR DS:[<&ntdll.NtDevice>; ntdll.ZwDeviceIoControlFile
[...]

Pour un send() l’IoControlCode est 0x1201F, WSPSend ne passe qu’un InputBuffer, l’OutputBufferLength étant nul, il n’y aura aucun retour. Il serait intéressant de connaitre ce qui est passé à travers l’InputBuffer. Pour cela, connaissant le prototype de WSPSend :

int
  WSPSend(
    IN SOCKET  s,
    IN LPWSABUF  lpBuffers,
    IN DWORD  dwBufferCount,
    OUT LPDWORD  lpNumberOfBytesSent,
    IN DWORD  dwFlags,
    IN LPWSAOVERLAPPED  lpOverlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE  lpCompletionRoutine,
    IN LPWSATHREADID  lpThreadId,
    OUT LPINT  lpErrno
    );

lpBuffers :
Pointer to an array of WSABUF structures. This array must remain valid for the duration of the send operation.

dwBufferCount :
Number of WSABUF structures at lpBuffers

typedef struct _WSABUF {
  u_long  len;
  char FAR  *buf;
} WSABUF, FAR * LPWSABUF;

Le paramètre lpBuffers pointe sur une liste de WSABUF, ces derniers contiennent nos datas, pour vérifier il suffit de mater la valeur du pointeur dans la stack juste avant l’appel à NtDeviceIoControlFile :

0012E980   0000005C  |Arg1 = 0000005C
0012E984   00000038  |Arg2 = 00000038
0012E988   00000000  |Arg3 = 00000000
0012E98C   00000000  |Arg4 = 00000000
0012E990   0012E9CC  |Arg5 = 0012E9CC
0012E994   0001201F  |Arg6 = 0001201F
0012E998   0012E9B4  |Arg7 = 0012E9B4 <-InputBuffer
0012E99C   00000010  |Arg8 = 00000010
0012E9A0   00000000  |Arg9 = 00000000
0012E9A4   00000000  Arg10 = 00000000

0012E9B4  8C EA 12 00 01 00 00 00 00 00 00 00 00 00 00 00  ΐ............

L’IntputBuffer pointe sur une structure de 16 bytes, le premier dword est un pointeur sur une liste de WSABUF, le dword suivant contient le nombre d’éléments de la liste. Regardons le contenu du WSABUF.

0012EA8C  13 00 00 00 74 EA 12 00                          ...tê.

0012EA74  47 45 54 20 2F 20 48 54 54 50 2F 31 2E 31 0D 0A  GET / HTTP/1.1..
0012EA84  0D 0A 00 00                                      ....

On a la taille de nos datas (0×13) et un pointeur sur celles-ci. Jusqu’ici rien d’impressionnant, maintenant on voudrait savoir à quel driver va être envoyé l’IRP. Pour cela, il regarde ce que référence le handle passé en tant que premier argument à NtDeviceIoControlFile.

kd> !handle 5C 3 288
processor number 0, process 00000288
Searching for Process with Cid == 288
PROCESS 865e22a8  SessionId: 0  Cid: 0288    Peb: 7ffdf000  ParentCid: 0234
    DirBase: 13fd7000  ObjectTable: e18fed48  HandleCount:  24.
    Image: rezo.exe

Handle table at e16e9000 with 24 Entries in use
005c: Object: 866705a0  GrantedAccess: 001f01ff (Inherit) Entry: e16e90b8
Object: 866705a0  Type: (867e9040) File
    ObjectHeader: 86670588 (old version)
        HandleCount: 1  PointerCount: 1
        Directory Object: 00000000  Name: Endpoint {Afd}

Le handle fait référence au device \Endpoint du driver Afd.sys. Si on regarde bien, on s’aperçoit que cet handle est en fait notre socket, initialisé lors de l’appel à la fonction socket(). Le driver afd (Ancillary Function Driver for WinSock) correspond, d’après ce que j’ai lu, au driver d’émulation des sockets servant à effectuer la gestion des buffers et des connexions afin de les transmettre à la couche inférieure appelé TDI (Transport Driver Interface). Regardons la table des IRP_MX_XXX, j’ajoute que cette table permet en fait aux autres composants du système de comuniquer avec le driver.

kd> !devobj 0x865df770
Device object (865df770) is for:
 Afd DriverAFD DriverObject 8661b240
Current Irp 00000000 RefCount 40 Type 00000011 Flags 00000050
Dacl e1406284 DevExt 00000000 DevObjExt 865df828
ExtensionFlags (0000000000)
Device queue is not busy.

kd> !drvobj DriverAFD 3
Driver object (8661b240) is for:
 DriverAFD
Driver Extension List: (id , addr)

Device Object list:
865df770  

DriverEntry:   baf48f40    afd!GsDriverEntry
DriverStartIo: 00000000
DriverUnload:  baf32482    afd!AfdUnload

Dispatch routines:
[00] IRP_MJ_CREATE                      baf36d40    afd!AfdDispatch
[01] IRP_MJ_CREATE_NAMED_PIPE           baf36d40    afd!AfdDispatch
[02] IRP_MJ_CLOSE                       baf36d40    afd!AfdDispatch
[03] IRP_MJ_READ                        baf36d40    afd!AfdDispatch
[04] IRP_MJ_WRITE                       baf36d40    afd!AfdDispatch
[05] IRP_MJ_QUERY_INFORMATION           baf36d40    afd!AfdDispatch
[06] IRP_MJ_SET_INFORMATION             baf36d40    afd!AfdDispatch
[07] IRP_MJ_QUERY_EA                    baf36d40    afd!AfdDispatch
[08] IRP_MJ_SET_EA                      baf36d40    afd!AfdDispatch
[09] IRP_MJ_FLUSH_BUFFERS               baf36d40    afd!AfdDispatch
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    baf36d40    afd!AfdDispatch
[0b] IRP_MJ_SET_VOLUME_INFORMATION      baf36d40    afd!AfdDispatch
[0c] IRP_MJ_DIRECTORY_CONTROL           baf36d40    afd!AfdDispatch
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         baf36d40    afd!AfdDispatch
[0e] IRP_MJ_DEVICE_CONTROL              baf36280    afd!AfdDispatchDeviceControl
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     baf36d40    afd!AfdDispatch
[10] IRP_MJ_SHUTDOWN                    baf36d40    afd!AfdDispatch
[11] IRP_MJ_LOCK_CONTROL                baf36d40    afd!AfdDispatch
[12] IRP_MJ_CLEANUP                     baf36d40    afd!AfdDispatch
[13] IRP_MJ_CREATE_MAILSLOT             baf36d40    afd!AfdDispatch
[14] IRP_MJ_QUERY_SECURITY              baf36d40    afd!AfdDispatch
[15] IRP_MJ_SET_SECURITY                baf36d40    afd!AfdDispatch
[16] IRP_MJ_POWER                       baf36d40    afd!AfdDispatch
[17] IRP_MJ_SYSTEM_CONTROL              baf36d40    afd!AfdDispatch
[18] IRP_MJ_DEVICE_CHANGE               baf36d40    afd!AfdDispatch
[19] IRP_MJ_QUERY_QUOTA                 baf36d40    afd!AfdDispatch
[1a] IRP_MJ_SET_QUOTA                   baf36d40    afd!AfdDispatch
[1b] IRP_MJ_PNP                         baf36d40    afd!AfdDispatch

Fast I/O routines:
FastIoRead                              baf321b2    afd!AfdFastIoRead
FastIoWrite                             baf32291    afd!AfdFastIoWrite
FastIoUnlockAll                         baf353d4    afd!AfdSanFastUnlockAll
FastIoDeviceControl                     baf2d880    afd!AfdFastIoDeviceControl

Ok, on remarque direct que la fonction principale est AfdDispatchDeviceControl, en effet c’est elle qui est chargée de gérer tous les IRP provenant des NtDeviceIoControlFile. Sachant que le format des paramètres des l’IRP est connu lorsque qu’il passe par une major fonction de type XxxDispatchDeviceControl, on peut retrouver facilement nos arguments. Au début, lorsque je mettais un BP sur la fonction AfdDispatchDeviceControl je n’arrivais pas à break après l’appel à WSPSend, je me suis en fait aperçu qu’il existant une fonction de dispatch fonctionnant en fast I/O appelé AfdFastIoDeviceControl. En fait les IRP peuvent être gérées de 2 manières, synchrone et asynchrone, dans le cas synchrone l’IRP est complété par le thread qui la crée, ce qui signifie que si les drivers manipulent des données userland représentées par l’IRP, elles seront accessibles durant tout le processus, typiquement lorsque qu’on utilise une méthode d’I/O de type Neither Buffered Nor Direct I/O. Dans la cas asynchrone, l’IRP peut très bien être complétée dans le contexte d’un autre thread (appartenant ou non au même process) ce qui signifie qu’un pointeur userspace n’est plus valide, il faut donc utilisé un MDL permettant d’accéder au physical pages représentant, on peut, pour cela, procéder par une méthode Buffered I/O ou bien Direct I/O.

Bon notre cas, c’est du Fast I/O, les pointeurs utilisés pour lire nos datas sont donc ceux du userspace. Il suffit de vérifier les arguments passés à AfdFastIoDeviceControl.

//
// Fast I/O device control procedure.
//

typedef
BOOLEAN
FAST_IO_DEVICE_CONTROL (
    __in struct _FILE_OBJECT *FileObject,
    __in BOOLEAN Wait,
    __in_opt PVOID InputBuffer,
    __in ULONG InputBufferLength,
    __out_opt PVOID OutputBuffer,
    __in ULONG OutputBufferLength,
    __in ULONG IoControlCode,
    __out PIO_STATUS_BLOCK IoStatus,
    __in struct _DEVICE_OBJECT *DeviceObject
    );
Breakpoint 1 hit
afd!AfdFastIoDeviceControl:
f9640880 680c010000      push    10Ch
kd> kv
ChildEBP RetAddr  Args to Child
f6300c50 8057fc67 8170d8c8 00000001 0012fd48 afd!AfdFastIoDeviceControl (FPO: [Non-Fpo])
f6300d00 8057fbfa 00000060 00000038 00000000 nt!IopXxxControlFile+0x261 (FPO: [Non-Fpo])
f6300d34 804df06b 00000060 00000038 00000000 nt!NtDeviceIoControlFile+0x2a (FPO: [Non-Fpo])
f6300d34 7c90eb94 00000060 00000038 00000000 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f6300d64)
0012fd08 7c90d8ef 71a55908 00000060 00000038 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0012fd0c 71a55908 00000060 00000038 00000000 ntdll!ZwDeviceIoControlFile+0xc (FPO: [10,0,0])
0012fd98 71ab42f8 00000060 0012fdd0 00000001 mswsock!WSPSend+0x16c (FPO: [Non-Fpo])
0012fde0 00400342 00000060 0012ff94 00000013 WS2_32!send+0x82 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ffc0 7c816d4f 00310039 00310037 7ffd4000 rezo+0x342
0012fff0 00000000 00400220 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])    

0012fd48==InputBuffer, dans le contexte du thread qui a call le NtDeviceIoControlFile

Bon pendant qu’on y est, regardons la fonction chargée de dispatcher les IRP « normales », AfdDispatchDeviceControl:

kd> uf afd!AfdDispatchDeviceControl
afd!AfdDispatchDeviceControl:
f9632280 8bff            mov     edi,edi
f9632282 55              push    ebp
f9632283 8bec            mov     ebp,esp
f9632285 8b4d0c          mov     ecx,dword ptr [ebp+0Ch] ;Irp
f9632288 8b5160          mov     edx,dword ptr [ecx+60h]
f963228b 56              push    esi
f963228c 57              push    edi
f963228d 8b7a0c          mov     edi,dword ptr [edx+0Ch] <;esi=IoControlCode
f9632290 8bc7            mov     eax,edi
f9632292 c1e802          shr     eax,2
f9632295 25ff030000      and     eax,3FFh
f963229a 83f846          cmp     eax,46h
f963229d 0f834c690000    jae     afd!AfdDispatchDeviceControl+0x3d (f9638bef)

afd!AfdDispatchDeviceControl+0x1f:
f96322a3 8bf0            mov     esi,eax
f96322a5 c1e602          shl     esi,2
f96322a8 39bea09062f9    cmp     dword ptr afd!AfdIoctlTable (f96290a0)[esi],edi
f96322ae 0f853b690000    jne     afd!AfdDispatchDeviceControl+0x3d (f9638bef)

afd!AfdDispatchDeviceControl+0x2c:
f96322b4 884201          mov     byte ptr [edx+1],al
f96322b7 8bb6b89162f9    mov     esi,dword ptr afd!AfdIrpCallDispatch (f96291b8)[esi]
f96322bd 85f6            test    esi,esi
f96322bf 0f842a690000    je      afd!AfdDispatchDeviceControl+0x3d (f9638bef)

afd!AfdDispatchDeviceControl+0x39:
f96322c5 ffd6            call    esi

afd!AfdDispatchDeviceControl+0x53:
f96322c7 5f              pop     edi
f96322c8 5e              pop     esi
f96322c9 5d              pop     ebp
f96322ca c20800          ret     8

afd!AfdDispatchDeviceControl+0x3d:
f9638bef be100000c0      mov     esi,0C0000010h
f9638bf4 897118          mov     dword ptr [ecx+18h],esi
f9638bf7 8a15119062f9    mov     dl,byte ptr [afd!AfdPriorityBoost (f9629011)]
f9638bfd ff158c8562f9    call    dword ptr [afd!_imp_IofCompleteRequest (f962858c)]
f9638c03 8bc6            mov     eax,esi
f9638c05 e9bd96ffff      jmp     afd!AfdDispatchDeviceControl+0x53 (f96322c7)

Déjà on a de la chance, la fonction est relativement simple, elle va prendre notre IoControlCode puis calculer l’indice d’une fonction dans une table appelée AfdIrpCallDispatch.

kd> dps afd!AfdIrpCallDispatch l 90
f96291b8  f962b23a afd!AfdBind
f96291bc  f962aa43 afd!AfdConnect
f96291c0  f9627bff afd!AfdDispatchImmediateIrp
f96291c4  f963d5dc afd!AfdWaitForListen
f96291c8  f963c053 afd!AfdAccept
f96291cc  f9634128 afd!AfdReceive
f96291d0  f9636833 afd!AfdReceiveDatagram
f96291d4  f9636ef2 afd!AfdSend
f96291d8  f9640e33 afd!AfdSendDatagram
f96291dc  f9632a14 afd!AfdPoll
f96291e0  f9627bff afd!AfdDispatchImmediateIrp
f96291e4  f962b0b9 afd!AfdGetAddress
f96291e8  f9627bff afd!AfdDispatchImmediateIrp
f96291ec  f9627bff afd!AfdDispatchImmediateIrp
f96291f0  f9627bff afd!AfdDispatchImmediateIrp
f96291f4  f9627bff afd!AfdDispatchImmediateIrp
f96291f8  f9627bff afd!AfdDispatchImmediateIrp
f96291fc  f9627bff afd!AfdDispatchImmediateIrp
f9629200  f9627bff afd!AfdDispatchImmediateIrp
f9629204  f9627bff afd!AfdDispatchImmediateIrp
f9629208  f9627bff afd!AfdDispatchImmediateIrp
f962920c  f9627bff afd!AfdDispatchImmediateIrp
f9629210  f9627bff afd!AfdDispatchImmediateIrp
f9629214  f9627bff afd!AfdDispatchImmediateIrp
f9629218  f9627bff afd!AfdDispatchImmediateIrp
f962921c  f9627bff afd!AfdDispatchImmediateIrp
f9629220  f9627bff afd!AfdDispatchImmediateIrp
f9629224  f9627bff afd!AfdDispatchImmediateIrp
f9629228  f9627bff afd!AfdDispatchImmediateIrp
f962922c  f9627bff afd!AfdDispatchImmediateIrp
f9629230  f9627bff afd!AfdDispatchImmediateIrp
f9629234  f9630781 afd!AfdTransmitFile
f9629238  f9636203 afd!AfdSuperAccept
f962923c  f9627bff afd!AfdDispatchImmediateIrp
f9629240  f9627bff afd!AfdDispatchImmediateIrp
f9629244  f963bb7a afd!AfdDeferAccept
f9629248  f963d5dc afd!AfdWaitForListen
f962924c  f963e563 afd!AfdSetQos
f9629250  f962e911 afd!AfdGetQos
f9629254  f962ede2 afd!AfdNoOperation
f9629258  f963f197 afd!AfdValidateGroup
f962925c  f9627bff afd!AfdDispatchImmediateIrp
f9629260  f9627bff afd!AfdDispatchImmediateIrp
f9629264  f9627f6d afd!AfdRoutingInterfaceChange
f9629268  f9627bff afd!AfdDispatchImmediateIrp
f962926c  f9635e99 afd!AfdAddressListChange
f9629270  f962dc7f afd!AfdJoinLeaf
f9629274  00000000
f9629278  f9630c88 afd!AfdTransmitPackets
f962927c  f962d8c8 afd!AfdSuperConnect
f9629280  f96280d9 afd!AfdSuperDisconnect
f9629284  f9636833 afd!AfdReceiveDatagram
f9629288  f9627bff afd!AfdDispatchImmediateIrp
f962928c  f9627bff afd!AfdDispatchImmediateIrp
f9629290  f9627bff afd!AfdDispatchImmediateIrp
f9629294  f9642e24 afd!AfdSanConnectHandler
f9629298  f9627bff afd!AfdDispatchImmediateIrp
f962929c  f9627bff afd!AfdDispatchImmediateIrp
f96292a0  f9627bff afd!AfdDispatchImmediateIrp
f96292a4  f9627bff afd!AfdDispatchImmediateIrp
f96292a8  f9627bff afd!AfdDispatchImmediateIrp
f96292ac  f9644693 afd!AfdSanAcquireContext
f96292b0  f9627bff afd!AfdDispatchImmediateIrp
f96292b4  f9627bff afd!AfdDispatchImmediateIrp
f96292b8  f9627bff afd!AfdDispatchImmediateIrp
f96292bc  f9627bff afd!AfdDispatchImmediateIrp
f96292c0  f9631af8 afd!AfdSanAddrListChange
f96292c4  f963584b afd!AfdSocketCloseNotify
f96292c8  f9627bff afd!AfdDispatchImmediateIrp
f96292cc  f962eead afd!AfdQueryFirewallSocketAddress
f96292d0  00000000
f96292d4  00000000
f96292d8  f963649b afd!AfdStartListen
f96292dc  00000000
f96292e0  00000000
f96292e4  00000000
f96292e8  00000000
f96292ec  00000000
f96292f0  00000000
f96292f4  00000000
f96292f8  f9635709 afd!AfdPartialDisconnect
f96292fc  00000000
f9629300  f9636ca4 afd!AfdQueryReceiveInformation
f9629304  f9629e62 afd!AfdQueryHandles
f9629308  f963348c afd!AfdSetInformation
f962930c  f962ecf9 afd!AfdGetRemoteAddress
f9629310  f962bb35 afd!AfdGetContext
f9629314  f962a22c afd!AfdSetContext
f9629318  f963ed8d afd!AfdSetConnectData
f962931c  f963ed8d afd!AfdSetConnectData
f9629320  f963ed8d afd!AfdSetConnectData
f9629324  f963ed8d afd!AfdSetConnectData
f9629328  f963e9ce afd!AfdGetConnectData
f962932c  f963e9ce afd!AfdGetConnectData
f9629330  f963e9ce afd!AfdGetConnectData
f9629334  f963e9ce afd!AfdGetConnectData
f9629338  f963ed8d afd!AfdSetConnectData
f962933c  f963ed8d afd!AfdSetConnectData
f9629340  f963ed8d afd!AfdSetConnectData
f9629344  f963ed8d afd!AfdSetConnectData
f9629348  f962b6cd afd!AfdGetInformation
f962934c  00000000
f9629350  00000000
f9629354  f96359c0 afd!AfdEventSelect
f9629358  f9635ad2 afd!AfdEnumNetworkEvents
f962935c  00000000
f9629360  00000000
f9629364  00000000
f9629368  00000000
f962936c  00000000
f9629370  00000000
f9629374  f963f369 afd!AfdGetUnacceptedConnectData
f9629378  f962f93d afd!AfdRoutingInterfaceQuery
f962937c  00000000
f9629380  f962bbd3 afd!AfdAddressListQuery
f9629384  00000000
f9629388  00000000
f962938c  00000000
f9629390  00000000
f9629394  00000000
f9629398  00000000
f962939c  00000000
f96293a0  f96438f6 afd!AfdSanFastCementEndpoint
f96293a4  f9643a8c afd!AfdSanFastSetEvents
f96293a8  f9643c2a afd!AfdSanFastResetEvents
f96293ac  00000000
f96293b0  f9643d7f afd!AfdSanFastCompleteAccept
f96293b4  f96443cb afd!AfdSanFastCompleteRequest
f96293b8  f9631261 afd!AfdSanFastCompleteIo
f96293bc  f9643fe1 afd!AfdSanFastRefreshEndpoint
f96293c0  f96312ed afd!AfdSanFastGetPhysicalAddr
f96293c4  00000000
f96293c8  f9631c27 afd!AfdSanFastTransferCtx
f96293cc  f96312fa afd!AfdSanFastGetServicePid
f96293d0  f963133a afd!AfdSanFastSetServiceProcess
f96293d4  f9631aab afd!AfdSanFastProviderChange
f96293d8  00000000
f96293dc  00000000
f96293e0  f962ebdd afd!AfdQueryFirewallSocketInfo
f96293e4  00000000
f96293e8  ffffffff
f96293ec  ffffffff
f96293f0  ffffffff
f96293f4  ffffffff

Je pense que les noms des fonctions sont assez parlant :]

Revenons à notre AfdFastConnectionSend, je rappel juste la call stack qui nous a mené là :

kd> bp afd!AfdFastConnectionSend
kd> g
Breakpoint 1 hit
afd!AfdFastConnectionSend:
f9633da6 8bff            mov     edi,edi

kd> kv
ChildEBP RetAddr  Args to Child
f615cb10 f962a5e5 81603ef0 f615cb9c 00000013 afd!AfdFastConnectionSend (FPO: [Non-Fpo])
f615cc50 8057fc67 815adc18 00000001 0012e9b4 afd!AfdFastIoDeviceControl+0x415 (FPO: [Non-Fpo])
f615cd00 8057fbfa 00000060 00000038 00000000 nt!IopXxxControlFile+0x261 (FPO: [Non-Fpo])
f615cd34 804df06b 00000060 00000038 00000000 nt!NtDeviceIoControlFile+0x2a (FPO: [Non-Fpo])
f615cd34 7c90eb94 00000060 00000038 00000000 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f615cd64)
0012e974 7c90d8ef 71a55908 00000060 00000038 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0012e978 71a55908 00000060 00000038 00000000 ntdll!ZwDeviceIoControlFile+0xc (FPO: [10,0,0])
0012ea04 71ab6294 00000060 0012ea8c 00000001 mswsock!WSPSend+0x16c (FPO: [Non-Fpo])
*** ERROR: Module load completed but symbols could not be loaded for rezo.exe
0012ea40 00400433 00000060 0012ea8c 00000001 WS2_32!WSASend+0x77 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ffc0 7c816d4f 00310039 00310037 7ffd7000 rezo+0x433
0012fff0 00000000 004002b0 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])

En observant le disass de AfdFastConnectionSend on peut voir que cette fonction appel le driver suivant, tcpip.sys (w00t on se rapproche de la fin). Voyons ce qu’on peut obtenir comme info sur ce dernier :

kd> !object global??tcp
Object: e1468cc0  Type: (817d2408) SymbolicLink
    ObjectHeader: e1468ca8 (old version)
    HandleCount: 0  PointerCount: 1
    Directory Object: e10012c8  Name: Tcp
    Target String is 'DeviceTcp'

kd> !devobj DeviceTcp
Device object (814e3880) is for:
 Tcp DriverTcpip DriverObject 81520688
Current Irp 00000000 RefCount 227 Type 00000012 Flags 00000050
Dacl e145b9bc DevExt 00000000 DevObjExt 814e3938
ExtensionFlags (0000000000)
Device queue is not busy.

kd> !drvobj DriverTcpip 3
Driver object (81520688) is for:
 DriverTcpip
Driver Extension List: (id , addr)

Device Object list:
814c7bd8  814e3d80  814e3880  8156a920
814cc358  

DriverEntry:   f96b7bef    tcpip!GsDriverEntry
DriverStartIo: 00000000
DriverUnload:  f96677aa    tcpip!ArpUnload

Dispatch routines:
[00] IRP_MJ_CREATE                      f9644a3e    tcpip!TCPDispatch
[01] IRP_MJ_CREATE_NAMED_PIPE           f9644a3e    tcpip!TCPDispatch
[02] IRP_MJ_CLOSE                       f9644a3e    tcpip!TCPDispatch
[03] IRP_MJ_READ                        f9644a3e    tcpip!TCPDispatch
[04] IRP_MJ_WRITE                       f9644a3e    tcpip!TCPDispatch
[05] IRP_MJ_QUERY_INFORMATION           f9644a3e    tcpip!TCPDispatch
[06] IRP_MJ_SET_INFORMATION             f9644a3e    tcpip!TCPDispatch
[07] IRP_MJ_QUERY_EA                    f9644a3e    tcpip!TCPDispatch
[08] IRP_MJ_SET_EA                      f9644a3e    tcpip!TCPDispatch
[09] IRP_MJ_FLUSH_BUFFERS               f9644a3e    tcpip!TCPDispatch
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    f9644a3e    tcpip!TCPDispatch
[0b] IRP_MJ_SET_VOLUME_INFORMATION      f9644a3e    tcpip!TCPDispatch
[0c] IRP_MJ_DIRECTORY_CONTROL           f9644a3e    tcpip!TCPDispatch
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         f9644a3e    tcpip!TCPDispatch
[0e] IRP_MJ_DEVICE_CONTROL              f9644a3e    tcpip!TCPDispatch
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     f9644cc8    tcpip!TCPDispatchInternalDeviceControl
[10] IRP_MJ_SHUTDOWN                    f9644a3e    tcpip!TCPDispatch
[11] IRP_MJ_LOCK_CONTROL                f9644a3e    tcpip!TCPDispatch
[12] IRP_MJ_CLEANUP                     f9644a3e    tcpip!TCPDispatch
[13] IRP_MJ_CREATE_MAILSLOT             f9644a3e    tcpip!TCPDispatch
[14] IRP_MJ_QUERY_SECURITY              f9644a3e    tcpip!TCPDispatch
[15] IRP_MJ_SET_SECURITY                f9644a3e    tcpip!TCPDispatch
[16] IRP_MJ_POWER                       f9644a3e    tcpip!TCPDispatch
[17] IRP_MJ_SYSTEM_CONTROL              f9644a3e    tcpip!TCPDispatch
[18] IRP_MJ_DEVICE_CHANGE               f9644a3e    tcpip!TCPDispatch
[19] IRP_MJ_QUERY_QUOTA                 f9644a3e    tcpip!TCPDispatch
[1a] IRP_MJ_SET_QUOTA                   f9644a3e    tcpip!TCPDispatch
[1b] IRP_MJ_PNP                         f9644a3e    tcpip!TCPDispatch

Ok, notre petit IRP est envoyé à la fonction TCPDispatchInternalDeviceControl qui est chargée de handler les IRP de type IRP_MJ_INTERNAL_DEVICE_CONTROL.

kd> kv
ChildEBP RetAddr  Args to Child
f9bfbaa0 804e3d77 815a0d80 815ff300 815e5738 tcpip!TCPDispatchInternalDeviceControl (FPO: [Non-Fpo])
f9bfbab0 f9633ede 00000000 00000008 f9bfbb10 nt!IopfCallDriver+0x31 (FPO: [0,0,0])
f9bfbb10 f962a5e5 81590918 f9bfbb9c 00000013 afd!AfdFastConnectionSend+0x209 (FPO: [Non-Fpo])
f9bfbc50 8057fc67 8159fbf8 00000001 0012fd48 afd!AfdFastIoDeviceControl+0x415 (FPO: [Non-Fpo])
f9bfbd00 8057fbfa 00000060 00000038 00000000 nt!IopXxxControlFile+0x261 (FPO: [Non-Fpo])
f9bfbd34 804df06b 00000060 00000038 00000000 nt!NtDeviceIoControlFile+0x2a (FPO: [Non-Fpo])
f9bfbd34 7c90eb94 00000060 00000038 00000000 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f9bfbd64)
0012fd08 7c90d8ef 71a55908 00000060 00000038 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0012fd0c 71a55908 00000060 00000038 00000000 ntdll!ZwDeviceIoControlFile+0xc (FPO: [10,0,0])
0012fd98 71ab42f8 00000060 0012fdd0 00000001 mswsock!WSPSend+0x16c (FPO: [Non-Fpo])
0012fde0 00400342 00000060 0012ff94 00000013 WS2_32!send+0x82 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ffc0 7c816d4f 00310039 00310037 7ffde000 rezo+0x342
0012fff0 00000000 00400220 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])

Si on regarde un peu l’IRP qui est passé au driver tcpip.sys, on peut voir :

kd> !irp 815ff300
Irp is active with 3 stacks 3 is current (= 0x815ff3b8)
 Mdl=815ff260: No System Buffer: Thread 00000000:  Irp stack trace.  Pending has been returned
     cmd  flg cl Device   File     Completion-Context
 [  0, 0]   0  0 00000000 00000000 00000000-00000000    

            Args: 00000000 00000000 00000000 00000000
 [  0, 0]   0  0 00000000 00000000 00000000-00000000    

            Args: 00000000 00000000 00000000 00000000
>[  f, 7]   0 e0 815a0d80 815a77b8 f9633d47-815ff228 Success Error Cancel
           DriverTcpip    afd!AfdRestartBufferSend
            Args: 00000013 00000000 00000000 00000000

Dans les arguments, la taille de nos datas et si on dump la mémoire référencé par le MDL :

kd> dt nt!_MDL 815ff260
   +0x000 Next             : (null)
   +0x004 Size             : 32
   +0x006 MdlFlags         : 4
   +0x008 Process          : (null)
   +0x00c MappedSystemVa   : 0x815ff280
   +0x010 StartVa          : 0x815ff000
   +0x014 ByteCount        : 0x13
   +0x018 ByteOffset       : 0x280
kd> db 0x815ff280
815ff280  47 45 54 20 2f 20 48 54-54 50 2f 31 2e 31 0d 0a  GET / HTTP/1.1..
815ff290  0d 0a

On retrouve toujours nos petites datas qui voyagent tranquillement dans le noyau. Pour la suite, je ne me suis pas amusé à tracer, analyser le driver tcpip (351 ko !) avec IDA et vous comprendrez ;) À partir de là, on y va full instinct et on met des BP un peu partout ! ET ça marche, il suffit de choisir les fonctions avec les bons noms. Alors arrivé dans le driver NDIS (Network Driver Interface Specification) on a cette call stack :

Breakpoint 1 hit
NDIS!ndisMSend:
f98a3d33 8bff            mov     edi,edi
kd> kv
ChildEBP RetAddr  Args to Child
f9acb788 f97b5528 8163e248 8179c310 8179c2d8 NDIS!ndisMSend (FPO: [Non-Fpo])
f9acb7c4 f988d985 8163caa8 8179c310 00000002 psched!MpSend+0x706 (FPO: [Non-Fpo])
f9acb7ec f964cd00 81699858 8179c310 815b9cc8 NDIS!ndisMSendX+0x1d6 (FPO: [Non-Fpo])
f9acb814 f964c8ce 815b9cc8 8179c310 81673160 tcpip!ARPSendData+0x198 (FPO: [Non-Fpo])
f9acb840 f964c70a 815b9cc8 f9acb800 00000001 tcpip!ARPTransmit+0x193 (FPO: [Non-Fpo])
f9acb870 f964c4ad 8159b4b0 0101a8c0 8179c310 tcpip!SendIPPacket+0x18e (FPO: [Non-Fpo])
f9acb9bc f9664427 f968a4b8 81558a28 815589c0 tcpip!IPTransmit+0x2859 (FPO: [Non-Fpo])
f9acba28 f96647b5 2ccbca7c 00000000 815fa3b8 tcpip!TCPSend+0x5d8 (FPO: [Non-Fpo])
f9acba50 f9663d97 00000001 00000000 00000000 tcpip!TdiSend+0x1cc (FPO: [Non-Fpo])
f9acba84 f9665977 815fa300 8159a3e4 815fa238 tcpip!TCPSendData+0x83 (FPO: [Non-Fpo])
f9acbaa0 804e3d77 816a9030 815fa300 81554430 tcpip!TCPDispatchInternalDeviceControl+0x51 (FPO: [Non-Fpo])
f9acbab0 f960bede 00000000 00000008 f9acbb10 nt!IopfCallDriver+0x31 (FPO: [0,0,0])
8167e130 8168f778 00000012 f9649000 00057a80 afd!AfdFastConnectionSend+0x209 (FPO: [Non-Fpo])

Remarquez que le packet a traversé la couche TDI (Transport Dispatch Interface) qu’on peut situer sur le schéma suivant :

tdi1.bmp

Et là, grand bonheur, si on utilise la commande !ndiskd.pkt sur le second argument de la fonction ndisMsend on peut voir !

kd> !pkt 8179c310 5
NDIS_PACKET at 8179c310
    MDL = 81511448
        StartVa ffffffff81511000, ByteCount 0xe, ByteOffset 0x468, NB MdlOffset 0x0
    81511468:  00 06 5b 39 5b b9 00 03 ff 6c d1 53 08 00
        MDL = 815122c4
        StartVa ffffffff81512000, ByteCount 0x14, ByteOffset 0x2e4, NB MdlOffset 0x0
    815122e4:  45 00 00 3b 01 c5 40 00 80 06 dd b6 c0 a8 01 dc
    815122f4:  d1 55 87 67
        MDL = 815589c0
        StartVa ffffffff81558000, ByteCount 0x14, ByteOffset 0xa00, NB MdlOffset 0x0
    81558a00:  04 28 00 50 2c cb ca 7c 50 74 0a eb 50 18 ff ff
    81558a10:  5e b8 00 00
        MDL = 815fa3dc
        StartVa ffffffff815fa000, ByteCount 0x13, ByteOffset 0x280, NB MdlOffset 0x0
    815fa280:  47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a
    815fa290:  0d 0a 00

0012EA74  47 45 54 20 2F 20 48 54 54 50 2F 31 2E 31 0D 0A  GET / HTTP/1.1..
0012EA84  0D 0A 00 00                                      ....

HOO le joli packet, avec toutes ses couches, ethernet, IP, TCP et les datas. Si c’est pas beau tout ça :] Mais évidemment, rien n’est fini, le packet n’a toujours pas été transmis au driver de la carte réseau. Avant cela ndisMSend va mettre dans une liste d’attente notre packet avec la fonction ndisMQueueWorkItem, les packets seront pris en compte plus tard lors de la prise en charge de la liste des DPC.

kd> kv
ChildEBP RetAddr  Args to Child
f9e67f68 f98a3a6e 81639000 81637f30 00000000 dc21x4!DC21X4Send (FPO: [Non-Fpo])
f9e67f94 f98a7f0f 00000000 81678590 81639008 NDIS!ndisMStartSends+0xd7 (FPO: [Non-Fpo])
f9e67fb8 f98a975b 10b8cbb0 00000005 ffdff000 NDIS!ndisMProcessDeferred+0x39 (FPO: [Non-Fpo])
f9e67fd0 804dc179 8163901c 81639008 00000000 NDIS!ndisMDpc+0x148 (FPO: [Non-Fpo])
f9e67ff4 804dbe2d f62f985c 00000000 00000000 nt!KiRetireDpcList+0x46 (FPO: [0,0,0])
f9e67ff8 f62f985c 00000000 00000000 00000000 nt!KiDispatchInterrupt+0x2a (FPO: [Uses EBP] [0,0,1])

Le bout de code intéressant de DC21X4Send faisant partie du driver DC21X4.sys qui est le NDIS 5.0 DC21X4 miniport driver de virtual PC est :

push    1               ; Value
push    dword ptr [esi+234h] ; Port
call    ds:__imp__WRITE_PORT_ULONG@8 ; WRITE_PORT_ULONG(x,x)

Le call à WRITE_PORT_ULONG va faire appel au hal :

in hal.dll
; ULONG __stdcall READ_PORT_ULONG(PULONG Port)
public _READ_PORT_ULONG@4
_READ_PORT_ULONG@4 proc near

Port= dword ptr  4

mov     edx, [esp+Port]
in      eax, dx
retn    4
_READ_PORT_ULONG@4 endp

Pour enfin dire au bon I/O port de la carte réseau qu’un packet est prèt à être envoyé.

Voilà, c’est fini, j’ai gagné un bon mal de crâne, mais j’ai à peu près compris comment sont agencés les différentes couches réseau sous Windows. Même si j’ai un peu bâclé la fin, j’espère que cela vous inspira pour faire mumuse avec votre OS préféré ;)

Quelques liens en vrac :

http://www.reactos.org/serendipity/index.php?/archives/9-Winsock-Architecture-Specification-WS2_32.DLL.html

http://msdn2.microsoft.com/en-us/library/ms740650.aspx

http://www.microsoft.com/msj/0599/LayeredService/LayeredService.aspx

http://mi.cnrs-orleans.fr/Security/Win2k/TCPIP/Win2k_TCPIP1.htm

http://2005.recon.cx/recon2005/papers/Jonathan_Levin/The%20Dark%20Side%20of%20Winsock.pdf

http://www.microsoft.com/msj/0599/LayeredService/LayeredService.aspx

http://en.wikipedia.org/wiki/Winsock

http://fr.wikipedia.org/wiki/Ancillary_Function_Driver

http://www.codeproject.com/system/driverdev2.asp

http://www.codeproject.com/system/driverdev5asp.asp

http://en.wikipedia.org/wiki/Transport_Dispatch_Interface

http://en.wikipedia.org/wiki/Network_Driver_Interface_Specification

http://ivanlef0u.free.fr/?p=5

9 comments juillet 10th, 2007


Calendar

juillet 2007
L Ma Me J V S D
« juin   août »
 1
2345678
9101112131415
16171819202122
23242526272829
3031  

Posts by Month

Posts by Category