Hace unos días hablábamos de la inyección de código y hacer hooks a las APIs. Pues bien me he puesto a jugar un poco, y esto es lo que he conseguido por ahora, una dll que se inyecta en el messenger, hace un hook en las APIs Send, WSASend y WSARecv, y cuando el messenger envía o recibe algo la dll utiliza la función OutputDebugString para mostrarnos lo que se envía o se recibe.
Es solo una prueba de concepto:
Código Delphi
[-]
library HookIt;
uses
Windows,
Sysutils,
Messages,
Psapi,
ImageHlp,
winsock;
type
IMAGE_IMPORT_DESCRIPTOR = record
Characteristics: DWORD;
TimeDateStamp: DWORD;
ForwarderChain: DWORD;
Name: DWORD;
FirstThunk: DWORD;
end;
PIMAGE_IMPORT_DESCRIPTOR = ^IMAGE_IMPORT_DESCRIPTOR;
u_int = Integer;
TSocket = u_int;
TWSABUF = record
len: u_long;
buf: PChar;
end;
PWSABUF = ^TWSABUF;
TSendFunc = function (s: TSocket; Buf: Pchar; len, flags: Integer): Integer; stdcall;
TWSASendFunc = function(s: TSocket; lpBuffers: PWSABUF; dwBufferCount: DWORD;
lpNumberOfBytesSent: Pointer; dwFlags: DWORD; lpOverlapped: Pointer;
lpCompletionRoutine: Pointer): Integer; stdcall;
TWSARecvFunc = function(s: TSocket; lpBuffers: PWSABUF; dwBufferCount: DWORD;
lpNumberOfBytesSent: Pointer; dwFlags: DWORD; lpOverlapped: Pointer;
lpCompletionRoutine: Pointer): Integer; stdcall;
const
strTarget = 'C:\Archivos de programa\MSN Messenger\msnmsgr.exe';
var
Injected: Boolean;
ModuleHandle: DWORD;
OldSend: TSendFunc;
OldWSASend: TWSASendFunc;
OldWSARecv: TWSARecvFunc;
function newSend(s: TSocket; Buf: PChar; len, flags: Integer): Integer; stdcall;
begin
OutputDebugString(PChar('Send: ' + String(Buf)));
if @OldSend <> nil then
Result:= OldSend(s,Buf,len,flags)
else
Result:= -1;
end;
function newWSASend(s: TSocket; lpBuffers: PWSABUF; dwBufferCount: DWORD;
lpNumberOfBytesSent: Pointer; dwFlags: DWORD; lpOverlapped: Pointer;
lpCompletionRoutine: Pointer): Integer; stdcall;
var
i: DWORD;
P: PWSABUF;
begin
i:= dwBufferCount;
P:= lpBuffers;
while i > 0 do
begin
OutputDebugString(PChar('Recv: ' + String(P.Buf)));
dec(i);
inc(P);
end;
if @OldWSASend <> nil then
Result:= OldWSASend(s,lpBuffers,dwBufferCount,lpNumberOfBytesSent,dwFlags,
lpOverlapped,lpCompletionRoutine)
else
Result:= -1;
end;
function newWSARecv(s: TSocket; lpBuffers: PWSABUF; dwBufferCount: DWORD;
lpNumberOfBytesSent: Pointer; dwFlags: DWORD; lpOverlapped: Pointer;
lpCompletionRoutine: Pointer): Integer; stdcall;
var
i: DWORD;
P: PWSABUF;
begin
if @OldWSARecv <> nil then
Result:= OldWSARecv(s,lpBuffers,dwBufferCount,lpNumberOfBytesSent,dwFlags,
lpOverlapped,lpCompletionRoutine)
else
Result:= -1;
i:= dwBufferCount;
P:= lpBuffers;
while i > 0 do
begin
OutputDebugString(PChar('Recv: ' + String(P.Buf)));
dec(i);
inc(P);
end;
end;
function EnablePrivilege(PrivilegeName: PChar; Enable: Boolean): Boolean;
var
hToken: THandle;
Tp: TOKEN_PRIVILEGES;
Luid: TLargeInteger;
begin
Result:= FALSE;
if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or
TOKEN_QUERY or TOKEN_READ, hToken) then
if LookupPrivilegeValue(nil,PrivilegeName,Luid) then
begin
Tp.PrivilegeCount:= 1;
Tp.Privileges[0].Luid:= Luid;
if Enable then
Tp.Privileges[0].Attributes:= SE_PRIVILEGE_ENABLED
else
Tp.Privileges[0].Attributes:= 0;
Result:= AdjustTokenPrivileges(hToken,FALSE,Tp,0,nil,PDWORD(nil)^);
CloseHandle(hToken);
end;
end;
function HookFunction(ModName, ProcName: PChar; Nuevo: Pointer): Pointer;
var
i: Integer;
hProcess: THandle;
hModules: array[0..1024] of HMODULE;
cbNeeded: DWORD;
hMod: HMODULE;
ImportDesc: PIMAGE_IMPORT_DESCRIPTOR;
Size: Cardinal;
szModName: PChar;
Thunk: PPointer;
MBI: MEMORY_BASIC_INFORMATION;
begin
Result:= nil;
hMod:= GetModuleHandle(ModName);
if hMod <> 0 then
begin
Result:= GetProcAddress(hMod, ProcName);
if Result <> nil then
begin
hProcess:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,
FALSE, GetCurrentProcessId);
if hProcess <> 0 then
begin
if EnumProcessModules(hProcess, @hModules, Sizeof(hModules), cbNeeded) then
for i:= 0 to (cbNeeded div Sizeof(HMODULE)) - 1 do
begin
ImportDesc:= ImageDirectoryEntryToData(Pointer(hModules[i]),
TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, Size);
if ImportDesc <> nil then
begin
while ImportDesc.Name > 0 do
begin
szModName:= PChar(hModules[i] + ImportDesc.Name);
if StrIComp(szModName,ModName) = 0 then
begin
Thunk:= Pointer(hModules[i] + ImportDesc.FirstThunk);
while Thunk^ <> nil do
begin
if Thunk^ = Result then
begin
OutputDebugString('Hookeado');
VirtualQuery(Thunk,MBI,Sizeof(MEMORY_BASIC_INFORMATION));
VirtualProtect(MBI.BaseAddress,MBI.RegionSize,PAGE_READWRITE,
MBI.Protect);
Thunk^:= Nuevo;
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,mbi.Protect,
MBI.Protect);
end;
inc(Thunk);
end;
end;
inc(ImportDesc);
end;
end;
end;
end;
end;
end;
end;
procedure Inject(LibPath: PChar); stdcall;
var
Procesos: array[1..1024] of DWORD;
Needed, i: DWORD;
Process, Thread: THandle;
ModName, Target: array[0..MAX_PATH] of Char;
RemLibPath: PChar;
begin
if ModuleHandle = 0 then
if EnablePrivilege('SeDebugPrivilege', TRUE) then
begin
FillChar(Target,Sizeof(Target),#0);
StrLCat(Target,strTarget,Sizeof(Target)-1);
if EnumProcesses(@Procesos, SizeOf(Procesos), Needed ) then
begin
for i:= 1 to (Needed div Sizeof(DWORD)) do
begin
Process := OpenProcess(PROCESS_ALL_ACCESS, FALSE,Procesos[i]);
if Process <> 0 then
begin
if GetModuleFileNameEx(Process,0,ModName,SizeOf(ModName)-1) > 0 then
begin
if AnsiStrPos(ModName,Target) <> nil then
begin
RemLibPath:= VirtualAllocEx(Process, nil,StrLen(LibPath)+1,
MEM_COMMIT, PAGE_READWRITE);
if RemLibPath <> nil then
begin
if WriteProcessMemory(Process, RemLibPath, LibPath,
StrLen(LibPath),PDWORD(nil)^) then
begin
Thread:= CreateRemoteThread(Process, nil, 0,
GetProcAddress(GetModuleHandle('Kernel32'),'LoadLibraryA'),
RemLibPath, 0, PDWORD(nil)^);
if Thread <> 0 then
begin
WaitForSingleObject(Thread,INFINITE );
GetExitCodeThread(Thread,ModuleHandle);
CloseHandle(Thread);
end;
end;
VirtualFreeEx(Process,RemLibPath,StrLen(LibPath)+1,MEM_RELEASE);
end;
end;
end;
CloseHandle(Process);
end;
end;
end;
EnablePrivilege('SeDebugPrivilege', FALSE);
end;
end;
procedure ProcessAttach; stdcall;
var
Process: THandle;
ModName: array[0..MAX_PATH] of Char;
Target: array[0..MAX_PATH] of Char;
begin
Injected:= FALSE;
ModuleHandle:= 0;
Process := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, FALSE,
GetCurrentProcessId);
if Process <> 0 then
begin
if GetModuleFileNameEx(Process, 0, ModName,SizeOf(ModName)-1) > 0 then
begin
FillChar(Target,Sizeof(Target),#0);
StrLCat(Target,strTarget,Sizeof(Target)-1);
if AnsiStrPos(ModName,Target) <> nil then
begin
OldSend:= HookFunction('wsock32.dll','send',@newSend);
OldSend:= HookFunction('Ws2_32.dll','send',@newSend);
OldWSASend:= HookFunction('Ws2_32.dll','WSASend',@newWSASend);
OldWSARecv:= HookFunction('Ws2_32.dll','WSARecv',@newWSARecv);
end;
end;
CloseHandle(Process);
end;
end;
procedure ProcessDetach; stdcall;
begin
end;
procedure DLLEntryPoint(Reason: integer);
begin
case Reason of
Dll_Process_Detach: ProcessDetach;
Dll_Process_Attach: ProcessAttach;
end;
end;
exports
Inject;
begin
ProcessAttach;
DLLProc:= @DLLEntryPoint;
end.
Necesitaremos una aplicación que realice la inyección, una vez inyectada la dll la aplicación se puede cerrar. Por ejemplo:
Código Delphi
[-]
program Probador;
{$APPTYPE CONSOLE}
uses Windows, SysUtils;
procedure Inject(LibPath: PChar); stdcall; external 'HookIt.dll';
var
LibPath: String;
begin
LibPath:= ExtractFilePath(ParamStr(0)) + 'HookIt.dll';
Inject(PChar(LibPath));
end.
Por último para ver los mensajes enviados por la función OutputDebugString necesitaremos un programa como
DebugView de Sysinternals.
¿Se le ocurre a alguien algo "divertido" que hacer con esto?
