Dll inject в приложение VB. Заполнение формы чужого приложения написанного на VB.

В предыдущем посте я писал про то, как сделать Proxy Dll. Теперь давайте рассмотрим аспект применения такой dll. Мне была поставлена задача заполнить форму приложения, написанного на VB, своими значениями. Исходники к приложению давно утеряны, а значит это как раз случай proxy dll. В качестве dll была выбрана библиотека интерпретатора бейсика msvbvm60. Пример проекта для изучения Вы можете взять => тут <=.

1. Запустим программку из предыдущего поста и сформируем fake.pas с функциями проксируемой dll.

2. Создадим проект dll в Delphi (или Lazarus) с названием msvbvm60.

3. Так как форма должна заполняться после нажатия определенной кнопки в целевой программе, нам надо это нажатие отловить и передать управления своей программе. Адрес процедуры, которая обрабатывает нажатие кнопки, вы можете найти в отладчике. После того, как мы узнали адрес, пишем в наш проект dll:

В верхнюю часть:


uses
Windows,
Messages,
CommCtrl,
DateUtils,
SysUtils,
fake in 'fake.pas'; // Наша proxy dll, вызывающая функции основной dll
var Addr: DWORD = $43FCF9; // Адресс процедуры, которая обрабатывает нажатие клавиши
tByte: DWORD; // Нужно для работы программы

В нижнюю часть:


procedure Click;
begin
end;
procedure EntryPoint;
begin
asm
call [eax+$724] // Этот кусок кода выдран из целевой программы. Он выполняет то, что мы затерли нашим jmp
pushad // Запоминаем состояние регистров чтобы не нарушить работу программы, потому что следующая команда запишет в регистры свои значения
end;
BeginThread(nil,0,@Click,nil,0, tByte); // Запускаем программу заполнения полей
asm
popad // Восстанавливаем значение регистров
push Addr // И отдаем управление целевой программе, следующей инструкции за нашей командой jmp EntryPoint
add [esp], 6
end;
end;
begin
VirtualProtect(Pointer(Addr), 6, PAGE_EXECUTE_READWRITE, tByte); // Снимаем защиту с секции кода для патча целевой программы
// Записываем в программу jmp EntryPoint, это точка входа для старта заполнения формы.
// Этими командами мы затерли оригинальную инструкцию CALL, которая обрабатывает нажатие клавиши в целевой программе.
// Затираемую инструкцию мы выполним после.
pbyte (Addr)^ :=$90;
pbyte (Addr+1)^:=$E9;
pdword(Addr+2)^:=dword(@EntryPoint)-Addr-6;
VirtualProtect(Pointer(Addr), 6, tByte, tByte); // Восстанавливаем защиту
end.

4. Теперь все готово чтобы заполнить нашу форму в целевой программе. Дописываем процедуру Click


// Функция для подстановки значений, перебирает хендлы родительского окна
function EnumWindowsProc(WHandle: HWND; lpParam: LPARAM): BOOL; export; stdcall;
var Text1, Text2: array [0..256] of Char;
tt: TSystemTime; // Нужно для заполнения DateTimePicker
mytt: TDateTime;
begin
GetClassName (WHandle, Text1, 255); // Получаем имя класса окна
GetWindowText(WHandle, Text2, 255); // Получаем текст окна
Result:=True; // Возвращаем истину, чтобы не останавливать обработку команд
end;
procedure Click;
begin
Sleep (100); tByte:=0; // Ждем когда отрисуется полностью форма, которую нужно заполнить нашими значениями, и она будет готова для того чтобы принимать значения
EnumChildWindows(FindWindow (nil,'TestWindow'), @EnumWindowsProc, 0); // Получаем Handle заполняемого окна по его заголовку и перебираем дочерние хендлы для заполнения значениями
EndThread(0); // Заканчиваем работу этой процедуры
end;

5. А дальше уже идет непосредственно само заполнение, весь код пишем в функцию EnumWindowsProc:


// Меняем значение текстового поля со значением Текст 1 на Текст 2:
if Text2='Текст 1' then begin
SendMessage(WHandle, WM_SETTEXT,0,LParam(PChar('Текст 2')));
end;
// Нажимаем на кнопку
if Text2='Кнопка' then begin
PostMessage(WHandle, BM_CLICK, 0, 0);
end;
// Текстовое поле TextBox с обработчиком заполнения
if (Text1='ThunderRT6TextBox') and (Text2='Поле с обработчиком') then begin
SendMessage(WHandle, WM_SETTEXT,0,LParam(PChar('Наше значение')));
PostMessage(WHandle, $100E,$A, 0); // Сообщаем форме что поле изменилось, чтобы запустить обработчик, значения подсмотрены в программе Microsoft Spy++
end;
// Заплняем поля, в которых вводятся даты. Использована компонента DateTimePicker
// Полей на форме две штуки. Первое заполняем текущей датой, второе Текущей датой + Год и минус один день
if Text1='DTPicker20WndClass' then begin
DateTime_GetSystemTime(WHandle, tt);
// Заполняем поле 2
if tByte = 0 then begin
mytt:=IncYear(Date, 1);
mytt:=IncDay (mytt,-1);
end else
// Заполняем поле 1
mytt:=Date;
DateTimeToSystemTime(mytt, tt);
DateTime_SetSystemTime(WHandle, GDT_VALID, tt);
inc (tByte);
end;

Отдельно стоит рассказать о заполнение ComboBox в приложении VB, так как он состоит из списка и текстового поля. Предположим нам надо выбрать последний элемент из списка. Дописываем в функцию EnumWindowsProc:


// Заполняем СоmboBox
if Text1='ThunderRT6ComboBox' then begin
// Выбираем последний элемент
SendMessage(WHandle, CB_SETCURSEL, SendMessage(WHandle, CB_GETCOUNT, 0, 0)-1, 0);
// Сообщаем форме что значение ComboBox изменилось
EnumChildWindows(FindWindow ('#32769',nil), @EnumWindowsProc1, WHandle);
end;

И соответственно пишем выше EnumWindowsProc1:


// Обработка ComboBox. Сообщаем форме что значение ComboBox изменилось
function EnumWindowsProc1(WHandle: HWND; lpParam: LPARAM): BOOL; export; stdcall;
var Text1: array [0..256] of Char;
begin
GetClassName (WHandle, Text1, 255);
if Text1='ComboLBox' then begin
SendMessage(lpParam, WM_COMMAND, $103E8, WHandle); // Сообщаем форме что поле изменилось, чтобы запустить обработчик, значения подсмотрены в программе Microsoft Spy++
end;
Result:=True;
end;

На этом обработку формы закончили, так как все поля заполнены.

Добавить комментарий

Ваш e-mail не будет опубликован.