Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Подключение DLL к процессу


Автор: AndAnd 22.7.2006, 07:42
Здравствуйте все участники форума! 
    У меня тут возникла проблема. Нашел в Инете статью о том как перехватывать API функцию MessageBoxA из user32.dll. 
   Суть данного метода заключается в следующем (очень кратко): 
Пишем свою DLL, с функцией двойником и какую функцию необходимо перехватывать. Затем отдельно код на подключение DLL к процессу, где используется искомая API функция (я создал простой проект с MessageBox  в Buildere). Далее запускаем проект с MessageBox (в диспетчере задач появляется процесс с каким-то PID), после чего подключаем DLL к процессу. Вызываем MessageBox (с любым текстом), DLL перехватывает этот текст и добавляет к нему (или совсем другой текст) некоторый набор символов, и выдает измененное сообщение, вродебы перехват осуществился. 
Но при попытке перехватить любую API функцию из Wininet.DLL, происходит ошибка приложения, использующее эту библиотеку. Ошибка скорее всего в коде DLL, но точно не заню.  Может кто знает как исправить
вот исходник DLL на Buildere
Код

#include <windows.h>
#include <vcl.h>
#include <stdio.h>

struct jmp_far{
 BYTE instr_push;//здесь будет код инструкции push
 DWORD arg;//аргумент push
 BYTE  instr_ret;//здесь будет код инструкции ret
 };
 BYTE old[6]; //область для хранения 6-ти затираемых байт начала функции
DWORD adr_MessageBoxA; //будущий адрес оригинальной функции
DWORD written; //вспомогательная переменная
jmp_far jump; //здесь будет машинный код инструкции перехода


 void InterceptFunctions(void);
/*BOOL WINAPI Intercept_MessageBoxA(HWND hwnd, char *text,
                                  char *hdr, UINT utype);*/
BOOL WINAPI Intercept_InternetConnectA(
    IN HINTERNET hInternet,
    IN LPCSTR lpszServerName,
    IN INTERNET_PORT nServerPort,
    IN LPCSTR lpszUserName OPTIONAL,
    IN LPCSTR lpszPassword OPTIONAL,
    IN DWORD dwService,
    IN DWORD dwFlags,
    IN DWORD dwContext );   
#pragma argsused
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{

    if(fwdreason == DLL_PROCESS_ATTACH)
     {InterceptFunctions();}


        return 1;
}
//---------------------------------------------------------------------------
void InterceptFunctions(void)
{
 DWORD op;
 //сначала получим абсолютный адрес функции для перехвата
 /*adr_MessageBoxA = (DWORD)GetProcAddress(GetModuleHandle("user32.dll"),"MessageBoxA");*/

adr_MessageBoxA = (DWORD)GetProcAddress(GetModuleHandle("wininet.dll"),"InternetConnectA");
                                                   
 if(adr_MessageBoxA == 0)
     {
       MessageBox(NULL, "Can`t get adr_InternetConnectA", "Error!", 0);
       return;
     }
     // Зададим машинный код инструкции перехода, который затем впишем
     // в начало полученного адреса:
       jump.instr_push = 0x68;
       jump.arg = (DWORD)&Intercept_InternetConnectA; 
       jump.instr_ret = 0xC3;
       //Прочитаем и сохраним первые оригинальные 6 байт стандартной API функции
       ReadProcessMemory(GetCurrentProcess(),(void*) adr_MessageBoxA,(void*)&old, 6, &written);//Запишем команду перехода на нашу функцию поверх этих 6-ти байт
       WriteProcessMemory(GetCurrentProcess(), (void*)adr_MessageBoxA,(void*)&jump, sizeof(jmp_far), &written);
}
//-------------------------------------------------------------------------------
/*BOOL WINAPI Intercept_MessageBoxA(HWND hwnd, char *text,
                                  char *hdr, UINT utype)
{
   //Сначала восстанавливаем 6 первых байт функции. Это не обязательное
  // действие
  // сообщения функции MessageBoxA переделать на свои, поэтому нам придется
  // вызвать оригинальную функцию, а для этого следует восстановить ее адрес:
  WriteProcessMemory(GetCurrentProcess(), (void*)adr_MessageBoxA,(void*)&old, 6, &written);
  //char *str = "Hi From MessageBOX!!!!";
   AnsiString Txt = text;
   Txt += "Hi From MessageBOX!!!!";
  // ShowMessage(Txt);
    //Вызываем оригинальную функцию через указатель
    ((BOOL (__stdcall*)(HWND, char*, char*, UINT))adr_MessageBoxA)(hwnd,Txt.c_str(), hdr, utype);
    //Снова заменяем  6 байт функции на команду перехода на нашу функцию
    WriteProcessMemory(GetCurrentProcess(), (void*)adr_MessageBoxA,(void*)&jump, 6,&written);
    return TRUE;
}*/
//------------------------------------------------------------------------------
BOOL WINAPI Intercept_InternetConnectA(
    IN HINTERNET hInternet,
    IN LPCSTR lpszServerName,
    IN INTERNET_PORT nServerPort,
    IN LPCSTR lpszUserName OPTIONAL,
    IN LPCSTR lpszPassword OPTIONAL,
    IN DWORD dwService,
    IN DWORD dwFlags,
    IN DWORD dwContext )
{
   WriteProcessMemory(GetCurrentProcess(), (void*)adr_MessageBoxA,(void*)&old, 6, &written);

   AnsiString Txt = lpszServerName;
   ShowMessage(Txt);
  //Вызываем оригинальную функцию через указатель
  ((BOOL (__stdcall*)(IN HINTERNET, IN LPCSTR, IN INTERNET_PORT, IN LPCSTR,IN LPCSTR,IN DWORD,IN DWORD,IN DWORD))adr_MessageBoxA)(hInternet,lpszServerName, nServerPort,lpszUserName OPTIONAL,lpszPassword OPTIONAL,dwService,dwFlags,dwContext);
    //Снова заменяем  6 байт функции на команду перехода на нашу функцию
  WriteProcessMemory(GetCurrentProcess(), (void*)adr_MessageBoxA,(void*)&jump, 6,&written);
    return TRUE;
}  


а вот исходник для присоединения DLL к процессу:
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Form1->Edit1->GetTextBuf(PID,sizeof(PID));
 InjectDll(StrToInt(PID), "Project1.dll");

}
//---------------------------------------------------------------------------
BOOL InjectDll(DWORD pid, char* lpszDllName)
{
  HANDLE hProcess;
  BYTE* p_code = NULL;
  INJECTORCODE cmds;
  DWORD wr, id;

  //открыть процесс с нужным доступом
  hProcess=OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_WRITE|
  PROCESS_VM_OPERATION, FALSE, pid);
  if(hProcess == NULL)
  {
    MessageBoxA(NULL, "You have not enough rights to attach dlls",
               "Error!", 0);
    return FALSE;
  }

  //зарезервировать память в процессе
    p_code = (BYTE*)VirtualAllocEx(hProcess, NULL, sizeof(INJECTORCODE),
                                   MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if(p_code==NULL)
    {
      MessageBox(NULL, "Unable to alloc memory in remote process",
                       "Error!", 0);
      return FALSE;
    }

  //инициализировать  машинный код
  cmds.instr_push_loadlibrary_arg = 0x68; //машинный код инструкции push
  cmds.loadlibrary_arg = (DWORD)((BYTE*)p_code
           + offsetof(INJECTORCODE, libraryname));

  cmds.instr_call_loadlibrary = 0x15ff; //машинный код инструкции call
  cmds.adr_from_call_loadlibrary =
  (DWORD)(p_code + offsetof(INJECTORCODE, addr_loadlibrary));

  cmds.instr_push_exitthread_arg  = 0x68;
  cmds.exitthread_arg = 0;
  
  cmds.instr_call_exitthread = 0x15ff;
  cmds.adr_from_call_exitthread = 
  (DWORD)(p_code + offsetof(INJECTORCODE, addr_exitthread));
  
  cmds.addr_loadlibrary =
  (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

  cmds.addr_exitthread  = 
  (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitThread");
  
  if(strlen(lpszDllName)>99)
  {
     MessageBox(NULL, "Dll Name too long", "Error!", 0);
     return FALSE;
  }
  strcpy((char*)cmds.libraryname, lpszDllName );

 // После инициализации cmds в мнемонике ассемблера выглядит следующим
  //  образом:
    /*  push  adr_library_name               ;//аргумент ф-ции loadlibrary
      call dword ptr [loadlibrary_adr]     ; //вызвать LoadLibrary
      push exit_thread_arg                 ;//аргумент для ExitThread
      call dword ptr [exit_thread_adr]     ;//вызвать ExitThread
        */

  //записать машинный код по зарезервированному адресу
  WriteProcessMemory(hProcess, p_code, &cmds, sizeof(cmds), &wr);

  //выполнить машинный код
  HANDLE z = CreateRemoteThread(hProcess, NULL, 0, 
               (unsigned long (__stdcall  *)(void *))p_code, 0, 0, &id);

  //ожидать завершения удаленного потока
  WaitForSingleObject(z, INFINITE);
  //освободить память
  VirtualFreeEx(hProcess, (void*)p_code, sizeof(cmds), MEM_RELEASE);

  return TRUE;
}


Спасибо за внимание. Жду ответов по-существу. 

Автор: GremlinProg 22.7.2006, 17:33
пока причины не понял, но явно бросается в глаза вызов "call" инструкции: 0x15ff - это что угодно, но не "call". У "call" всего 4 возможных варианта кода: E8im, FF/2, 9Aim, FF/3 и ни под один 0x15ff не подходит! После слеша указан код регистра RO, т.е. старший халф-байт второго слова расширенной(двухбайтовой) команды.

Теперь по сути: ты пытаешься перепизать какой-нибудь экспортный вызов в уже загруженном модуле, но учти один момент: не все экспортируемые методы имеют одинаковую модель вызова: экспортировать можно и __fastcall и __cdecl, а ты используешь только один подход при подстановке своего кода. 

Автор: Romikgy 22.7.2006, 17:58
GremlinProg, имхо в обратном порядке это и будет 0xff, 0x15 smile 

Автор: GremlinProg 22.7.2006, 18:01
Romikgy, собственно, об чем я и говорю: (15!=3)&&(15!=2). ))

Добавлено @ 18:02 
вернее (0x01!=0x03)&&(0x01!=0x02), это стабший полубайт! 

Автор: Romikgy 22.7.2006, 18:04
проглядел smile
я не силен в асме , не знаю его команд smile  

Автор: AndAnd 22.7.2006, 20:26
Вот какую ошибку мне выдает IE если к нему подключать эту библиотеку.
 как я вижу DLL подключена к IE, но почему-то здесь все  по нулям. 

Автор: dumb 22.7.2006, 22:18
ff 15 xx xx xx xx - call dword ptr [xx xx xx xx]

__fastcall на экспорт - таки не типичный случай smile

по существу: #pragma pack(1) smile

Добавлено @ 22:20 
и не забудь подставлять полный путь к .dll, если она не в system32... 

Автор: AndAnd 23.7.2006, 06:20
Цитата

ff 15 xx xx xx xx - call dword ptr [xx xx xx xx]

__fastcall на экспорт - таки не типичный случай 

по существу: #pragma pack(1) 



dumb, извини, но я не понял, что ты этим хочешь сказать.

GremlinProg, ты не нашел причину? я один с этим не справлюсь, мне нужна ваша помощь, я смотрю тут все соображают, кроме меня. smile  

Автор: dumb 23.7.2006, 19:04
"оберни" объявления структур INJECTORCODE и jmp_far:

Код

#pragma pack(push, 1)
struct бла-бла-бла {
...
};
#pragma pack(pop)
 

Автор: GremlinProg 23.7.2006, 22:10
Извиняюсь, про двухбитный MOD забыл, все привильно ff 15 xx xx xx xx - call dword ptr [xx xx xx xx], как ты и сказал.

Добавлено @ 22:13 
покажи объявление INJECTORCODE

Добавлено @ 22:21 
call dword ptr [loadlibrary_adr] - это вызов из импортного THUNC'а LoadLibrary, а GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA") даст тебе прямой линк на метод, получается команда выполнит джамп неизвестно куда, т.е. на адрес по адресу первого дворда кода LoadLibraryA. Нужно заменить тогда ff 15 xx xx xx xx на  E8 xx xx xx xx - это на байт короче.

Добавлено @ 22:22 
но бросает вызов сразу по смещению, без dword ptr [] 

Автор: AndAnd 24.7.2006, 02:49
Цитата

покажи объявление INJECTORCODE

вот
Код

//структура описывает поля, в которых содержится код внедрения
#pragma pack(push, 1)
struct INJECTORCODE
{
  BYTE  instr_push_loadlibrary_arg; //инструкция push
  DWORD loadlibrary_arg;            //аргумент push

  WORD  instr_call_loadlibrary;     //инструкция call []
  DWORD adr_from_call_loadlibrary;

  BYTE  instr_push_exitthread_arg;
  DWORD exitthread_arg;

  WORD  instr_call_exitthread;
  DWORD adr_from_call_exitthread;

  DWORD addr_loadlibrary;
  DWORD addr_exitthread;     //адрес функции ExitTHread
  BYTE  libraryname[100];    //имя и путь к загружаемой библиотеке
};
#pragma pack(pop)


 

Автор: dumb 24.7.2006, 03:23
код рабочий, слит с http://www.rsdn.ru/article/baseserv/IntercetionAPI.xml. просто было не учтено выравнивание в структуре.

AndAnd, неужели не работает до сих пор?! smile 

Автор: AndAnd 24.7.2006, 04:03
dumb, именно с этой статьи я и брал код. Просто мне надо перехватить не MessageBoxA, а функцию InternetConnectA из wininet.dll. Но при простой замене функции MessageBox на InternetConnectA ничего не получается, приложение выдает ошибку.

Цитата

неужели не работает до сих пор?! 

Может я что-н. не так делаю, но не работает. smile  

Автор: dumb 24.7.2006, 12:01
dll:
Код

#include <windows.h>
#include <vcl.h>
#include <stdio.h>

#pragma pack(push, 1)
struct jmp_far{
 BYTE instr_push;//здесь будет код инструкции push
 DWORD arg;//аргумент push
 BYTE  instr_ret;//здесь будет код инструкции ret
 };
#pragma pack(pop)

 BYTE old[6]; //область для хранения 6-ти затираемых байт начала функции
DWORD adr_MessageBoxA; //будущий адрес оригинальной функции
DWORD written; //вспомогательная переменная
jmp_far jump; //здесь будет машинный код инструкции перехода
 void InterceptFunctions(void);
/*BOOL WINAPI Intercept_MessageBoxA(HWND hwnd, char *text,
                                  char *hdr, UINT utype);*/
HINTERNET WINAPI Intercept_InternetConnectA(
    IN HINTERNET hInternet,
    IN LPCSTR lpszServerName,
    IN INTERNET_PORT nServerPort,
    IN LPCSTR lpszUserName OPTIONAL,
    IN LPCSTR lpszPassword OPTIONAL,
    IN DWORD dwService,
    IN DWORD dwFlags,
    IN DWORD dwContext );
#pragma argsused
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
    if(fwdreason == DLL_PROCESS_ATTACH)
     {InterceptFunctions();}
        return 1;
}
//---------------------------------------------------------------------------
void InterceptFunctions(void)
{
 DWORD op;
 //сначала получим абсолютный адрес функции для перехвата
 /*adr_MessageBoxA = (DWORD)GetProcAddress(GetModuleHandle("user32.dll"),"MessageBoxA");*/
adr_MessageBoxA = (DWORD)GetProcAddress(GetModuleHandle("wininet.dll"),"InternetConnectA");

 if(adr_MessageBoxA == 0)
     {
       MessageBox(NULL, "Can`t get adr_InternetConnectA", "Error!", 0);
       return;
     }
     // Зададим машинный код инструкции перехода, который затем впишем
     // в начало полученного адреса:
       jump.instr_push = 0x68;
       jump.arg = (DWORD)&Intercept_InternetConnectA;
       jump.instr_ret = 0xC3;
       //Прочитаем и сохраним первые оригинальные 6 байт стандартной API функции
       ReadProcessMemory(GetCurrentProcess(),(void*) adr_MessageBoxA,(void*)&old, 6, &written);//Запишем команду перехода на нашу функцию поверх этих 6-ти байт
       WriteProcessMemory(GetCurrentProcess(), (void*)adr_MessageBoxA,(void*)&jump, sizeof(jmp_far), &written);
}
//------------------------------------------------------------------------------
HINTERNET WINAPI Intercept_InternetConnectA(
    IN HINTERNET hInternet,
    IN LPCSTR lpszServerName,
    IN INTERNET_PORT nServerPort,
    IN LPCSTR lpszUserName OPTIONAL,
    IN LPCSTR lpszPassword OPTIONAL,
    IN DWORD dwService,
    IN DWORD dwFlags,
    IN DWORD dwContext )
{
   WriteProcessMemory(GetCurrentProcess(), (void*)adr_MessageBoxA,(void*)&old, 6, &written);
   AnsiString Txt = lpszServerName;
   ShowMessage(Txt);
  //Вызываем оригинальную функцию через указатель
  HINTERNET ret = ((HINTERNET (__stdcall*)(HINTERNET,LPCSTR,INTERNET_PORT,LPCSTR,LPCSTR,DWORD,DWORD,DWORD))adr_MessageBoxA)(hInternet,lpszServerName, nServerPort,lpszUserName,lpszPassword,dwService,dwFlags,dwContext);
    //Снова заменяем  6 байт функции на команду перехода на нашу функцию
  WriteProcessMemory(GetCurrentProcess(), (void*)adr_MessageBoxA,(void*)&jump, 6,&written);
  return ret;
}


кусок от "присоединителя" (в нем, собственно, и так все нормально - на всякий случай):
Код

//---------------------------------------------------------------------------
//структура описывает поля, в которых содержится код внедрения
#pragma pack(push, 1)
struct INJECTORCODE
{
  BYTE  instr_push_loadlibrary_arg; //инструкция push
  DWORD loadlibrary_arg;            //аргумент push

  WORD  instr_call_loadlibrary;     //инструкция call []
  DWORD adr_from_call_loadlibrary;

  BYTE  instr_push_exitthread_arg;
  DWORD exitthread_arg;

  WORD  instr_call_exitthread;
  DWORD adr_from_call_exitthread;

  DWORD addr_loadlibrary;
  DWORD addr_exitthread;     //адрес функции ExitTHread
  BYTE  libraryname[100];    //имя и путь к загружаемой библиотеке
};
#pragma pack(pop)

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 char PID[512];
 Form1->Edit1->GetTextBuf(PID,sizeof(PID));
 InjectDll(StrToInt(PID), поменяй на свой"D:\\Projects\\inject_test\\Pinj_dll.dll");
}
//---------------------------------------------------------------------------
BOOL InjectDll(DWORD pid, char* lpszDllName)
{
  HANDLE hProcess;
  BYTE* p_code = NULL;
  INJECTORCODE cmds;
  DWORD wr, id;
  //открыть процесс с нужным доступом
  hProcess=OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_WRITE|
  PROCESS_VM_OPERATION, FALSE, pid);
  if(hProcess == NULL)
  {
    MessageBoxA(NULL, "You have not enough rights to attach dlls",
               "Error!", 0);
    return FALSE;
  }
  //зарезервировать память в процессе
    p_code = (BYTE*)VirtualAllocEx(hProcess, NULL, sizeof(INJECTORCODE),
                                   MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if(p_code==NULL)
    {
      MessageBox(NULL, "Unable to alloc memory in remote process",
                       "Error!", 0);
      return FALSE;
    }
  //инициализировать  машинный код
  cmds.instr_push_loadlibrary_arg = 0x68; //машинный код инструкции push
  cmds.loadlibrary_arg = (DWORD)((BYTE*)p_code
           + offsetof(INJECTORCODE, libraryname));
  cmds.instr_call_loadlibrary = 0x15ff; //машинный код инструкции call
  cmds.adr_from_call_loadlibrary =
  (DWORD)(p_code + offsetof(INJECTORCODE, addr_loadlibrary));
  cmds.instr_push_exitthread_arg  = 0x68;
  cmds.exitthread_arg = 0;

  cmds.instr_call_exitthread = 0x15ff;
  cmds.adr_from_call_exitthread =
  (DWORD)(p_code + offsetof(INJECTORCODE, addr_exitthread));

  cmds.addr_loadlibrary =
  (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
  cmds.addr_exitthread  = 
  (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitThread");
  
  if(strlen(lpszDllName)>99)
  {
     MessageBox(NULL, "Dll Name too long", "Error!", 0);
     return FALSE;
  }
  strcpy((char*)cmds.libraryname, lpszDllName );
 // После инициализации cmds в мнемонике ассемблера выглядит следующим
  //  образом:
    /*  push  adr_library_name               ;//аргумент ф-ции loadlibrary
      call dword ptr [loadlibrary_adr]     ; //вызвать LoadLibrary
      push exit_thread_arg                 ;//аргумент для ExitThread
      call dword ptr [exit_thread_adr]     ;//вызвать ExitThread
        */
  //записать машинный код по зарезервированному адресу
  WriteProcessMemory(hProcess, p_code, &cmds, sizeof(cmds), &wr);
  //выполнить машинный код
  HANDLE z = CreateRemoteThread(hProcess, NULL, 0, 
               (unsigned long (__stdcall  *)(void *))p_code, 0, 0, &id);
  //ожидать завершения удаленного потока
  WaitForSingleObject(z, INFINITE);
  //освободить память
  VirtualFreeEx(hProcess, (void*)p_code, sizeof(cmds), MEM_RELEASE);
  return TRUE;
}
 

Автор: AndAnd 24.7.2006, 18:53
dumb , спасибо тебе большое, очень выручил! Спасибо и всем  участникам данной темы: GremlinProg , Romikgy . 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)