[Source] API Hooking - C

Started by Dark_Side, 05 de February , 2008, 08:14:07 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Dark_Side

Hi,

Como havia dito algum tempo, irei postar hoje um exemplo da técnica API Hooking. Existem diferentes formas de realizar tal técnica, como por exemplo substituir a chamada a uma determinada função pela instrução JMP contendo um endereço específico para onde o programa irá saltar. No entanto, vou mostrar uma das maneiras mais simples e eficientes de implementação: IAT (Import Address Table) Patching.

Antes de mostrar o exemplo, gostaria de esclarecer algumas coisas. Basicamente, API Hooking é uma técnica que consiste em interceptar as chamadas à API do sistema feitas por um determinado aplicativo. O hooking de API é muito usado por programas que filtram as funções CreateProcess(), CreateFile() e DeleteFile() por exemplo.


Se você já escreveu algum programa para Windows, seja ele em C, C++, Delphi, Visual Basic e até mesmo Assembly, já deve ter utilizado funções como MessageBox, Sleep, FindWindow, CopyFile, dentre outras. Essas funções fazem parte da API do Windows e estão presentes em DLLs (kernel32.dll, user32.dll). Quando você usa uma função da API, o endereço da mesma é importado da DLL e armazenado na tabela de endereços de importação (IAT) do executável. Após executá-lo, o sistema irá carregá-lo na memória justamente com as DLL's nas quais estão as funções são importadas por ele. Neste processo, os endereços das funções importadas são obtidos e escritos na tabela para que o aplicativo possa chamá-las.

A essência do IAT Hooking está justamente neste aspecto. Modificando o endereço associado a cada função importada para um endereço de uma função definida por nós, podemos fazer com que o programa sempre chame por nossa função em vez da função original importada.

Então, declaramos uma função com o mesmo protótipo e escrevemos seu endereço no lugar do endereço da função original. Assim, podemos interceptar as chamadas, filtrá-las e, opcionalmente, passar os parâmetros interceptados à função legítima.

Contudo, as funções que iremos declarar para monitorar as chamadas à API devem estar no mesmo espaço de memória do aplicativo alvo para que este último possa acessá-las. Por isso, iremos injetar uma DLL no processo contendo uma função que será executada imediatamente após a injeção para realizar o Hook. Contendo, também, as nossas funções de monitoração.

Bem, isso foi apenas uma pequena introdução sobre API Hooking, sobretudo, IAT Hooking. Antes de visualizar o código, sugiro que você procure mais informações a respeito.



Vejamos então o código de exemplo:

DLL.h


#define EXPORT __declspec(dllexport)

#define NO_IMPORT_DESCRIPTOR -2
#define MODULE_NOT_FOUND     -1
#define FUNCTION_NOT_FOUND    0
#define REPLACE_SUCCESS       1


typedef struct FuncList
  {
   
       LPCSTR lpszModuleName;
       LPCSTR lpszFunctionName;
       LPVOID lpNewAddress;
   
  }* PFUNCLIST;



EXPORT int HookFunctions();
EXPORT int ReplaceIATEntries(PFUNCLIST, HMODULE, LPDWORD);



DLL.c


#include <string.h>
#include <windows.h>
#include <tlhelp32.h>
#include <imagehlp.h>
#include "dll.h"
#include "HookFunctions.h"


#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"imagehlp.lib")

static HINSTANCE hApiHookDLL = 0;

struct FuncList Functions[] =
 {
 
   {"kernel32.dll","LoadLibraryA",LoadLibraryAHooked},
   {"wsock32.dll","recv",RecvHooked},
   {"wsock32.dll","send",SendHooked},
   {"ws2_32.dll","recv",RecvHooked},
   {"ws2_32.dll","send",SendHooked}


};

 
BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
 
    switch (dwReason)
    {

      case DLL_PROCESS_ATTACH:
         
         
hApiHookDLL = hInstance;
         
switch(HookFunctions())
           {
             
              case -1:
               MessageBox(0,"Erro ao monitorar as funções especificadas.","Erro",0x10);
               FreeLibrary(hInstance);
              break;
             
              case 0:
               MessageBox(0,"Uma ou mais funções não puderam ser monitoradas.","Aviso",0x30);
              break;

              case 1:
               MessageBox(0,"As funções especificadas estão sendo monitoradas.","OK",0x40);
              break;
                   
           }
           
        break;

        case DLL_PROCESS_DETACH:
          FreeLibrary(hInstance);
        break;

 
    }

 return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////


EXPORT int HookFunctions()
{

 DWORD dwPID, dwHooked, dwNumOfFunctions, dwMatches, i;
 HANDLE hSnapShot;
 MODULEENTRY32 mModuleEntry;
 PFUNCLIST pFuncList;

     
    dwPID     = GetCurrentProcessId();
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);

    if(hSnapShot == INVALID_HANDLE_VALUE)
      return -1;

    mModuleEntry.dwSize = sizeof(MODULEENTRY32);
   
    if(!Module32First(hSnapShot, &mModuleEntry))
      return -1;
   
    dwNumOfFunctions = sizeof(Functions) / sizeof(struct FuncList);
   
    dwHooked = dwMatches = 0;
         
    do
     {

      if(mModuleEntry.hModule != hApiHookDLL)
       {
       
         for(pFuncList = Functions, i = 0; i < dwNumOfFunctions;  pFuncList++, i++)
          {

           if(ReplaceIATEntries(pFuncList, mModuleEntry.hModule, &dwMatches) == REPLACE_SUCCESS)
             dwHooked++;
          }

       }

     }while(Module32Next(hSnapShot,&mModuleEntry));
 

    CloseHandle(hSnapShot);
   
   
    if(!dwMatches)
      return -1;
    if(dwHooked == dwMatches)
      return 1;
    else
      return 0;

}

/////////////////////////////////////////////////////////////////////////////////////

EXPORT int ReplaceIATEntries(PFUNCLIST pFuncList, HMODULE hModuleBase, LPDWORD lpMatches)
{
 
 LPCSTR  lpszModuleName;
 DWORD   dwSize, dwOld;
 HANDLE  hProcess;
 
 PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
 PIMAGE_THUNK_DATA pThunk;
 PROC * pfnFunc, pfnTarget;
 
     hProcess = GetCurrentProcess();
 
     pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
                    ImageDirectoryEntryToData(hModuleBase,
                                             TRUE,
                                             IMAGE_DIRECTORY_ENTRY_IMPORT,
                                             &dwSize);
     if(!pImportDesc)
       return NO_IMPORT_DESCRIPTOR;
                                           
     while(pImportDesc->Name)
      {
                             
        lpszModuleName = (LPSTR)((LPBYTE)hModuleBase + pImportDesc->Name);
 
        if(!_strcmpi(lpszModuleName, pFuncList->lpszModuleName))
          break;
       
        pImportDesc++;
   
      }
     
      if(!pImportDesc->Name)
       return MODULE_NOT_FOUND;
       
 
     pThunk = (PIMAGE_THUNK_DATA)((LPBYTE) hModuleBase + pImportDesc->FirstThunk);
 
     while(pThunk->u1.Function)
      {
        pfnFunc   = (PROC*)&pThunk->u1.Function;
       
        pfnTarget = GetProcAddress(GetModuleHandle(pFuncList->lpszModuleName),
                                pFuncList->lpszFunctionName);
       
        if(pfnFunc && pfnTarget && *pfnFunc == pfnTarget)
         {         
         
          if(lpMatches)
            (*lpMatches)++;
         
          if(VirtualProtect(pfnFunc, sizeof(pfnFunc), PAGE_EXECUTE_READWRITE, &dwOld) &&
            (WriteProcessMemory(hProcess, pfnFunc, &pFuncList->lpNewAddress, sizeof(LPVOID), NULL)))
            {
              return REPLACE_SUCCESS;
            }
         }
       
        pThunk++;
 
      }
 
  return FUNCTION_NOT_FOUND;
}

/////////////////////////////////////////////////////////////////////////////////////

 


HookFunctions.h


HINSTANCE WINAPI LoadLibraryAHooked(LPCSTR);

int WINAPI SendHooked(SOCKET, const char *, int, int);
int WINAPI RecvHooked(SOCKET, char *, int, int);


HookFunctions.c


#include <stdio.h>
#include <string.h>
#include <windows.h>
#include "HookFunctions.h"
#include "DLL.h"

///////////////////////////////////////////////////////////////////////////////

HINSTANCE WINAPI LoadLibraryAHooked(LPCSTR lpszModuleName)
{
 
  HINSTANCE hModule = LoadLibraryA(lpszModuleName);
 
    if(!hModule)
      return NULL;
 
    HookFunctions(); 
 
  return hModule;   
}

///////////////////////////////////////////////////////////////////////////////

int WINAPI RecvHooked(SOCKET sock, char * buffer, int  len, int flags)
{

   struct sockaddr_in addr;
   int bytes, size;
   char * IP;
   FILE * fp;

bytes = recv(sock,buffer,len,flags);

if(bytes == -1)
  return -1;


if(getpeername(sock,(struct sockaddr*)&addr,&size) == 0)
  {
   
  if(addr.sin_addr.s_addr != inet_addr("127.0.0.1"))
  {
  fp = fopen("c:\\log.txt","a+");
 
  if(fp)
   { 
    IP = inet_ntoa(addr.sin_addr);
                fputs("**********************************************************\n",fp);
    fprintf(fp,"-> Dados recebidos\n\n[+] Origem: %s\n",IP);
                fputs("**********************************************************\n\n",fp);


fwrite(buffer,1,bytes,fp);
fputs("\n\n",fp);
    fclose(fp);
       }
  }
  }

  return bytes;
}

///////////////////////////////////////////////////////////////////////////////

int WINAPI SendHooked(SOCKET sock, const char * buffer, int  len, int flags)
{

   struct sockaddr_in addr;
   int bytes, size;
   char * IP;
   FILE * fp;

     
    bytes = send(sock,buffer,len,flags);

if(bytes == -1)
  return -1;

if(getpeername(sock,(struct sockaddr*)&addr,&size) == 0)
  {
   
  if(addr.sin_addr.s_addr != inet_addr("127.0.0.1"))
  {
  fp = fopen("c:\\log.txt","a+");
 
  if(fp)
   {
        IP = inet_ntoa(addr.sin_addr);
                fputs("**********************************************************\n",fp);
fprintf(fp,"->Dados enviados\n\n[+] Destino: %s\n",IP);
                fputs("**********************************************************\n\n",fp);
fwrite(buffer,1,bytes,fp);
fputs("\n\n",fp);
    fclose(fp);
   }
  }

  }

 return bytes;
}

///////////////////////////////////////////////////////////////////////////////





No exemplo, interceptamos as funções send() e recv() da biblioteca do WinSock. Cada chamada a uma destas funções fará com que o programa abra o arquivo c:\log.txt, armazene os dados enviados/recebidos nele e repasse os parâmetros às funções verdadeiras.

Observe a seguinte linha:

if(addr.sin_addr.s_addr != inet_addr("127.0.0.1"))

Bem, resolvi incluir algo assim no código apenas para mostrar como podemos fazer um filtro simples na função. No caso, se o endereço associado ao socket não for o de loopback, loga-se a chamada à função ;)


Exemplo:








Para injetar a DLL no processo acima, eu utilizei um DLL Injector escrito em Delphi. O código fonte e o executável do mesmo foi postado no seguinte tópico:

http://www.darkers.com.br/forum/index.p ... 644.0.html


Download do source + binário da DLL:

http://one.xthost.info/darkside17/proje ... ooking.rar



Alguns links externos úteis:

http://en.wikipedia.org/wiki/Hooking
http://www.windowsitlibrary.com/Content/356/11/5.html
http://www.internals.com/articles/apispy/apispy.htm
http://www.numaboa.com.br/informatica/oiciliS/PE/
http://sandsprite.com/CodeStuff/Underst ... ports.html
http://www.ntcore.com/Files/inject2it.htm
http://cio.uiowa.edu/ITSecurity/resourc ... otkits.ppt

whit3_sh4rk

Muito bom velho.. Terei que estudar C pra entender, porém já ajuda bastante a entender como funciona na prática e pesquisar métodos para realizar o Hooking em determinada "linguagem"..

Só para completar, outro site interessante que simplesmente explica de forma bem simples sobre API Hooking:

http://www.projetobms.net/artigos.php?id=2

Ponto positivo

[]s

imleetbr

bastante interessante, a microsoft possui a detours lib, alguém já usou?

#phobia

Hum...
Esses floods me são familiar... ^^

Losdive, bem vindo novamente!
Acho que não preciso nem falar neh... rsrs

Outra coisinha... por favor, reduza o tamanho da sua assinatura por gentileza.
(maiores informações veja na Regra de Conduta do fórum)


Bye!

BiLL

Quote from: "#phobia"Losdive, bem vindo novamente!
Acho que não preciso nem falar neh... rsrs

Esse cara naum desiste, ele deve msm gostar desse forum...  ;D

LOL

Menor

Ei truta, È possivel hookar uma dll que fica
na pasta system32? Vejo que fica dificil
substituir a original por outra alterada,
Então como vou fazer um hooking assim lá...

Não vai ter como interceptar porque substituir não da,
E criar outro arquivo igual com o mesmo nome na pasta
para interceptar tbm não da!

Então como vou fazer um Api Hooking deste jeito...

kernel32.dll
wsock32.dll
ws2_32.dll

Tipo vc utilizou estas api's como base,
Nisso eu pergunto se teria como substituir elas
por uma alterada lá na pasta, Ou interceptar de alguma
forma dentro da pasta?

Té mais!