Página 1 de 2 12 ÚltimoÚltimo
Resultados 1 a 10 de 20

Tópico: [Tutorial] Criando um editor/leitor de memória em C

  1. #1
    Hacker Avatar de FoXxD
    Data de Ingresso
    Jun 2006
    Posts
    1.159
    Post Thanks / Like

    Cool [Tutorial] Criando um editor/leitor de memória em C

    Olá pessoal hoje vamos criar um simples editor de memória em C utilizando as famosas API's do Windows.
    Iremos utilizar as API's WriteProcessMemory, ReadProcessMemory, OpenProcess, CloseHandle, FindWindow.

    Bem, vou utilizar o Dev-C++ no Windows XP, mas funciona tranquilamente no WIN 7. Don't worry...
    Vamos lá.

    Primeiramente, temos que saber o quê nós queremos ler/editar. E nós vamos ler e editar os pontos de um joguinho famoso, o Pinball do Windows.
    Iremos utilizar um programa para fazer uma procura na memória de valores que queremos editar. Este programa é o TSearch, mas poderia ser utilizado outro Memory Search como Cheat Engine, Art Money e etc.

    Download do jogo, Pinball do Windows: Clique AQUI

    Mais opções de download:
    http://www.4shared.com/file/21320340...0b5/Pball.html
    http://www.mediafire.com/?rya1mmoaleu18ci
    http://www.hotshare.net/br/file/219429-9589578d07.html

    Download do TSearch, Clique AQUI:

    Mais opções de download:
    http://www.4shared.com/file/f8LJLmwa/tsearch.html
    http://www.plunder.com/tsearch-zip-download-59235.htm
    http://www.mediafire.com/?znjgyjqmjjz


    Feito o download dos arquivos necessário, vamos a procura dos endereços de memória que iremos ler e editar.
    Primeiramenta, execute o TSearch.exe e depois o pinball.exe.
    Volte ao TSearch e clique em Open Process
    Selecione o processo pinball.exe e clique em Open.

    Inicie um novo jogo no pinball e certifique-se de que a sua pontuação é zero.
    Abaixo do ícone Open Process que você acabou de clicar tem uma pequena lupa, clique nela para fazermos a primeira pesquisa na memória.
    Ao clicar na lupinha irá aparecer uma pequena janela, em value coloque 0, o resto deixe como está.
    Vamos pesquisar por um valor exato, do tipo 4 bytes e o valor é a nossa pontuação atual, ou seja zero.

    Bem, quando você clicar em OK irá aparecer uma janelinha mostrando o total de endereços na memória que tem o valor zero armazenado. São muitos endereços, precisamos de saber exatamente qual endereço armazena nossos pontos, portanto vamos filtrar ainda mais nosso conjunto de endereços.

    Volte ao pinball e faça alguns pontos.
    Capture sua pontuação atual e clique na lupinha acompanhada de três pontos, do lado direito da pequena lupa que você clicou anteriormente, para continuarmos a pesquisa a partir ds endereços achados.
    Em value coloque valor atual dos seus pontos.

    Repita estes passos até que sobre apenas 2 endereços (nesse caso).

    Estes 2 endereços armazenam nossa pontuação, se alterarmos um deles já é o bastante para demonstrarmos o uso das api's de leitura e escrita.

    Bem, escolha um endereço se houver dois, no meu caso escolhi o primeiro: A2305A.

    Agora vamos criar nosso código base para o desenvolvimento.
    Código:
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    
    int main()
    {
        return 0;   
    }
    A biblioteca windows.h é necessária para a utilização das API's, as outras duas são mais conhecidas stdio para uso das funções de entrada e saída e stdlib para a função system.

    Se verificarmos os parâmetros das funções de leitura e escrita iremos notar que exigem alguns dados que não temos no momento, portanto não podemos utilizar WriteProcessMemory e/ou ReadProcessMemory de cara, precisamos de alguns dados, que vamos obter utilizando outras API's.

    Em uma análise rápida, vamos notar que WPM, e RPM requer um parâmetro hProcess do tipo HANDLE, que é obtido através da função OpenProcess, porém essa função requer um ProcessId do tipo WORD que é obtido através da função GetWindowThreadProcessId e essa por sua vez requer um handle da janela que vamos manipular os valores do tipo HWND que é obtido através da função FindWindow.

    Bem, vamos LER o endereço capturado primeiro, e para isso precisamos abrir o processo e como já vimos uma abertura de processo requer que a chamada de mais algumas funções.
    A primeira coisa a se fazer é utilizar a função FindWindow, segue o sua sintaxe:

    HWND WINAPI FindWindow (__in_opt lpClassName LPCTSTR, __in_opt LPCTSTR lpWindowName);

    Ela vai nos retornar um identificador de janela do tipo HWND, caso erro ela nos retornará NULL.
    O primeiro parâmetro é a classe da janela, vamos deixar NULL pois utilizaremos apenas o nome da janela para capturar seu identificador.
    O segundo parâmetro é o nome da janela, vamos colocar exatamente o nome da janela do nosso joguinho. Segue como ficaria a função:
    Código:
    void lerMem(char *nJanela)
    {
    	HWND Ident;
    	
    	Ident = FindWindow(NULL, nJanela);
    	
    	if (Ident == NULL)
    	{
    		printf("Erro ao capturar identificador da janela.");
    		return;
    	}
    	
    	//HWND Obtido.
    	
    }
    Após obter o identificador de janela, vamos continuar nossa função de leitura com utilizando a API GetWindowThreadProcessId para capturar o PID (Identificador de processo).

    Segue sua sintaxe:
    DWORD WINAPI GetWindowThreadProcessId(__in HWND hWnd, __out_opt LPDWORD lpdwProcessId);

    Veja como ficaria o código:
    Código:
    void lerMem(char *nJanela)
    {
    	HWND hIdent;
    	DWORD PID;
    
    	hIdent = FindWindow(NULL, nJanela);
    	
    	if (hIdent == NULL)
    	{
    		printf("Erro ao capturar identificador da janela.");
    		return;
    	}
    	
    	//HWND Obtido.
    
    	GetWindowThreadProcessId(hIdent, &PID);
    
    	//PID Obtido.	
    	
    }
    Observe que declaramos a variável PID do tipo DWORD que vai armazenar o identificador de processo capturado. Agora podemos utilizar a função que abre o processo nos dando permissão de acesso para leitura/escrita. O protótipo dessa função é: HANDLE WINAPI OpenProcess (__in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in DWORD dwProcessId);

    Segue o código:
    Código:
    void lerMem(char *nJanela)
    {
    	HWND hIdent;
    	DWORD PID;
    	HANDLE hProc;
    
    	hIdent = FindWindow(NULL, nJanela);
    	
    	if (hIdent == NULL)
    	{
    		printf("Erro ao capturar identificador da janela.");
    		return;
    	}
    	
    	//HWND Obtido.
    
    	GetWindowThreadProcessId(hIdent, &PID);
    
    	//PID Obtido.
    
    	hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    	
    	if (hProc == NULL)
    	{
    		printf("Erro ao abrir processo.");
    		return;
    	}
    	
    	//Processo aberto
    
    	
    	
    }
    O primeiro parâmetro usado nessa função é um macro, verifique os possíveis valores de acesso aqui:
    http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx

    O segundo parâmetro, se passado como TRUE, outros processos criados a partir deste iram herdar o seu handle; do contrário, o seu handle não será herdado pelos outros processos criados a partir dele.

    O terceiro é o nosso identificador de processo, ou PID.

    Finalmente poderemos utilizar nossa função de leitura, ReadProcessMemory. Segue sua sintaxe:
    BOOL WINAPI ReadProcessMemory (__in HANDLE hProcess, __in LPCVOID lpBaseAddress, __out LPVOID lpBuffer, __in nSize size_t, __out size_t * lpNumberOfBytesRead);

    Vamos ao código:
    Código:
    void lerMem(char *nJanela)
    {
    	HWND hIdent;
    	DWORD PID;
    	HANDLE hProc;
    	int pontos; 
    
    	hIdent = FindWindow(NULL, nJanela);
    	
    	if (hIdent == NULL)
    	{
    		printf("Erro ao capturar identificador da janela.\n");
    		return;
    	}
    	
    	//HWND Obtido.
    
    	GetWindowThreadProcessId(hIdent, &PID);
    
    	//PID Obtido.
    
    	hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    	
    	if (hProc == NULL)
    	{
    		printf("Erro ao abrir processo.\n");
    		return;
    	}
    	
    	//Processo aberto
    
    	if (ReadProcessMemory(hProc, (LPCVOID)0xA2305A, &pontos, 4, 0)  == 0)
    		printf("Erro ao ler memoria.\n");
    	else
    		printf("Pontos: %d\n", pontos);
    	 
    	CloseHandle(hProc);
    }
    O primeiro parâmetro hProc é o valor que o OpenProcess retornou, necessário para utilização do ReadProcessMemory.
    O segundo parâmetro é o nosso endereço de memória a ser lido, ele está em HEXADECIMAL portanto é necessário colocar 0x antes para dizer ao compilador que isso é um número hexa.
    Utilizei um CAST para forçar sua conversão para o tipo exigido pela função.
    O terceiro parâmetro pede uma variável para ele armazenar o valor lido, e declarei uma variável do tipo int chamada pontos. Obrigatóriamente temos que passar o seu endereço na memória por isso o uso do &.
    O quarto parâmetro é o número de bytes que devem ser lidos, vamos ler 4 bytes que é o tamanho de um inteiro aqui. Verifique se no seu computador o inteiro é tratado como 4 bytes. ( printf("%d", sizeof(int)); )
    O último parâmetro é opcional, pede uma variável para ser armazenado o número de bytes lidos, vamos deixar como 0 ou NULL.

    O CloseHandle vai fechar nosso processo, aberto pelo OpenProcess.

    Execute o código abaixo e verifique o funcionamento do programa:
    Código:
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    
    void lerMem(char *nJanela)
    {
    	HWND hIdent;
    	DWORD PID;
    	HANDLE hProc;
    	int pontos; 
    
    	hIdent = FindWindow(NULL, nJanela);
    	
    	if (hIdent == NULL)
    	{
    		printf("Erro ao capturar identificador da janela.\n");
    		return;
    	}
    	
    	//HWND Obtido.
    
    	GetWindowThreadProcessId(hIdent, &PID);
    
    	//PID Obtido.
    
    	hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    	
    	if (hProc == NULL)
    	{
    		printf("Erro ao abrir processo.\n");
    		return;
    	}
    	
    	//Processo aberto
    
    	if (ReadProcessMemory(hProc, (LPCVOID)0xA2305A, &pontos, 4, NULL)  == 0)
    		printf("Erro ao ler memoria.\n");
    	else
    		printf("Pontos: %d\n", pontos);
    	 
    	CloseHandle(hProc);
    }
    
    
    int main()
    {
        lerMem("3D Pinball for Windows - Space Cadet");
        system("PAUSE");
        
        return 0;   
    }

    Ufa! Conseguimos ler um endereço de memória. Se tudo ocorreu bem, você pôde notar que lhe foi apresentado seus pontos e principalmente que são míseros pontos rsrs...
    Mas... agora vamos a escrita na memória, vamos manipular estes valores usando a API WriteProcessMemory.
    Segue sua sintaxe:

    BOOL WINAPI WriteProcessMemory( __in HANDLE hProcess, __in LPVOID lpBaseAddress, __in LPCVOID lpBuffer, __in SIZE_T nSize, __out SIZE_T *lpNumberOfBytesWritten);

    Bem, vamos utilizar o mesmo código que usamos para o ReadProcessMemory com algumas modificações, segue:

    Código:
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    
    void edtMem(char *nJanela, LPVOID endereco, int novovalor)
    {
    	HWND hIdent;
    	DWORD PID;
    	HANDLE hProc;
    
    	hIdent = FindWindow(NULL, nJanela);
    	
    	if (hIdent == NULL)
    	{
    		printf("Erro ao capturar identificador da janela.\n");
    		return;
    	}
    	
    	//HWND Obtido.
    
    	GetWindowThreadProcessId(hIdent, &PID);
    
    	//PID Obtido.
    
    	hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    	
    	if (hProc == NULL)
    	{
    		printf("Erro ao abrir processo.\n");
    		return;
    	}
    	
    	//Processo aberto
    
    	if (WriteProcessMemory(hProc, endereco, &novovalor, 4, NULL)  == 0)
    		printf("Erro ao escrever na memoria.\n");
    	else
    		printf("Memoria editada com sucesso.\n");
      
    	 
    	CloseHandle(hProc);
    }
    
    
    
    int main()
    {
        
        edtMem("3D Pinball for Windows - Space Cadet", (LPVOID)0xA2305A ,999997999);
        
        system("PAUSE");
        
        return 0;   
    }
    Fiz uma nova função usando bastante código da nossa função anterior.
    void edtMem(char *nJanela, LPVOID endereco, int pontos)

    Ela requer o título da janela, o endereço a ser editado, e o novo valor.
    A função foi chamada dentro do main da seguinte forma:
    edtMem("3D Pinball for Windows - Space Cadet", (LPVOID)0xA2305A, 999997999);

    Observe o CAST, LPVOID e não LPCVOID.

    Dentro da função, a diferença é que os valores passados para a API são requeridos no parâmetro da função edtMem.
    E a API em questão pede o endereço do novo valor, que passamos assim: &novovalor.


    Eis aí o código completo:

    Código:
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    
    void lerMem(char *nJanela)
    {
    	HWND hIdent;
    	DWORD PID;
    	HANDLE hProc;
    	int pontos; 
    
    	hIdent = FindWindow(NULL, nJanela);
    	
    	if (hIdent == NULL)
    	{
    		printf("Erro ao capturar identificador da janela.\n");
    		return;
    	}
    	
    	//HWND Obtido.
    
    	GetWindowThreadProcessId(hIdent, &PID);
    
    	//PID Obtido.
    
    	hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    	
    	if (hProc == NULL)
    	{
    		printf("Erro ao abrir processo.\n");
    		return;
    	}
    	
    	//Processo aberto
    
    	if (ReadProcessMemory(hProc, (LPCVOID)0xA2305A, &pontos, 4, NULL)  == 0)
    		printf("Erro ao ler memoria.\n");
    	else
    		printf("Pontos: %d\n", pontos);
    	 
    	CloseHandle(hProc);
    }
    
    void edtMem(char *nJanela, LPVOID endereco, int novovalor)
    {
    	HWND hIdent;
    	DWORD PID;
    	HANDLE hProc;
    
    	hIdent = FindWindow(NULL, nJanela);
    	
    	if (hIdent == NULL)
    	{
    		printf("Erro ao capturar identificador da janela.\n");
    		return;
    	}
    	
    	//HWND Obtido.
    
    	GetWindowThreadProcessId(hIdent, &PID);
    
    	//PID Obtido.
    
    	hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    	
    	if (hProc == NULL)
    	{
    		printf("Erro ao abrir processo.\n");
    		return;
    	}
    	
    	//Processo aberto
    
    	if (WriteProcessMemory(hProc, endereco, &novovalor, 4, NULL)  == 0)
    		printf("Erro ao escrever na memoria.\n");
    	else
    		printf("Memoria editada com sucesso.\n");
      
    	 
    	CloseHandle(hProc);
    }
    
    
    
    int main()
    {
        lerMem("3D Pinball for Windows - Space Cadet");
        edtMem("3D Pinball for Windows - Space Cadet", (LPVOID)0xA2305A, 999997999);
        lerMem("3D Pinball for Windows - Space Cadet");
        
        system("PAUSE");
        
        return 0;   
    }
    É isso aí pessoal espero que tenham gostado, qualquer sugestão/correção é bem-vinda.


    OBS:

    __in = Entrada
    __out = Saída
    __out_opt = Saída opcional
    __in_opt = Entrada opcional


    Referências:

    FindWindow Function - http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
    GetWindowThreadProcessId Function - http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
    OpenProcess Function - http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
    Process Security and Access Rights - http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
    ReadProcessMemory Function - http://msdn.microsoft.com/en-us/libr...(v=VS.85).aspx
    WriteProcessMemory Function - http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
    Última edição por fvox; 25 Jul 2011 às 16:17.

  2. #2
    White Hat Avatar de C0M3ND4D0R
    Data de Ingresso
    Oct 2007
    Localização
    Lins - SP
    Posts
    3.473
    Post Thanks / Like
    Muito bom, obrigado pelo seu tutorial......gosto muito do MSDN, é uma fonte de informação excelente sobre funções e API's

    Abraços
    Eu não crio falhas em Softwares, eu exploro as já existentes...I'm not a Cracker, I am a "Professional Reverse Engineers"

    http://img192.imageshack.us/img192/9291/c0mq.png
    http://img807.imageshack.us/img807/3460/vb2008bar3.png
    http://img85.imageshack.us/img85/6213/overallsig2.png
    http://img692.imageshack.us/img692/8666/5uy6z.png
    Minha Regra: ...(Do or Die)
    É extremamente proibido pedir: CHAVE DE ATIVAÇÃO/SERIAL/CRACKS/PATCHS/KEYGENS/…

  3. #3
    Newbie Avatar de Voik
    Data de Ingresso
    Jul 2011
    Posts
    32
    Post Thanks / Like
    Muito bom FoXxD!
    Manipulação de memória sempre me intrigou. É bom ver tutoriais que não são Cake Recipe. (:
    Ah... só uma coisa...
    No último código do seu post, você chama a função lerMem, porém ela não está nesse código.

    E o Delphi? Abandonou?

    []'s
    Última edição por pzn3d; 25 Jul 2011 às 18:16.

  4. #4
    Hacker Avatar de FoXxD
    Data de Ingresso
    Jun 2006
    Posts
    1.159
    Post Thanks / Like
    Voik,

    valeu a correção passou batido ali, mas já pedi para corrigirem.
    O Delphi eu deixei um pouco de lado para mexer com C, mas logo logo volto...


    Abraços,
    FoXxD

  5. #5
    Veterano ingresso Avatar de pzn3d
    Data de Ingresso
    Jul 2007
    Posts
    2.990
    Post Thanks / Like
    Simplesmente de primeira o tutorial FoXxD !
    Abraços !

  6. #6
    Membro Avatar de mayckon
    Data de Ingresso
    Aug 2006
    Posts
    389
    Post Thanks / Like
    Excelente tópico, explicou de maneira muito clara. Parabéns FoXxD!!!
    Skype:mayckonxp
    iChat:mayckon@mac.com

  7. #7
    Wannabe Avatar de h1h4x0r
    Data de Ingresso
    May 2007
    Localização
    localhost
    Posts
    592
    Post Thanks / Like
    Salve FoXxD,

    Exelente tutorial, está de parabéns

    Abração,

    Att,
    Marcelo Moraes.

  8. #8
    Newbie
    Data de Ingresso
    May 2011
    Localização
    São Paulo
    Posts
    62
    Post Thanks / Like
    Muitoo bom, parabens pelo post ;D
    Mas uma dúvida...como utilizar a apiReadProcessMemory para ler strings na memória?

  9. #9
    Newbie Avatar de Voik
    Data de Ingresso
    Jul 2011
    Posts
    32
    Post Thanks / Like
    @itachi kun:

    Eu fiz um código em Delphi, que talvez lhe ajude:

    Código:
    const
      BUFFER_SIZE = $10;
    
    function ReadProcessStringA(dwPID: DWORD; lpString: Pointer): string;
    var               
      I: Integer;
      dwTemp: DWORD;
      hProcess: THandle;
      Buffer: array [1.. BUFFER_SIZE] of Byte;
    begin
      Result := '';
    
      // Abre o processo
      hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, dwPID);
    
      while True do
      begin
        // Lê alguns bytes da memória
        ReadProcessMemory(hProcess, lpString, @Buffer, SizeOf(Buffer), dwTemp);
    
        for I := 1 to BUFFER_SIZE do
        begin
          // Verifica se terminou a string
          if Buffer[I] = $00 then
          begin
            CloseHandle(hProcess);
            Exit;
          end;
    
          // Adiciona ao resultado
          Result := Result + Chr(Buffer[I]);
        end;
    
        // Incrementa o buffer
        Inc(DWORD(lpString), SizeOf(Buffer));
      end;
    end;
    
    function ReadProcessStringW(dwPID: DWORD; lpString: Pointer): string;
    var
      I: Integer;
      dwTemp: DWORD;
      hProcess: THandle;
      Buffer: array [1.. BUFFER_SIZE] of WORD;
    begin
      Result := '';
    
      // Abre o processo
      hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, dwPID);
    
      while True do
      begin
        // Lê alguns bytes da memória
        ReadProcessMemory(hProcess, lpString, @Buffer, SizeOf(Buffer), dwTemp);
        for I := 1 to BUFFER_SIZE do
        begin
          // Verifica se terminou a string
          if Buffer[I] = $0000 then
          begin
            CloseHandle(hProcess);
            Exit;
          end;
    
          // Adiciona ao resultado
          Result := Result + Chr(Buffer[I]);
        end;
    
        // Incrementa o buffer
        Inc(DWORD(lpString), SizeOf(Buffer));
      end;
    end;
    A ideia é ir copiando os bytes, enquanto não achar o fim da string (0x00).

    Eu fiz duas funções: uma para ler strings em ASCII e outra pra ler strings em UNICODE.

    Uso:
    Código:
    myString := ReadProcessStringW(3656, Pointer($004CA748));
    Para utilizá-lo, você passa o PID e o endereço base da string como parâmetro.

    Espero que alguém possa traduzir isso para C/C++. (:

    []'s

  10. #10
    Newbie
    Data de Ingresso
    May 2011
    Localização
    São Paulo
    Posts
    62
    Post Thanks / Like
    Valeu Voik, mas ainda to com algumas dúvidas aki heheh, desde que li o post to fuçando aqui com isso pra entender melhor...
    No caso do exemplo que vc fez vc definiu que o programa irá ler 10bytes no endereço desejado certo? Mas e se no caso a string que eu estou procurando tem menos ou mais de 10bytes, mesmo assim vai funcionar?
    No exemplo do Foxxd, acredito que funcionou perfeitamente pq o valor que ele procurava era um inteiro, e ele informou seu tamanho corretamente (int = 4 bytes (32bits))..

Tags para este Tópico

Permissões de Postagem

  • Você não pode iniciar novos tópicos
  • Você não pode enviar respostas
  • Você não pode enviar anexos
  • Você não pode editar suas mensagens
  •