Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Buscando texto en la memoria de un programa. (https://www.clubdelphi.com/foros/showthread.php?t=88921)

deliriun 27-08-2015 00:28:05

Buscando texto en la memoria de un programa.
 
Hola qué tal?

Pues me supongo que ha muchos les sonara conocido el programa "Cheat Engine" y las multiples funciones que tiene y no
solamente que se usan para crear Trainers ( aunque siendo sinceros es lo más típico ) de hecho tiene varias funciones que me
han llamado la atención como por ejemplo la opción que sirve para acelerar un programa seleccionado a cierta velocidad...
Pero realmente vengo a hacer la consulta por la busqueda de "String" ... En Cheat Engine se selecciona un programa y se puede buscar
distintos tipos de variables, Floats, Double, Strings entre otros...

Quisiera saber como hacer un programa similar que busque un String y que halle su address para posteriormente modificar su valor..
Espero no sea mucho pedir...

Gracias

Casimiro Notevi 27-08-2015 00:39:00

Ábrelo en modo binario, hay varios ejemplos por los foros.

ecfisa 27-08-2015 00:57:37

Hola.

Si como dice Casimiro, se ha tratado varias veces. Ahora mismo recuerdo este ejemplo.

Saludos :)

Casimiro Notevi 27-08-2015 01:16:16

Buena memoria :)

escafandra 27-08-2015 01:21:22

Creo que a lo que se refiere deliriun es a buscar y reemplazar texto el la memoria de otro proceso.

Para ello deberás abrir el proceso y usar las AIPs ReadProcessMemory y WriteProcessMemory. Con la primera lees un bloque de memoria del proceso "atacado" en él debes buscar tu cadena, reemplazarla (en este caso el número de caracteres reemplazados no puede superar NUNCA a la cadena original) y luego escribir el bloque de nuevo en el proceso en cuestión con WriteProcessMemory. Para buscar y reemplazar el texto puedes usar el código que propone ecfisa o este.


Saludos.

deliriun 27-08-2015 01:21:43

Aclaración
 
Cuando puse de ejemplo el programa Cheat Engine me refería cuando el programa este corriendo
abrirlo desde el proceso ( como lo hace el Cheat Engine ) y una vez ahí hacer una búsqueda en memoria pero solo de String..
Obtener el address y cambiar su valor...

ecfisa 27-08-2015 01:33:21

Hola deliriun.
Cita:

Empezado por deliriun (Mensaje 495896)
Cuando puse de ejemplo el programa Cheat Engine me refería cuando el programa este corriendo
abrirlo desde el proceso ( como lo hace el Cheat Engine ) y una vez ahí hacer una búsqueda en memoria pero solo de String..
Obtener el address y cambiar su valor...

A... había interpretado otra cosa, ahora entiendo. Entonces escafandra ya te dió las pautas correctas de como encararlo en el mensaje #5.

Saludos :)

escafandra 27-08-2015 01:33:29

Lee el mensaje 5

Saludos.

escafandra 27-08-2015 01:34:34

Caramba, ecfisa, ya nos hemos pisado :D

Saludos.

deliriun 27-08-2015 04:13:45

Un ejemplo.
 
Alguien me podría dar un ejemplo de como emplear... No entiendo mucho las paginas de Microsoft

Casimiro Notevi 27-08-2015 09:58:11

No te lo tomes a mal, pero si dices eso es que quizás no estés preparado todavía para hacer algo así.

ecfisa 27-08-2015 13:42:10

Hola.

Seguramente... y por otro lado:
Saludos :)

escafandra 27-08-2015 14:06:51

Independientemente de que sea o no capaz de llegar a entender el uso de esas API y que les de un uno eficaz para su problema, yo quisiera saber cual es el interés particular de deliriun en alterar las cadenas de un proceso y cual es el proceso que quiere alterar.


Saludos.

escafandra 28-08-2015 14:39:47

Vamos a dar un empujoncito al tema. He escrito una función un poco a lo bruto para cambiar una cadena en un proceso. He de aclarar que está escrito en delphi7, que se compila para 32bits, esto quiere decir que no se pueden atacar de esta forma procesos de 64bits. Lo he probado en winXP y en Win10:

Código Delphi [-]
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Label1: TLabel;
    Edit2: TEdit;
    Label2: TLabel;
    Label3: TLabel;
    Edit3: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

function  _wcsnicmp(wstr1, wstr2: PWCHAR; Size: integer): Integer; cdecl external 'crtdll';
function  wcslen(wstr: PWCHAR): Integer; cdecl external 'crtdll';


var
  Form1: TForm1;

implementation

{$R *.dfm}

//---------------------------------------------------------------------------
function EnablePrivilege(name: String; Enable: boolean = true): boolean;
var
  hToken: Cardinal;
  priv: TOKEN_PRIVILEGES;
begin
  priv.PrivilegeCount:= 1;
  priv.Privileges[0].Attributes:= 0;
  if Enable then priv.Privileges[0].Attributes:= SE_PRIVILEGE_ENABLED;
  LookupPrivilegeValue(nil, PCHAR(name), priv.Privileges[0].Luid);
  OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, hToken);
  AdjustTokenPrivileges (hToken, FALSE, priv, sizeof(priv), nil, PDWORD(nil)^);
  Result:= (GetLastError = ERROR_SUCCESS);
  CloseHandle (hToken);
end;

//---------------------------------------------------------------------------
function RemoteReplaceString(PID: DWORD; Str, NewStr: PWCHAR): BOOL;
const
  nBytes = $16000;
var
  hProc: THANDLE;
  n, i, T, Len, nBytesRead: DWORD;
  Buffer, RemoteAddr: PBYTE;
  bi: MEMORY_BASIC_INFORMATION;
begin
  Result:= false;
  hProc:= OpenProcess(PROCESS_ALL_ACCESS, false, PID);
  Len:= wcslen(Str) * sizeof(WCHAR);
  GetMem(Buffer, nBytes);

  EnablePrivilege('SeDebugPrivilege');

  // Escaneamos la memoria del proceso en busca de la cadena
  for n:= 0 to $1000 do
  begin
     RemoteAddr:= PBYTE(n*nBytes-Len);
     ReadProcessMemory(hProc, RemoteAddr, Buffer, nBytes, nBytesRead);
     if nBytesRead > 0 then
     begin
       //Le resto el modulo de la división para no perderme cadenas partidas
       T:= nBytesRead - (nBytesRead mod wcslen(Str));
       i:= 0;
       while i <= T do
       begin
         // Busco la cadena en el buffer "no case sensitive"
         if _wcsnicmp(PWCHAR(DWORD(Buffer)+i), Str, wcslen(Str)) = 0 then
         begin
            // Si la encuentro la escribo en el proceso remoto
            if WriteProcessMemory(hProc, pointer(DWORD(RemoteAddr) + i), NewStr, Len, nBytesRead) then
              // si pude escribir incremento el índice para seguir buscando
              i:= i + wcslen(Str);
         end;
         inc(i);
       end;
     end;
   end;
   FreeMem(Buffer);
   CloseHandle(hProc);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  proc: TProcessEntry32;
  hSysSnapshot: THandle;
begin
  proc.dwSize := SizeOf(TProcessEntry32);
  hSysSnapshot:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (hSysSnapshot <> INVALID_HANDLE_VALUE) and Process32First(hSysSnapshot, proc) then
  begin
    repeat
      if SameText(String(proc.szExeFile), Edit1.Text) then
        RemoteReplaceString(proc.th32ProcessID, PWCHAR(WideString(Edit2.Text)), PWCHAR(WideString(Edit3.Text)));
    until not (Process32Next(hSysSnapshot, proc));
  end;
  CloseHandle(hSysSnapshot);
end;

end.


He de comentar lo siguiente:

1- Las cadenas de un proceso en memoria deben tratarse como UNICODE
2- He usado funciones "C" de la librería crtdll.dll que trae Windows como parte del S.O. para faciñitar la búsquede de cadenas a bajo nivel
- wcslen devuelve el número de caracteres de una cadena tipo WCHAR
- _wcsnicmp compara una cadena WCHAR con otra en una longitud máxima dada y sin ser sensible a mayúsculas/minúsculas
3- Un código para 32bits no puede leer la memoria de un proceso 64 bits sin más, se precisa un driver o trucos especiales, esto es debido a la incompatibilidad de los punteros que definen el rango de direcciones que son capaces de direccionar.
4- No se puede sustituir una cadena por otra de mayor longitud.

Espero aclarar alguna duda a deliriun.


Saludos.

deliriun 28-08-2015 20:32:10

Unicode y 64 Bits
 
El código me funciona muy bien en 32 Bits... sin embargo estoy seguro que todo programador quiere que sus programas corran en cualquier PC por eso pregunto si es posible convertirlo a 64 Bits.

Por otro lado me funciona con Unicode... pero si ese no es el caso... Cómo se debería emplear...

Muchas Gracias de antemano por su ayuda...

escafandra 29-08-2015 01:00:12

deliriun, ReadProcessMemory y WriteProcessMemory funcionan para un código de 64biits atacando otro también de 64 bits o de 32 bits, las que no te van a funcionar si compilas mi código en 64 bits (lazarus, por ejemplo) son wcslen y _wcsnicmp puesto que la librería crtdll.dll es se Win32. Te he dado las pautas, aunque pueden complicarse un poco más incorporando VirtualQueryEx. Puedes empezar a trabajar y nos cuetas. :)

Saludos.

deliriun 30-08-2015 06:02:25

Problema con String Unicode
 
Pues la verdad el código que me ha dado funciona bien... pero siendo sinceros no tengo experiencia programando con Delphi... Yo uso más vb.net por su sencillez pero admito que Delphi es mucho más potente y también hay mucho asesoramiento y ayuda como esta pagina por ejemplo así que hice la pregunta en este Foro y solo me gustaría ver como es posible hacer la búsqueda del String sin que sea UNICODE... ya que esa es mi dificultad... Y sé que este no es el sitio adecuado para preguntar pero si alguien conociera un Foro como este pero destinado a vb.net se lo agradecería mucho.

Gracias

ecfisa 30-08-2015 08:21:41

Hola deliriun.
Cita:

Empezado por deliriun (Mensaje 496037)
... Y sé que este no es el sitio adecuado para preguntar pero si alguien conociera un Foro como este pero destinado a vb.net se lo agradecería mucho.

Supongo que Msdn foros - Lenguaje VB.NET - Microsoft debería ser suficientemente serio.

En la búsqueda, aparecen varios mas:Pero como no conozco nada acerca de vb.net, no puedo evaluar el nivel de esos foros como para recomendarte alguno en especial.

Saludos :)

escafandra 30-08-2015 15:56:25

Cita:

Empezado por deliriun (Mensaje 496037)
Pues la verdad el código que me ha dado funciona bien... pero siendo sinceros no tengo experiencia programando con Delphi... Yo uso más vb.net por su sencillez pero admito que Delphi es mucho más potente y también hay mucho asesoramiento y ayuda como esta pagina por ejemplo así que hice la pregunta en este Foro y solo me gustaría ver como es posible hacer la búsqueda del String sin que sea UNICODE... ya que esa es mi dificultad... Y sé que este no es el sitio adecuado para preguntar pero si alguien conociera un Foro como este pero destinado a vb.net se lo agradecería mucho.

Gracias

Bueno, vista la dificultad y siendo éste un foro para responder preguntas, te muestro este nuevo código, mucho más elaborado, que funciona en delphi y Lazarus. En Lazarus compilado para 64bits encuentra y cambia cadenas en procesos de 32 y 64 bits:
Código Delphi [-]
function _StrCmpNIW(Str1, Str2: PWCHAR; N: integer): DWORD;
var
  T: integer;
begin
  T:= 0;
  // Pasando a minusculas y comparando caracteres
  while ((Ord(Str1^) or 32) = (Ord(Str2^) or 32)) and (Str1^ <> #0) and (Str2^ <> #0) and (T < N) do
  begin
    inc(Str1);
    inc(Str2);
    inc(T);
  end;
  Result:= N-T;
end;

function EnablePrivilege(name: String; Enable: boolean = true): boolean;
var
  hToken: THANDLE;
  priv: TOKEN_PRIVILEGES;
begin
  priv.PrivilegeCount:= 1;
  priv.Privileges[0].Attributes:= 0;
  if Enable then priv.Privileges[0].Attributes:= SE_PRIVILEGE_ENABLED;
  LookupPrivilegeValue(nil, PCHAR(name), priv.Privileges[0].Luid);
  OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, hToken);
  AdjustTokenPrivileges (hToken, FALSE, priv, sizeof(priv), PTOKEN_PRIVILEGES(0)^, PDWORD(nil)^);
  Result:= (GetLastError = ERROR_SUCCESS);
  CloseHandle (hToken);
end;

//---------------------------------------------------------------------------
function RemoteReplaceString(PID: DWORD; Str, NewStr: PWCHAR): BOOL;
var
  hProc: THANDLE;
  i, T, Size, Len, nBytesRead: DWORD;
  Buffer: PCHAR;
  mbi: MEMORY_BASIC_INFORMATION;
  Start: PCHAR;
begin
  Result:= false;
  hProc:= OpenProcess(PROCESS_ALL_ACCESS, false, PID);
  Len:= lstrlenW(Str);
  Size:= Len * sizeof(WCHAR);
  EnablePrivilege('SeDebugPrivilege');
  Start:= nil;

  // Escaneamos la memoria del proceso en busca de la cadena
  while VirtualQueryEx(hProc, Start, mbi, sizeof(mbi))<>0 do
  begin
    if mbi.Protect <> 0 and not(mbi.Protect and (PAGE_NOACCESS or PAGE_GUARD)) then
    begin
      Buffer:= VirtualAlloc(nil, mbi.RegionSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
      if Buffer <> nil then
      begin
        ReadProcessMemory(hProc, mbi.BaseAddress, Buffer, mbi.RegionSize, nBytesRead);
        if nBytesRead > 0 then
        begin
          //Le resto el modulo de la división para no perderme cadenas partidas
          T:= nBytesRead - Size - nBytesRead mod Size;
          i:= 0;
          while i <= T do
          begin
            // Busco la cadena en el buffer "no case sensitive"
            if _StrCmpNIW(PWCHAR(Buffer+i), Str, Len) = 0 then
            begin
              // Si la encuentro la escribo en el proceso remoto
              if WriteProcessMemory(hProc, pointer(mbi.BaseAddress + i), NewStr, Size, PDWORD(0)^) then
              begin
                // Incremento el índice para seguir buscando
                //i:= i + Size - 1;
                Result:= Result or true;
              end;
            end;
            inc(i);
          end;
        end;
        VirtualFree(Buffer, 0, MEM_RELEASE);
      end;
    end;
    inc(Start, mbi.RegionSize);
  end;
  CloseHandle(hProc);
end;

Al escribir código a bajo nivel para 64bits hay que tener mucho cuidado con la aritmética de punteros y conversiones, ya que su tamaño es de 64bits y no de 32, como estamos acostumbrados.
Aprovecho para mostrar una función, _StrCmpNIW, simple y más rápida que la API del mismo nombre. Es importante dotar de velocidad al barrido de memoria.

Saludos.

escafandra 30-08-2015 17:24:54

Nota de última hora:

Al ver el código, subí una versión en la que comenté una línea para hacer pruebas, se debe "descomentar" para ganar algo de velocidad. Esta es la parte del código:
Código Delphi [-]
               if WriteProcessMemory(hProc, pointer(mbi.BaseAddress + i), NewStr, Size, PDWORD(0)^) then
               begin
                 // Incremento el índice para seguir buscando
       //==>     //i:= i + Size - 1;                 
                 Result:= Result or true;
               end;

Debe quedar así:
Código Delphi [-]
               if WriteProcessMemory(hProc, pointer(mbi.BaseAddress + i), NewStr, Size, PDWORD(0)^) then
               begin
                 // Incremento el índice para seguir buscando
                 i:= i + Size - 1;
                 Result:= Result or true;
               end;


Saludos.


La franja horaria es GMT +2. Ahora son las 06:25:33.

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