Конференция "WinAPI" » Хук WinAPI функции в определенном приложении
 
  • Dark_Ph0eNix (04.07.16 07:58) [0]
    Доброго времени суток
    Читал много про хуки , но понять не смог.

    У меня есть просто приложение которое из мемо1 конвертирует текст в Dos кодировку и записывает полученный результат в мемо2.

    Использую вот такой код функции

    function WinToDos(ASource: String): AnsiString;
    var
     Ch: PAnsiChar;
    begin
     Ch := AnsiStrAlloc(Length(ASource) + 1);
     CharToOem(PChar(ASource), Ch);
     Result := StrPas(Ch);
     StrDispose(Ch)
    end;



    В программе

    memo2.text:=WinToDos(memo1.text);



    Хочу сделать хук WinAPI функции CharToOem, что бы моя функция не конвертировала данные с помощью функции CharToOem, а просто передавала не измененный данные в мемо2
    Вот как это реализовать через SetWindowsHookEx не знаю.

    Предполагаю надо начинать с чтения отправляемых данных через WH_CALLWNDPROC и эти же данные передавать через WH_GETMESSAGE
    Возможно только что описал бред.

    В распоряжение Win7 и RAD 2010
    Подскажите как это можно реализовать.
    Пример с комментариями по возможности или ссылку на подобный материал.
    Заранее благодарен.

    P.S. Программированием занимался в последний раз лет 7 назад, так что не судите строго.
  • Leonid Troyanovsky © (04.07.16 08:36) [1]

    > Dark_Ph0eNix   (04.07.16 07:58)

    > memo2.text:=WinToDos(memo1.text);

    memo2.text:=memo1.text; ?

    Если же приложение чужое, то перетащить текст
    можно путем SendMessage WM_SETTEXT/WM_GETTEXT.

    --
    Regards, LVT.
  • Dark_Ph0eNix (04.07.16 09:36) [2]
    Leonid Troyanovsky спасибо
    мемо1 и мемо2 использованы в качестве наглядного примера использования функции CharToOem
    Но задача в том чтобы перехватывать именно вызов CharToOem и что бы подмененная функция выдавал те же данные что и получила

    К примеру

    var
    src: PChar
    dst: AnsiChar
    begin
    src:='й';
    CharToOem(src, dst);
    showmessage(src);
    showmessage(dst);
    end;

    Результатом будет вывод двух сообщений в первом будет "й", во втором будет "©".

    А мне надо что бы при перехвате CharToOem в "dst" было "й" вместо "©".

    Надеюсь понятно объяснил )))
  • Dark_Ph0eNix (04.07.16 09:39) [3]
    Хукать планирую приложение которое получает данные из базы, конвертирует в оем и потом шлет в ком-порт. Из-за конвертирования в в оем русские и казахские символы передаются не правильные.
  • NoUser © (04.07.16 14:31) [4]
    1
    Dark_Ph0eNix , а как работала, и работали ли правильно, подопытная программа до того как Вы решили вспомнить сколько лет не программировали и почему уверенны что в ней есть вызов CharToOem ?

    2
    > Читал много про хуки , но понять не смог.
    Теперь  много читайте про http://habrahabr.ru/post/178393/

    3
    > и что бы подмененная функция выдавал те же данные что и получила
    но это Вас, скорее всего, не спасёт, так как дальше программа, всё равно эти данные будет воспринимать и передавать
    не как UnicodeString,  а как AnsiString.

    4
    > и потом шлет в ком-порт
    Почитайте ёще про “com0com”.
    Напишите (или подумайте над пунктом б ) программу которая будет:
    a) получать данные с подопытной посредством виртуальных ком портов;
    б) преобразовывать их так ‘как нужно’;
    в) отправлять в реальный ком порт на внешнее устройство.

    @
    AnsiStrAlloc, StrDispose – где-то подсмотрели, или осознанно используете эти функции?
  • Dark_Ph0eNix (07.07.16 06:03) [5]

    > NoUser ©

    Спасибо большое за ссылку, ушел изучать что и как


    > AnsiStrAlloc, StrDispose – где-то подсмотрели, или осознанно
    > используете эти функции?

    Нашел на просторах интернета, код выполнял поставленную задачу, а именно преобразовывал текст из 1251 в дос.


    > Dark_Ph0eNix , а как работала, и работали ли правильно,
    > подопытная программа до того как Вы решили вспомнить сколько
    > лет не программировали и почему уверенны что в ней есть
    > вызов CharToOem ?

    Программа до этого работала нормально и сейчас работает нормально, только вот проблема в том что казахские символы в частности он преобразует в знак "_".
    По поводу вызова CharToOemA уверен т.к. предварительно поставил API Monitor, отловил исходный текст до преобразования, пробежался по списку функций которые работают с текстом, поставил брэйкпоинты, откорректировал текст который отдает функция CharToOemA и после этого в порт ушел правильный текст, по этому абсолютно уверен в использовании именно этой функции из user32.dll
  • Dark_Ph0eNix (07.07.16 07:10) [6]
    А сколько будет стоить написание такого приложения?
    Может мне будет проще заплатить кому-нибудь чем ломать свой мозг?
  • Alekc (07.07.16 16:48) [7]
    Вот эту библиотеку посмотрите, по идее это то что вам нужно:
    https://github.com/MahdiSafsafi/delphi-detours-library#start-of-content
  • NoUser © (07.07.16 23:21) [8]
    > Dark_Ph0eNix   (07.07.16 07:10) [5]

    Раз уж знакомы со словом брэйкпоинт, то на коленке можно сделать так:

    1. Найти с помощью отладчика адрес памяти куда загрузчик помещает адрес нужной нам функции.

    2. Разместить в директории программы длл-переходник на одну из системных длл,
    что даст возможность разместить в программе новую функцию и подменить на неё значение по адресу из пункта 1.

    --
    Основная сложность, скорее всего, будет в поиске удобной длл.

    Посмотреть из каких длл и что вызывает программа можно этим инструментом http://www.dependencywalker.com/.

    --
    Например, программы собранные в Delphi с Vcl, экспортируют несколько функций из Version.dll (да и всего там ~15 функций).
    Что идеально подходит для эксперимента.

    Код длл-переходника может быть таким:

    library Version;

    uses
     Windows;

    // ------

    const
    cVersionDLL = '\Version.dll';
    cVerQueryValue = 'VerQueryValue';
    cGetFileVersionInfo = 'GetFileVersionInfo';
    cGetFileVersionInfoSize = 'GetFileVersionInfoSize';

    type
    TVerQueryValue = function (pBlock: Pointer; lpSubBlock: LPWSTR; var lplpBuffer: Pointer; var puLen: UINT): BOOL; stdcall;
    TGetFileVersionInfo = function (lptstrFilename: LPWSTR; dwHandle, dwLen: DWORD; lpData: Pointer): BOOL; stdcall;
    TGetFileVersionInfoSize = function (lptstrFilename: LPWSTR; var lpdwHandle: DWORD): DWORD; stdcall;

    var
    OriVersionDLL : HModule;
    OriVerQueryValueA ,
    OriVerQueryValueW : TVerQueryValue;
    OriGetFileVersionInfoA ,
    OriGetFileVersionInfoW : TGetFileVersionInfo;
    OriGetFileVersionInfoSizeA ,
    OriGetFileVersionInfoSizeW : TGetFileVersionInfoSize;

    //

    function OriDllLoad: Boolean;
    var
    s: string;
    begin
    Result := (OriVersionDLL <> 0);
    if not Result then begin
      SetLength(s, 1024);
      SetLength(s, GetSystemDirectory(PChar(s), Length(s)));
      OriVersionDLL := LoadLibrary(PChar(s + cVersionDLL));
      if (OriVersionDLL <> 0) then begin
        OriVerQueryValueA := GetProcAddress(OriVersionDLL, cVerQueryValue+'A');
        OriVerQueryValueW := GetProcAddress(OriVersionDLL, cVerQueryValue+'W');
        OriGetFileVersionInfoA := GetProcAddress(OriVersionDLL, cGetFileVersionInfo+'A');
        OriGetFileVersionInfoW := GetProcAddress(OriVersionDLL, cGetFileVersionInfo+'W');
        OriGetFileVersionInfoSizeA := GetProcAddress(OriVersionDLL, cGetFileVersionInfoSize+'A');
        OriGetFileVersionInfoSizeW := GetProcAddress(OriVersionDLL, cGetFileVersionInfoSize+'W');
        Result := ( (@OriVerQueryValueA <> nil) and (@OriVerQueryValueW <> nil)
                and (@OriGetFileVersionInfoA <> nil) and (@OriGetFileVersionInfoW <> nil)
                and (@OriGetFileVersionInfoSizeA <> nil) and (@OriGetFileVersionInfoSizeW <> nil) );
     end;
    end;
    end;

    procedure OriDllFree;
    begin
    if (OriVersionDLL <> 0) then begin
      FreeLibrary(OriVersionDLL);
      OriVersionDLL := 0;
    end;
    end;

    //

    function VerQueryValueA(pBlock: Pointer; lpSubBlock: LPWSTR; var lplpBuffer: Pointer; var puLen: UINT): BOOL; stdcall;
    begin
    if ( OriDllLoad() )
      then Result := OriVerQueryValueA(pBlock, lpSubBlock, lplpBuffer, puLen)
      else Result := False;
    end;

    function GetFileVersionInfoA(lptstrFilename: LPWSTR; dwHandle, dwLen: DWORD; lpData: Pointer): BOOL; stdcall;
    begin
    if ( OriDllLoad() )
      then Result := OriGetFileVersionInfoA(lptstrFilename, dwHandle, dwLen, lpData)
      else Result := False;
    end;

    function GetFileVersionInfoSizeA(lptstrFilename: LPWSTR; var lpdwHandle: DWORD): DWORD; stdcall;
    begin
    if ( OriDllLoad() )
      then Result := OriGetFileVersionInfoSizeA(lptstrFilename, lpdwHandle)
      else Result := 0;
    end;

    function VerQueryValueW(pBlock: Pointer; lpSubBlock: LPWSTR; var lplpBuffer: Pointer; var puLen: UINT): BOOL; stdcall;
    begin
    if ( OriDllLoad() )
      then Result := OriVerQueryValueW(pBlock, lpSubBlock, lplpBuffer, puLen)
      else Result := False;
    end;

    function GetFileVersionInfoW(lptstrFilename: LPWSTR; dwHandle, dwLen: DWORD; lpData: Pointer): BOOL; stdcall;
    begin
    if ( OriDllLoad() )
      then Result := OriGetFileVersionInfoW(lptstrFilename, dwHandle, dwLen, lpData)
      else Result := False;
    end;

    function GetFileVersionInfoSizeW(lptstrFilename: LPWSTR; var lpdwHandle: DWORD): DWORD; stdcall;
    begin
    if ( OriDllLoad() )
      then Result := OriGetFileVersionInfoSizeW(lptstrFilename, lpdwHandle)
      else Result := 0;
    end;

    //

    exports
    VerQueryValueA          index 11 name cVerQueryValue+'A',
    VerQueryValueW          index 14 name cVerQueryValue+'W',
    GetFileVersionInfoA     index  1 name cGetFileVersionInfo+'A',
    GetFileVersionInfoW     index  4 name cGetFileVersionInfo+'W',
    GetFileVersionInfoSizeA index  2 name cGetFileVersionInfoSize+'A',
    GetFileVersionInfoSizeW index  3 name cGetFileVersionInfoSize+'W';

    // ------

    type
    PCharToOemA = ^TCharToOemA;
    TCharToOemA = function (lpszSrc: LPCSTR; lpszDst: LPSTR): BOOL; stdcall;

    const
    cMyExeName = 'Project.exe';                                    // !
    OemFuncAddrFromDebuger : PCharToOemA = Pointer($005e0dac);     // !!!

    var
    OriginalCharToOemA : TCharToOemA;

    function MyCharToOemA(lpszSrc: LPCSTR; lpszDst: LPSTR): BOOL; stdcall;
    begin
    OutputDebugString('Hi, MyFunc In Work!');
    ////
    // My Code                                                     // ???
    ////

    // Result := ;

    Result := OriginalCharToOemA(lpszSrc, lpszDst);                //     del
    if (lpszSrc <> nil) and (lpszDst <> nil) then                  //     del
      if (lpszSrc^<>#0) then lpszDst^ := '!';                      // ;)  del
    end;

    procedure DllMain(Reason: Integer);
    var
    s :String;
    begin
       case Reason of
         DLL_PROCESS_ATTACH:
         begin
           SetLength(s, 1024);
           SetLength(s, GetModuleFileName(0, PChar(s), Length(s)));
           OutputDebugString(PChar(s));
           if (Pos(cMyExeName, s) > 0) then begin                  //
             OriginalCharToOemA := OemFuncAddrFromDebuger^;
             OemFuncAddrFromDebuger^ := @MyCharToOemA;
             OutputDebugString('Ok, MyFunc SetUp!');
           end;
         end;
         //
         DLL_PROCESS_DETACH: OriDllFree;
       end;
    end;

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


    В строке 122 указывается название программы – для простой самопроверки,
    а в 123 адрес, найденный в отладчике адрес


    005C9B66 E881AFE4FF  call CharToOemA ->   00414AFC FF25B40D5E00     jmp dword ptr [$005e0dac]  => // $005e0dac
    или
    00411D25  call        dword ptr [__imp__CharToOemA@8 (041A0B4h)]                               => // $0041A0B4  

  • DayGaykin © (10.08.16 22:43) [9]

    unit Unit1;

    interface

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

    type
     TForm1 = class(TForm)
       procedure FormCreate(Sender: TObject);
     private
       { Private declarations }
     public
       { Public declarations }
     end;

    var
     Form1: TForm1;

    implementation

    {$R *.dfm}

    type
     PPatch = ^TPatch;
     TPatch = packed record
       A, B: DWORD;
       B8: Byte;
       lstrcpy: DWORD;
    //    E: Word;
       C: DWORD;
       D: DWord;
     end;
     PRemoteDisableCharToOemParam = ^TRemoteDisableCharToOemParam;
     TRemoteDisableCharToOemParam = record
       strCharToOemA: string[11];
       strUser32_dll:  string[11];

       GetProcAddress: function (hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall;
       GetModuleHandle: function (lpLibFileName: PChar): HMODULE; stdcall;
       VirtualProtect: function (lpAddress: Pointer; dwSize, flNewProtect: DWORD;  var OldProtect: DWORD): BOOL; stdcall;

       Patch: TPatch;
     end;

    procedure RemoteDisableCharToOem(P: PRemoteDisableCharToOemParam); stdcall;
    var
     H: THandle;
     Ptr: PPatch;
     D: DWORD;
    begin
     // Функция заменяет код CharToOemA на вызов lstrcpyA
     H := P.GetModuleHandle(@P.strUser32_dll[1]);
     Ptr := P.GetProcAddress(H, @P.strCharToOemA[1]);
     P.VirtualProtect( Ptr, SizeOf(P.Patch), PAGE_EXECUTE_READWRITE, D);
     Ptr^ := P.Patch;
    end;

    procedure DisableCharToOem(ProcessHandle: THandle);
    var
     Param: TRemoteDisableCharToOemParam;
     L: THandle;
     RemPtr: Pointer;
     RemCB: Pointer;
     W: Cardinal;
     Thread: THandle;
    begin
     L := GetModuleHandle(kernel32);
     Param.Patch.A := $042474FF;
     Param.Patch.B := $0C2474FF;
     Param.Patch.B8 := $B8;
     Param.Patch.C := $C883D0FF;
     Param.Patch.D := $0008C2FF;
     Param.strUser32_dll := 'user32.dll'#0;
     Param.strCharToOemA := 'CharToOemA'#0;

     // Эти функции имеют одинаковый адрес во всех процессах
     Param.GetProcAddress := GetProcAddress(L, 'GetProcAddress'); // Почему-то тут нет буквы A
     Param.GetModuleHandle := GetProcAddress(L, 'GetModuleHandleA');
     Param.VirtualProtect := GetProcAddress(L, 'VirtualProtect');
     Param.Patch.lstrcpy := DWORD(GetProcAddress(L, 'lstrcpyA'));

     // Выделяем память в удаленном процессе
     RemPtr := VirtualAllocEx( ProcessHandle, nil, 2048, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
     Win32Check(RemPtr <> nil);
     try
       RemCB := Pointer(NativeUInt(RemPtr) + SizeOf(Param));

       // Копируем туда код и патч
       Win32Check(WriteProcessMemory(ProcessHandle, RemPtr, @Param, SizeOf(Param), W));
       Win32Check(WriteProcessMemory(ProcessHandle, RemCB, @RemoteDisableCharToOem, 2048 - SizeOf(Param), W));

       // Запускаем патч
       Thread := CreateRemoteThread(ProcessHandle, nil, 0, RemCB, RemPtr, 0, W);
       Win32Check(Thread <> 0);
       try
         // Ждем завершения
         Win32Check(WaitForSingleObject(Thread, INFINITE) = WAIT_OBJECT_0);
       finally
         Win32Check(CloseHandle(Thread));
       end;
     finally
       // Освобождаем удаленную память
       Win32Check(VirtualFreeEx(ProcessHandle, RemPtr, 0, MEM_RELEASE) <> nil);
     end;

    end;

    function WinToDos(ASource: AnsiString): AnsiString;
    begin
     SetLength(Result, Length(ASource));
     CharToOem(PChar(ASource), @Result[1]);
    end;

    procedure TForm1.FormCreate(Sender: TObject);
    begin
     // Тестируем до
     ShowMessage(WinToDos('Тест'));

     // Патчим текущий процесс, но можно пропатчить любой,
     // полученный из CreateProcess или OpenProcess.
     DisableCharToOem(GetCurrentProcess);

     // Тестируем после
     ShowMessage(WinToDos('Тест'));
    end;

    end.
  • DayGaykin © (17.08.16 14:48) [10]
    Писал-писал, и без ответа:(
  • KSergey © (26.08.16 09:00) [11]
    Есть еще вариант: изменить формат в базе.
  • имя (15.09.16 19:14) [12]
    Удалено модератором
 
Конференция "WinAPI" » Хук WinAPI функции в определенном приложении
Есть новые Нет новых   [118456   +51][b:0][p:0.002]