Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   API de Windows (https://www.clubdelphi.com/foros/forumdisplay.php?f=7)
-   -   SetWindowsHookEx (https://www.clubdelphi.com/foros/showthread.php?t=91345)

angelp4492 09-01-2017 19:41:06

SetWindowsHookEx
 
Hola, como están.

Estoy intentado hacer un hook al mouse tengo una dll con el siguiente code.
Código Delphi [-]
library hook;


uses
  Windows,
  Messages,
  sysutils,
  dialogs;
const
      WH_MOUSE_LL = 14;
      var
      HookHandle: Cardinal;
   

 
{$R *.res}

function StopMouseHook: Boolean;stdcall;
begin
  UnhookWindowsHookEx(HookHandle);
  if HookHandle = 0 then
    Result := False
  else
   Result := True;
end;

function LowLevelMouseProc(nCode: Integer; wParam: wParam; lParam: lParam): LRESULT; stdcall;
Type
  tagMSLLHOOKSTRUCT = record
    POINT: TPoint;
    mouseData: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: DWORD;
  end;
  TMSLLHOOKSTRUCT = tagMSLLHOOKSTRUCT;
  PMSLLHOOKSTRUCT = ^TMSLLHOOKSTRUCT;
var
 Delta:Smallint;
 posx:Integer;
 posy:integer;
begin
   
  if (nCode >= 0) then
  begin
   
    if wParam = WM_LButtonDOWN then
    begin
           
           //hacer lo que queramos al detectar.
            showmessage('Me has pulsado');
    end;
  end;
  Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
end;

function startMouseHook: Boolean;stdcall;
 var
   msg:TMsg;
begin
  Result := false;
  if HookHandle = 0 then
  begin
     HookHandle := SetWindowsHookExW(WH_MOUSE_LL, @LowLevelMouseProc, HInstance, 0);
     Result := HookHandle <> 0;
  end;
end;

procedure DLLEntryPoint(dwReason:DWORD);
begin
  case dwReason of
    DLL_PROCESS_ATTACH:
    begin

      startMouseHook;

     
    end;
    DLL_PROCESS_DETACH:
    begin

       StopMouseHook;
      
    end;

    end;
end;



begin
   DllProc:=@DLLEntryPoint;
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.

Esta dll si la cargo normal funciona bien, pero por el contrario si la inyecto en un proceso no me funciona. La inyección se realiza por que lo he comprobado por medio de un messagebox, he leido que SetwindowsHookex no funciona desde una dll inyectada. alguien a tenido el mismo problema.

escafandra 10-01-2017 14:11:17

Si se puede inyectar la dll y funcionará. El hook sólo funciona en el contexto del thread que hace la llamada a SetWindowsHookEx. El problema es que si inyectas con CreateRemoteThread para provocar un LoarLibrary en un proceso remoto, el thread creado termina tras la inyección y por eso dejará de funcionar el hook al mouse.

La solución es crear un nuevo Thread que se mantenga en el proceso inyectado y listo:
Código Delphi [-]
library hook;


uses
  Windows,
  Messages,
  sysutils,
  dialogs;

const
  WH_MOUSE_LL = 14;

var
  HookHandle: Cardinal;
  TID: DWORD;

{$R *.res}

function StopMouseHook: Boolean;stdcall;
begin
  UnhookWindowsHookEx(HookHandle);
  if HookHandle = 0 then
    Result := False
  else
   Result := True;
end;

function LowLevelMouseProc(nCode: Integer; wParam: wParam; lParam: lParam): LRESULT; stdcall;
Type
  tagMSLLHOOKSTRUCT = record
    POINT: TPoint;
    mouseData: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: DWORD;
  end;
  TMSLLHOOKSTRUCT = tagMSLLHOOKSTRUCT;
  PMSLLHOOKSTRUCT = ^TMSLLHOOKSTRUCT;
var
 Delta:Smallint;
 posx:Integer;
 posy:integer;
begin
  if (nCode >= 0) then
  begin
    if wParam = WM_LButtonDOWN then
    begin
      //hacer lo que queramos al detectar.
      //showmessage('Me has pulsado');
      Windows.Beep(1000, 100);
    end;
  end;
  Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
end;

function startMouseHook: Boolean;stdcall;
 var
   msg:TMsg;
begin
  Result := false;
  if HookHandle = 0 then
  begin
     HookHandle := SetWindowsHookEx(WH_MOUSE_LL, @LowLevelMouseProc, Hinstance, 0);
     Result := HookHandle <> 0;
  end;
end;

procedure ThreadHook;
var
  msg: TMsg;
begin
  startMouseHook;
  repeat
    PeekMessage(msg,0,0,0, PM_REMOVE);
  until (msg.message = WM_QUIT);
  StopMouseHook;
end;

procedure DLLEntryPoint(dwReason: integer);
begin
  case dwReason of
    DLL_PROCESS_ATTACH:
    begin
      CloseHandle(CreateThread(nil, 0, @ThreadHook, nil, 0, TID));
    end;
    DLL_PROCESS_DETACH:
    begin
      PostThreadMessage(TID, WM_QUIT, 0, 0);
    end;
  end;
end;

begin
  DllProc:=@DLLEntryPoint;
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.

Este problema no aparece si realizas la técnica de inyección por suspensión de thread, más compleja y que requiere inyección directa.
Ten en cuenta que si tu S.O. es de 64bits, no puedes inyectar una dll 32bits en procesos 64bits. Para eso debes compilar tu dll y tu inyector a 64bits.


Saludos.

angelp4492 10-01-2017 14:32:36

Gracias escafandra, lo pruebo en cuanto llegue a casa. Precisamente estoy siguiendo un tutorial tuyo sobre Api_hook espectacular por cierto.

la técnica que utizo para la inyección es como tu bien dices CreateremoteThread (seguro que alguna función publicada por tí), voy a buscar la técnica que me indicas de suspender el thread.

Tengo un poco de lio con la inyección en 64 bit y de 32 bit.

a ver yo tengo un pc con windows 7 de 64 bits y hay procesos que corren en 32 bits (firefox.exe *32) por ejemplo y otros en 64 bits.

con esta inyección me funciona en un pc con windows 7 32 bit, pero no me funciona en windows 7 64 bits en proceso de 32 bits. (ni compilando la dll y el injector en el pc de 64 bits, CreateremoteThread me devuelve 0 y sale).

Código Delphi [-]
function InjectLib(ProcessID: Integer): Boolean;
var
  Process: HWND;
  ThreadRtn: FARPROC;
  DllPath: AnsiString;
  RemoteDll: Pointer;
  BytesWriten: DWORD;
  Thread: DWORD;
  ThreadId: DWORD;

  aux:string;
begin
  Result := False;
  Process := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE,
                         True,
                         ProcessID);
  if Process = 0 then Exit; 
  try

   
    DllPath := AnsiString(ExtractFilePath(ParamStr(0)) + DLLName) + #0;
   

    RemoteDll := VirtualAllocEx(Process,
                                nil,
                                Length(DllPath),
                                MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
    if RemoteDll = nil then Exit;
    try
     
      if not WriteProcessMemory(Process,
                                RemoteDll,
                                PChar(DllPath),
                                Length(DllPath),
                                BytesWriten)      then Exit;
      if BytesWriten <> DWORD(Length(DllPath)) then Exit;
      
      ThreadRtn := GetProcAddress(GetModuleHandle('Kernel32.dll'),
                                                  'LoadLibraryA');
      
      if ThreadRtn = nil then Exit;
    
      Thread := CreateRemoteThread(Process,
                                   nil,
                                   0,
                                   ThreadRtn,
                                   RemoteDll,
                                   0,
                                   ThreadId);
      if Thread = 0 then Exit;
      try
       
        Result := WaitForSingleObject(Thread,
                                      INFINITE) = WAIT_OBJECT_0;
      finally
        CloseHandle(Thread);
      end;
    finally
      VirtualFreeEx(Process,
                    RemoteDll,
                    0,
                    MEM_RELEASE);
    end;
  finally
    CloseHandle(Process);
  end;
end;

function GetPID(ExeFileName: string): dword;
var
  ContinueLoop, ffound: boolean;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
  UpEFN: string;
begin
  result := 0;
  ffound := false;
  UpEFN := UpperCase(ExeFileName);
  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
  while integer(ContinueLoop) <> 0 do
  begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) = UpEFN) or
        (UpperCase(FProcessEntry32.szExeFile) = UpEFN)) then
    begin
      result := FProcessEntry32.th32ProcessID;
      ffound := true;
      break;
    end;
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
  if not ffound then
    ShowMessage(ExeFileName+'is not runing');
end;



var
  PID: dword;
begin

  PID := GetPID('notepad.exe');
 
  if (PID > 0) then

  if InjectLib(PID) = True then
       Application.MessageBox('Inyección OK', 'Application.Title', MB_OK +
       MB_ICONINFORMATION);

Casimiro Notevi 10-01-2017 14:40:07

Cita:

Empezado por angelp4492 (Mensaje 512271)
con esta inyección me funciona en un pc con windows 7 32 bit, pero no me funciona en windows 7 64 bits en proceso de 32 bits.

Seguramente tendrás que crearla compilando 64 bits, con las versiones nuevas de delphi, o con lazarus.

escafandra 10-01-2017 14:41:13

Reglas básicas:

1. No puedes inyectar código 32 bits en un proceso de 64bits
2. No puedes inyectar código 64bits en un proceso de 32bits.
3. Si puedes inyectar 32->32 y 64->64
4. Para inyectar en 32bits necesitas un inyector compilado a 32bits
5. Para inyectar 64bits necesitas un inyector compilado a 64bits.
6. Las dos últimas reglas pueden solucionarse teóricamente con técnicas complejas.

Saludos.

escafandra 10-01-2017 14:45:18

Código Delphi [-]
function InjectCRT(Pid: integer; dll: PCHAR): BOOL;
var
   hThread:  THANDLE;
   ExitCode: DWORD;
   hLib:     Pointer;//LPTHREAD_START_ROUTINE;
   hProc:    THANDLE;
   Buffer:   Pointer;
begin
   Result:= false;
   if(dll^ = #0) then exit;
 
   hThread:= 0;
   ExitCode:= 0;
   hProc:= OpenProcess(PROCESS_ALL_ACCESS, false, Pid);
   if hProc<>0 then
   begin
     Buffer:= VirtualAllocEx(hProc, nil, lstrlen(dll)+1, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
     if Buffer<>nil then
     begin
       if WriteProcessMemory(hProc, Buffer, dll, lstrlen(dll)+1, PDWORD(0)^) then
       begin
         hLib:= GetProcAddress(GetModuleHandle('Kernel32.dll'), 'LoadLibraryA');
         if hLib <> nil then
         begin
           hThread:= CreateRemoteThread(hProc, nil, 0, hLib, Buffer, 0, PDWORD(0)^);
           if hThread <> 0 then
           begin
             // libero la memoria localizada...
             WaitForSingleObject(hThread, INFINITE);  // Espero a que se cree y termine el Thread
             GetExitCodeThread(hThread, ExitCode);    // Extraigo el código de terminación del Thread
             CloseHandle(hThread);                    // Cierro el Handle hThread
           end;
         end;
       end;
       VirtualFreeEx(hProc, Buffer, 0, MEM_RELEASE);  // Libero memoria
     end;
     CloseHandle(hProc);                       // Cierro el Handle hProc
   end;
   Result:= (ExitCode <> 0);
end;


Saludos.

escafandra 10-01-2017 15:06:19

Cita:

Empezado por angelp4492 (Mensaje 512271)
Gracias escafandra, lo pruebo en cuanto llegue a casa. Precisamente estoy siguiendo un tutorial tuyo sobre Api_hook espectacular por cierto.

la técnica que utizo para la inyección es como tu bien dices CreateremoteThread (seguro que alguna función publicada por tí), voy a buscar la técnica que me indicas de suspender el thread.

Aquí tienes dos tutoriales sobre la inyección directa de código, recuerda que son vigentes las 6 normas básicas:
Inyección directa de código en C y en delphi
Inyección directa de código II


Saludos.

angelp4492 10-01-2017 16:40:56

Funciono Perfecto. Gracias escafandra, voy a revisar los enlaces que me has puesto.

angelp4492 11-01-2017 20:01:30

Hola me surgió una nueva duda. ahora que todo va bien con la inyección y el hook al mouse, intento capturar el titulo de la ventana activa al pulsar un boton pero no me sale. Lo he intentado con GetwindowsText pero en la MSDN dice que si estamos en otro proceso utilizar SendMessage con WM_GETTEXT.
este es el codigo en condiciones normales si funciona.

Código Delphi [-]
function GetWindowText: WideString;
var
  TextLength: Integer;
  Text: PWideChar;
  wnd: HWND;
begin
  Result := '';
  if wnd = 0 then
    Exit;
    wnd:=GetActiveWindow;;
  TextLength := SendMessageW(wnd, WM_GETTEXTLENGTH, 0, 0);
  if TextLength <> 0 then
  begin
    GetMem(Text, TextLength * 2 + 1);
    SendMessageW(wnd, WM_GETTEXT, TextLength + 1, Integer(Text));
    Result := Text;
    FreeMem(Text);
  end;
end;

que estoy haciendo mal, donde se me ha perdido el handle:confused:

escafandra 11-01-2017 20:16:57

Código Delphi [-]
function GetWindowText: WideString;
var
  TextLength: Integer;
  Text: PWideChar;
  Wnd: HWND;
begin
  Wnd:= GetActiveWindow;
  TextLength:= SendMessageW(Wnd, WM_GETTEXTLENGTH, 0, 0);
  if TextLength <> 0 then
  begin
    GetMem(Text, TextLength * 2 + 2);
    SendMessageW(Wnd, WM_GETTEXT, WPARAM(TextLength*2 + 2), LPARAM(Text));
    Result := Text;
    FreeMem(Text);
  end;
end;


Saludos.

angelp4492 11-01-2017 20:20:57

Gracias, escafandra casi lo tenia:)


La franja horaria es GMT +2. Ahora son las 00:10:53.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi