Если нельзя, но очень хочется...

Бывают ситуации, когда есть софт и хочется в него добавить какую-нибудь очень нужную функцию, не хватающую для полного счастья, но вот незадача ни исходников, ни реализованной системы плагинов в программе нет. Как же быть, смириться и забыть или пойти через гремучий лес и воплотить свою мечту. Вот об одном из возможных путей решения и пойдет речь далее.

Все, о чем будет написано ниже, не является инструкцией к действию, а всего на всего маленькое сумасшествие из раздела «зачем это надо». В качестве испытуемого возьмем стандартный Notepad. Для примера реализуем в блокноте конвертацию выделенного текста в hex вид, добавив дополнительный пункт в меню – “String to Hex”.

Реализация задуманного.

Функцию конвертации реализуем в отдельной динамической библиотеке (dll). Для её загрузки в процесс notepad.exe можно использовать разные способы, один из таких – правка таблицы импорта и добавление в неё нашей библиотеки. Правку таблицы можно легко осуществить, например LoadPE (PE Editor -> Directories -> Import Table). Добавим импортируемую функцию, обозвав ее именем InitPlugin и укажем имя библиотеки, в которой реализуется данная функция, например plugin.dll. Сохраняем блокнот с исправленной таблицей импорта. Теперь при запуске блокнот будет требовать наличие plugin.dll с экспортируемой функцией InitPlugin. На этом пытки бинарника блокнота прекращаем и переходим к реализации нашей динамической библиотеки.

Создадим шаблон будущей библиотеки:

library plugin;
uses Windows, Messages, SysUtils;
procedure InitPlugin(); stdcall; begin
end;
procedure DLLEntryPoint(Reason: DWORD); begin case Reason of Dll_Process_Attach: begin
end; Dll_Process_Detach: begin
end; end; end;
exports InitPlugin;
begin if @DllProc = nil then begin DllProc:= @DLLEntryPoint; DllEntryPoint(Dll_Process_Attach); end; end.

Добавим в него функцию конвертации строки в шестнадцатеричное представление:

function StrToHexStr(Str: String): String;
var
  i: Integer;
const
  Hex = '0123456789ABCDEF';
begin
  Result:= '';
  for i:= 1 to Length(Str) do
    Result:= Result + Hex[Str[i] shr $4 + 1] + Hex[Str[i] and $0f + 1]
end;

И функцию, выполняющую обратное преобразование hex строки в обычную строку:

function HexStrToStr(Str: String): String;
var
  HexValue: Integer;
begin
  while (Length(Str)>0) do begin
    HexValue:= StrToInt('$' + Copy (Str, 1, 2));
    Delete(Str, 1, 2);
    Result:= Result + Chr (HexValue);
  end;
end;

Следующим шагом получим идентификаторы элемента управления Edit и меню Notepad. Но тут есть один момент, при загрузке plugin.dll контролы еще не инициализированы, и получить их указатели не получится, поэтому прибегнем к небольшой хитрости и создадим новый тред, в котором через 100 мс вызовем инициализацию нашего plugin’a. Таким образом код модуля будет выглядеть так:

var
  hThread: DWORD;
procedure DLLEntryPoint(Reason: DWORD); begin case Reason of Dll_Process_Attach: begin hThread:= CreateThread(nil, 0, @InitPlugin, nil,0,DWORD(nil^)); end; Dll_Process_Detach: begin CloseHandle(hThread); end; end; end;

Что бы обрабатывать выбранные пункты меню, и вызывать соответствующие обработчики событий, нам надо обрабатывать сообщение WM_COMMAND, а для этого придется подменить процедуру WinProc основного окна, делается это при помощи вызова SetWindowLong с параметром GWL_WNDPROC. Для всех сообщений, которые мы не будем обрабатывать нужно вызывать оригинальный обработчик через вызов CallWindowProc :

function WindowProc(hDlg, uMsg, wParam, lParam: DWORD): LongInt; stdcall;
begin
  case uMsg of
    WM_COMMAND: begin
      Result:= 0;
      case LOWORD(wParam) of
        IDC_StrToHex: begin StrToHex; Exit; end;
        IDC_HexToStr: begin HexToStr; Exit; end;
      end;
    end;
    WM_DESTROY: begin
      DestroyMenu(hSubMenu);
    end;
  end;
  Result:= CallWindowProc(wpOrigProc, hDlg, uMsg, wParam, lParam);
end;

procedure InitPlugin(); stdcall; begin sleep(100);
hMainDlg:= FindWindow('Notepad', nil); hEdit:= FindWindowEx(hMainDlg, 0, 'Edit', nil);
wpOrigProc:= Pointer(GetWindowLong(hMainDlg, GWL_WNDPROC));
SetWindowLong(hMainDlg, GWL_WNDPROC, Cardinal(@WindowProc));
// Insert Menu plugin's
hMainMenu:= GetMenu(hMainDlg); hSubMenu:= CreatePopupMenu;
AddItemMenu(hMainMenu, hSubMenu,'&Plugins', 4); AddItemPopur(hSubMenu,'String -> Hex', IDC_StrToHex); AddItemPopur(hSubMenu,'Hex -> String', IDC_HexToStr);
RedrawWindow(hMainDlg, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_UPDATENOW); end;

Добавление пунктов меню делаем через InsertMenuItem, назначая “Srting -> Hex” и “Hex -> String” индексы IDC_StrToHex и IDC_HexToStr соответственно.

Извращения прилогаются (notepad win7 ×86):

Скачать: Patched notepad + plug-in (bin, src) [ 144,2kB] загрузок: 336


Комментарии

  1. Maestro · 19 Ноябрь 2015, 20:38 · #

    Алексей привет! Очень интересный материал. Было бы интересно почитать про внедрение в код андроид apk, с дезассемблированием, дебагом и обратной сборкой apk!

Комментарии