winsock example code

c++ / delphi package - dll injection and api hooking
Post Reply
mutantc0der
Posts: 11
Joined: Thu Jun 17, 2004 1:48 pm

winsock example code

Post by mutantc0der »

hi

i'v been working on a winsock demo project (orginally made by Aphex, but heavly modified) but i some things still goes wrong.. I attempt to intercept send/recv data and log it back to the base application using IPC. The problem is that IE/MSN act very unstable on this code. IE crashes after ~30 page requests, I suspect that a few bytes are lost in the send/recv events but im unable to find any thing wrong. The reason i think this is because MSN traffic is shown perfectly in the base logapp but it still says that there is a problem while connecting. Can somebody please look at it, so when its fixed u can give this thread url as refrence for a winsock demo .. (i'v seen the request for this alot of times). note: this code also tries to log the remote ip of the socket, i commented that code first, but had no effect.

btw, sorry for the sloppy coding, but just hacking on it for a few hours and the last thing on my mind was to clean up the code a bit :wink:

thanks for all the work madshi!

Code: Select all

library Project1;

uses
  Windows,
  Winsock,
  SysUtils,
  madRemote,
  madCodeHook;

{$R *.RES}


var
  connectNextHook: function (s: TSocket; var name: TSockAddr; namelen: Integer): Integer; stdcall;
  sendNextHook: function(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
  recvNextHook: function(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
  DataSocket: TSocket;
  LastDataSocket : Tsocket;

var
  strAppExe: string;
  strSockIP : String;

  function GetModuleFileName: string;
var
  pid : dword;
  Buffer: array[0..MAX_PATH] of Char;
begin
{  SetString(Result, Buffer, Windows.GetModuleFileName(HInstance,
    Buffer, SizeOf(Buffer)));}
ProcessIdToFilename(ProcessHandleToId (GetCurrentProcess()),Buffer);
Result := StrPas(Buffer);

end;



function GetPeerAddr: string;
var
    saddr    : TSockAddrIn;
    saddrlen : integer;
    szAddr   : PChar;
begin
    Result := 'error';

        if GetPeerName(DataSocket, TSockAddr(saddr), saddrlen) = 0 then begin
            szAddr := Inet_ntoa(saddr.sin_addr);
            Result := StrPas(szAddr);
        end;
end;

procedure CheckIP();
begin
        if (LastDataSocket = 0) OR ( DataSocket <> LastDataSocket) then
        begin
        LastDataSocket := DataSocket;
        strSockIP := GetPeerAddr();
        end
end;

procedure IPCLog(strWhat : String);
var
Result : Integer;
begin
strWhat := '[' + strAppExe + ' (' + strSockIP + ')] ' + strWhat;
SendIpcMessage('whooklib', pchar(strWhat), Length(strWhat), @result, sizeOf(result));

end;


function ConvertDataToAscii(Buffer: pointer; Length: Word): string;
var
  Iterator: integer;
  AsciiBuffer: string;
begin
  AsciiBuffer := '';
  for Iterator := 0 to Length - 1 do
  begin
    if char(pointer(integer(Buffer) + Iterator)^) in [#32..#127] then
      AsciiBuffer := AsciiBuffer + ' ' + char(pointer(integer(Buffer) + Iterator)^) + ' '
    else
      AsciiBuffer := AsciiBuffer + ' . ';
  end;
  Result := AsciiBuffer;
end;

function ConvertDataToHex(Buffer: pointer; Length: Word): string;
var
  Iterator: integer;
  HexBuffer: string;
begin
  HexBuffer := '';
  for Iterator := 0 to Length - 1 do
  begin
    HexBuffer := HexBuffer + IntToHex(Ord(char(pointer(integer(Buffer) + Iterator)^)), 2) + ' ';
  end;
  Result := HexBuffer;
end;

function connectHookProc (s: TSocket; var name: TSockAddr; namelen: Integer): Integer; stdcall;
begin
Result := connectNextHook(s,name,namelen);
        if Result = 0 then
        begin
        DataSocket := s;
        CheckIP();
        end;
end;

function recvHookProc(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
var
  AsciiBuffer: string;
  HexBuffer: string;
  DataBuffer: pchar;
begin


  //call the real winsock function
  Result := recvNextHook(s, Buf, len, flags);

   //allocate memory for our copy of the data
  GetMem(DataBuffer, Result);
  try
    //get our copy of the data
    CopyMemory(DataBuffer, @Buf, Result);

  IPCLog( 'recv: ' + DataBuffer);
  finally
    FreeMem(DataBuffer);
  end;

end;

function sendHookProc(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
var
  AsciiBuffer: string;
  HexBuffer: string;
  DataBuffer: pchar;
begin
{ DataSocket := s;
 CheckIP();}

  //allocate memory for our copy of the data
  GetMem(DataBuffer, len);
  try
    //get our copy of the data
    CopyMemory(DataBuffer, @Buf, len);
   IPCLog( 'send: ' + DataBuffer);
  finally
    FreeMem(DataBuffer);
  end;

    //call the real winsock function
  Result := sendNextHook(s, Buf, len, flags);

end;


begin
strAppExe := GetModuleFileName();
strSockIP := 'undefined';
DataSocket := 0;
LastDataSocket := 0;
hookapi('ws2_32.dll','send', @sendHookProc, @sendNextHook);
hookapi('wsock32.dll','send', @sendHookProc, @sendNextHook);
hookapi('ws2_32.dll','recv', @recvHookProc, @recvNextHook);
hookapi('wsock32.dll','recv', @recvHookProc, @recvNextHook);

hookapi('ws2_32.dll','connect', @connectHookProc, @connectNextHook);
hookapi('wsock32.dll','connect', @connectHookProc, @connectNextHook);

end.

for the interested ppl, the base log app:

Code: Select all


unit injecter;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, madCodeHook,
  StdCtrls;

type
  // this is how you get notified about incoming ipc messages
  // you have to write a function which fits to this type definition
  // and then you give it into "CreateIpcQueue"
  TIpcCallback = procedure (name       : pchar;
                            messageBuf : pointer;
                            messageLen : dword;
                            answerBuf  : pointer;
                            answerLen  : dword); stdcall;



type
  TForm1 = class(TForm)
    log: TMemo;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
{procedure RecvIPCmsg(name: pchar; messageBuf: pointer;
  messageLen: dword; answerBuf: pointer; answerLen: dword);stdcall;}
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure RecvIPCmsg(name: pchar; messageBuf: pointer;
  messageLen: dword; answerBuf: pointer; answerLen: dword);stdcall;
begin
//wassap
Form1.log.Lines.Add(StrPas(messageBuf));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
CreateIpcQueue('whooklib', RecvIPCmsg);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyIpcQueue('whooklib');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
log.Lines.Add('injecting');
InjectLibrary(ALL_SESSIONS or SYSTEM_PROCESSES, 'Project1.dll');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
log.Lines.Add('uninjecting');
UnInjectLibrary(ALL_SESSIONS or SYSTEM_PROCESSES, 'Project1.dll');
end;

end.
madshi
Site Admin
Posts: 10764
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

I've not the time to check your code out in detail. Just two comments:

(1) When calling GetPeerName I think you need to initialize "saddrlen".

(2) Storing sockets in global variables is not really good for thread stuff. What happens if IE calls winsock functions at the "same" time from different threads?

I suggest that you comment out your code piece by piece until the hook dll is stable. This way you should be able to locate the guilty code.
nildo
Posts: 249
Joined: Mon Mar 22, 2004 11:32 am
Contact:

Post by nildo »

just a Tip:
Do not call GetPeerAddr at the Recv hook because I've experienced with this error and was not able to fix....
mutantc0der
Posts: 11
Joined: Thu Jun 17, 2004 1:48 pm

Post by mutantc0der »

ok, i got it working with another piece of code


now my question is, when do i get the remote IP of the connected socket ? i can retrieve it successfully with the following code BUT the socket gets disconnected after the function completes :sceptic: :

Code: Select all

....
function GetPeerAddr(DataSocket : TSocket): string;
var
    saddr    : TSockAddrIn;
    saddrlen : integer;
    szAddr   : PChar;
begin
    Result := 'error';
    saddrlen := sizeof(TSockAddrIn);

        if GetPeerName(DataSocket, TSockAddr(saddr), saddrlen) = 0 then
        begin
            szAddr := Inet_ntoa(saddr.sin_addr);
            Result := StrPas(szAddr);
        end;
end;
....

function connectHookProc (s: TSocket; var name: TSockAddr; namelen: Integer): Integer; stdcall;
begin
Result := connectNextHook(s,name,namelen);
        if Result = -1 then
        begin
        LogWithIPC('connect() to ' + GetPeerAddr(s));
        //socket is now disconnected here  
        end;

LogWithIPC('connect() retcode : ' + IntToStr(Result));
end;

.........



nildo, u said u couldnt retrieve it in recv() , did you already find another suitable event to retrieve the remote ip/port and more info ??

thanks
nildo
Posts: 249
Joined: Mon Mar 22, 2004 11:32 am
Contact:

Post by nildo »

mutantc0der wrote:nildo, u said u couldnt retrieve it in recv() , did you already find another suitable event to retrieve the remote ip/port and more info ??

thanks
No. What I did on my program was a Timer that keeps checking for those SocketIDs from my target application. To retreve the information you need to OpenProcess then DuplicateHandle on the SocketID. But I am trying a new way. Do you know NETSTAT? I'am adapting some code to my Delphi applacition that can read exatly what Netstat reads. So I will search for the SocketID on that list and then display the Remote and Local IP and port.
mutantc0der
Posts: 11
Joined: Thu Jun 17, 2004 1:48 pm

Post by mutantc0der »

do you want to pipe stdout from netstat.exe and read the information ? which parameters allows you to print the socket handles ? or are u using API's to retrieve a list

can you explain why we cannot retrieve the remote peer ip? i mean, we just intercept the recv() call, we directly call the real recv() , and then we perform some functions on the socket . the recv() is blocking for the app anyway, so why cant we let it wait and do our own things and then return from the call. very weird
mic
Posts: 11
Joined: Mon May 31, 2004 12:04 pm

Post by mic »

mutant, that code looks nice :P

what i was wondering was how would u send data using it?
nildo
Posts: 249
Joined: Mon Mar 22, 2004 11:32 am
Contact:

Post by nildo »

mutantc0der, I was wrong, theres no way to retrieve sockets handle from netstat... :cry:
mutantc0der
Posts: 11
Joined: Thu Jun 17, 2004 1:48 pm

Post by mutantc0der »

too bad nildo :( !!!

mic, thanx , but what do you mean with 'how do you send data?' , the DLL will process every send() call from the injected process, is that what you mean ?
mic
Posts: 11
Joined: Mon May 31, 2004 12:04 pm

Post by mic »

i was wondering how u would send like a byte array like from the loader

do u just call the dll send function from the loader and if so how i am crass at delphi
mutantc0der
Posts: 11
Joined: Thu Jun 17, 2004 1:48 pm

Post by mutantc0der »

you'll need to use sendNextHook() if you want to send data in the DLL yourself, if i'v understood your question correctly
iridium
Posts: 9
Joined: Thu Aug 19, 2004 10:39 am

Post by iridium »

mutantc0der wrote:ok, i got it working with another piece of code
mutant, would you mind sending me the "new" code? I'm working on a similar project.

Thanks a lot!
jlist
Posts: 9
Joined: Sun Aug 29, 2004 12:48 am

Post by jlist »

It's an interesting topic.
I tried mutantc0der's code. Issues are:

- not getting connects. (mutantc0der mentioned this) Actually, I was surprised that recv and send worked. My impression is that MS will use WinSock2 functions as opposed to BSD sockets. And BSD sockets will be translated to WinSock2 calls. If IE calls WinSock2 functions directly, there would be no chance that recv, send or connect got called. I'm quite confused. I suppose hooking winsock2 functions could be the solution to connect?

- there are often garbage characters shown in the log. What are they? Below is the beginning part of a session accessing whatismyip.com

- sometimes, IE just can not access internet. Need to uninject, close the host app, start it again.
injecting
[IEXPLORE.EXE (undefined)] recv:
[IEXPLORE.EXE (undefined)] recv: ¤´E
[IEXPLORE.EXE (undefined)] recv: À´ v:R
[IEXPLORE.EXE (undefined)] recv: À´
[IEXPLORE.EXE (undefined)] send: GET / HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: www.whatismyip.com
Connection: Keep-Alive

ibŽ
[IEXPLORE.EXE (undefined)] recv: ¤´ü
[IEXPLORE.EXE (undefined)] send: !´
[IEXPLORE.EXE (undefined)] recv: !´
[IEXPLORE.EXE (undefined)] recv: HTTP/1.1 200 OK
Date: Sun, 29 Aug 2004 00:54:35 GMT
Server: Microsoft-IIS/6.0
...
uninjecting
zamolx32
Posts: 31
Joined: Thu Sep 16, 2004 7:58 am

What trick have solved your problem

Post by zamolx32 »

I'm having the same problems with this sample. :cry:

After a random period of time, iexplorer (or myie in my case) is crashing.
I have no ideea what could cause this problem.

mutantc0der, could you please share with us the trick you have used to solve this problem.

Thanks,
nildo
Posts: 249
Joined: Mon Mar 22, 2004 11:32 am
Contact:

Post by nildo »

I had no problem with my hook. I had not used MutantCoder's example but works perfectly. Some things that I've learned when doing my hook: Do not use GetPeerName and others like it, inside the callback of Send, Recv, WSASend and WSARec. Other things: You need to know what is making your IE to crash, so you do this: First of all, hook only the SEND without anything else. If works well then you hook Send and Recv. Then you keep going till you find where the error is... :D
Post Reply