i'm hooking AcceptEx. how i can retrieve remote connection ip from socket? since getpeername don't show right ip.
function GetRemoteSocketAddress ( s : TSocket ) : String;
var
Addr : TSockAddrIn;
Size: integer;
begin
Size := sizeof(Addr);
getpeername(s, Addr, Size);
Result := inet_ntoa(Addr.sin_addr);
end;
this code works fine for others APIS like Send, Connect, Recv, all return right ip but not with AcceptEx.
function AcceptExHookProc(sListenSocket, sAcceptSocket: TSocket;
lpOutputBuffer: Pointer; dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD; var lpdwBytesReceived: DWORD;
lpOverlapped: POverlapped): BOOL; stdcall;
begin
WriteLn(Format('[%s] Connection from IP (%s)', [TimeToStr(Now), GetRemoteSocketAddress(sAcceptSocket)]));
Result := TrampolineAcceptEx(sListenSocket, sAcceptSocket, lpOutputBuffer,
dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
lpOverlapped);
end;
tried sAcceptSocket or sListenSocket both show wrong ip. tried retrieve ip before result, after result, always same wrong ip.
any idea?
CodePudding user response:
You are calling getpeername()
too soon.
You are calling getpeername()
before you call the original AcceptEx()
, so there is no socket connection established yet on which to query the remote party. You can't call getpeername()
until after AcceptEx()
is successful. This is even stated as much in the AcceptEx()
documentation:
Using a single buffer improves performance. When using
AcceptEx
, theGetAcceptExSockaddrs
function must be called to parse the buffer into its three distinct parts (data, local socket address, and remote socket address). On Windows XP and later, once theAcceptEx
function completes and theSO_UPDATE_ACCEPT_CONTEXT
option is set on the accepted socket, the local address associated with the accepted socket can also be retrieved using thegetsockname
function. Likewise, the remote address associated with the accepted socket can be retrieved using thegetpeername
function.
So, you must call AcceptEx()
first, wait for it to complete successfully, then set the SO_UPDATE_ACCEPT_CONTEXT
state via setsockopt()
(which the program calling AcceptEx()
should handle, don't set that state inside your hook code, unless you are also hooking setsockopt()
itself), and only then can you use getpeerename()
on the accepted connection.
A better option is to have your AcceptExHookProc()
call the original GetAcceptExSockaddrs()
immediately after TrampolineAcceptEx()
exits (if successful) to parse the lpOutputBuffer
contents. But, that will only work if the lpOverlapped
parameter is nil
so AcceptEx()
acts synchronously.
If lpOverlapped
is not nil
, so AcceptEx()
acts asynchronously, you would have to hook GetAcceptExSockaddrs()
and log the result of the original function parsing the caller's buffer. But, there is no guarantee the program will actually call GetAcceptExSockaddrs()
after AcceptEx()
, if it doesn't want the remote party info. In which case, you would have to resort to hooking into GetOverlappedResult/Ex()
and/or GetQueuedCompletionStatus()
to receive the original lpOverlapped
operation result in order to then call GetAcceptExSockaddrs()
yourself if the operation is successful.
CodePudding user response:
Solution:
var
IP : String;
LRet, RRet : Winsock.PSockAddr;
lsize, rsize : Integer;
begin
Result := TrampolineAcceptEx(sListenSocket, sAcceptSocket, lpOutputBuffer,
dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
lpOverlapped);
lsize := 32;
rsize := 32;
Winsock.GetAcceptExSockaddrs(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, LRet, lsize, RRet, rsize);
IP := Winsock.inet_ntoa(RRet.sin_addr);
WriteLn(Format('[%s] Connection from IP (%s)', [TimeToStr(Now), IP]));