Blog. Just Blog

Управление другим приложением из своей программы

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Под управлением сторонним приложением я подразумеваю некие действия своей программы, эмулирующие действия живого человека. Например, нажатия на кнопки в диалоговых окнах. Попробуем сделать это на примере лоадера для популярного файлового менеджера Total Commander. Кто пользуется им, тот знает, что единственное различие между полной и незарегистрированной версией в том, что триалка при запуске показывает наг-скрин с предложением купить программу или нажать для продолжения одну из трех кнопок с цифрами. Конечно, кнопку можно нажать самостоятельно, но можно доверить это лоадеру.

Наг-скрин Total Commander
Наг-скрин Total Commander

Чтобы работать с содержимым окна, сперва надо узнать его хэндл. Проще всего воспользоваться функцией FindWindow, указав в качестве параметра наименование класса наг-скрина. Его можно посмотреть при помощи моей программы WinDowzer или любой другой аналогичной программы. Противное окно имеет название класса TNASTYNAGSCREEN, первый шаг сделан. Теперь нам надо узнать хэндл нужной кнопки, для этого нужно изучить все связи родительских и дочерних объектов окна.

Иерархический список хэндлов всех элементов нужного окна можно получить при помощи последовательного вызова функции FindWindowEx или же рекурсивного вызова функции EnumChildWindows. Вот что у меня получилось:

TNASTYNAGSCREEN - Total Commander
  TNotebook
    TPage - NagPage
      TPanel
        TPanel - 3
        TButton - &3
        TButton - &2
        TButton - &1
      TPanel - 3
      TButton - &3
      TButton - &2
      TButton - &1
      TButton - &Registration info
      TButton - Program &information
    TPanel
      TPanel - 3
      TButton - &3
      TButton - &2
      TButton - &1
    TPanel - 3
    TButton - &3
    TButton - &2
    TButton - &1
    TButton - &Registration info
    TButton - Program &information
    TPage - StartupPage
      TPanel
    TPanel
    TPage - UserPage
      TButton - &Update Information
    TButton - &Update Information
  TPage - NagPage
    TPanel
      TPanel - 3
      TButton - &3
      TButton - &2
      TButton - &1
    TPanel - 3
    TButton - &3
    TButton - &2
    TButton - &1
    TButton - &Registration info
    TButton - Program &information
  TPanel
    TPanel - 3
    TButton - &3
    TButton - &2
    TButton - &1
  TPanel - 3
  TButton - &3
  TButton - &2
  TButton - &1
  TButton - &Registration info
  TButton - Program &information
  TPage - StartupPage
    TPanel
  TPanel
  TPage - UserPage
    TButton - &Update Information
  TButton - &Update Information

В списке для удобства я записал название класса и текст дочерних окон. Казалось бы, в момент перебора надо просто "нажать" нужную кнопку. Но здесь не все так просто, так как каждый раз выбирается случайный номер кнопки, и если нажать неправильную, то Total Commander просто завершит работу. Значит нам еще надо узнать правильный номер кнопки, прежде чем "нажать" на нее. Обратите внимание, что номер кнопки повторяется в форме несколько раз, и длина строки (то есть цифры) - всегда один символ. Облегчим себе задачу и рекурсивно переберем все дочерние элементы наг-скрина, а строку длиной один символ будем считать правильным номером.
  1. ; Секция данных
  2. button   db '&',0,0
  3. buff     rb 3
  4.  
  5. ; Секция кода
  6.         ...
  7.         invoke  EnumChildWindows,[nag],FindNastyNumber,NULL
  8.         ...
  9.  
  10. proc FindNastyNumber hwnd:dword,lParam:dword
  11.         mov     [buff],0
  12.         invoke  GetWindowText,[hwnd],buff,100
  13.         invoke  lstrlen,buff
  14.         cmp     eax,1
  15.         jne     @f
  16.         invoke  lstrcpy,button+1,buff
  17. @@:
  18.         invoke  EnumChildWindows,[hwnd],FindNastyNumber,NULL
  19.  
  20.         mov     eax,1
  21.         ret
  22. endp
Нужный нам номер в виде строки теперь записан в переменную button, причем именно так, как он написан на кнопке, то есть "&3". Нам осталось найти хэндл самой кнопки и "нажать" на нее. Воспользуемся точно таким же трюком, рекурсивно пройдемся по всем дочерним элементам и кликнем по всем найденным элементам, текст на которых совпадает с искомым номером кнопки.

Лирическое отступление. Наиболее правильная эмуляция клика на каком-либо элементе окна выглядит следующим образом:
  1.         invoke  SendMessage,[hwnd],WM_SETFOCUS,NULL,NULL
  2.         invoke  SendMessage,[hwnd],WM_LBUTTONDOWN,MK_LBUTTON,NULL
  3.         invoke  SendMessage,[hwnd],WM_LBUTTONUP,MK_LBUTTON,NULL
Возвращаемся к перебору дочерних элементов наг-скрина.
  1.         ...
  2.         invoke  EnumChildWindows,[nag],ClickNastyButton,NULL
  3.         ...
  4.  
  5. ; Рекурсивная функция перебора всех дочерних элементов
  6. proc ClickNastyButton hwnd:dword,lParam:dword
  7.         mov     [buff],0
  8.         ; Прочитать текст дочернего элемента
  9.         invoke  GetWindowText,[hwnd],buff,3
  10.         ; Это текст искомой кнопки?
  11.         invoke  lstrcmp,buff,button
  12.         or      eax,eax
  13.         jnz     @f
  14.  
  15.         ; Эмулировать нажатие на кнопку
  16.         invoke  SendMessage,[hwnd],WM_SETFOCUS,NULL,NULL
  17.         invoke  SendMessage,[hwnd],WM_LBUTTONDOWN,MK_LBUTTON,NULL
  18.         invoke  SendMessage,[hwnd],WM_LBUTTONUP,MK_LBUTTON,NULL
  19. @@:
  20.         invoke  EnumChildWindows,[hwnd],ClickNastyButton,NULL
  21.  
  22.         mov     eax,1
  23.         ret
  24. endp
Вот и все. Осталось приделать к лоадеру функцию запуска программы, чтобы наг-скрин закрывался сразу же после старта Total Commander, но это уже к делу не относится. В приложении пример программы, находящей и закрывающей наг-скрин уже запущенного Total Commander.

Примеры программы с исходным текстом (FASM)Примеры программы с исходным текстом (FASM)

Apptication.Control.Demo.zip (1,725 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 10606 | Комментариев: 7

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
addhaloka (01.06.2017 в 04:43):
В своё время немного заморочился с этим - недостаток Sleep'ов - зависимость от конфига ПК. в итоге сделал так, но всё-равно костыль:
...
invoke CreateProcess,curdir,buffer,0,0,FALSE,0,0,0,sinfo,pinfo
test eax,eax
je  procfail
loc_nag:
invoke WaitForInputIdle,[pinfo.hProcess],-1
xor  esi,esi
mov  esi,6
@@:
invoke Sleep,500
invoke FindWindow,sznag,0
test eax,eax
jne  @F
dec  esi
jne  @B
jmp  exit ; выход, если наг не найден (если случайно запущен крякнутый сабж или с ключом)
@@:
mov  ebx,eax
...
Лучше, имхо, делать через FindWindowEx - в паре похожих лоадеров для других прог прекрасно работает такой цикл:
invoke  ShellExecute,0,0,rdate,param,0,10
mov     esi,1000000
@@:
invoke  FindWindow,nag,0
or      eax,eax
jne     @F
dec     esi
jne     @B
jmp     loc_exit
@@:
mov     [hNAG],eax
@@:
invoke  ShowWindow,[hNAG],SW_HIDE
or      eax,eax
je      @B
@@:
invoke  FindWindowEx,[hNAG],0,tedit,0
or      eax,eax
je      @B
invoke  GetWindow,eax,GW_HWNDLAST
mov     [hID],eax
...
Т. е. достаточно счётчика, без Sleep'ов, но с EnumChildWindows так почему-то не хочет работать. Попробовал  здесь сделать с FindWindowEx - сходу не получилось, глубже разбираться пока что лень. :)
SendMessageA (09.01.2017 в 22:30):
Если хотите еще и запускать лоадером, то добавьте Sleep'ы, чтобы наг-скрин успел сформировать элементы своего окна:
totCom   db 'D:\TOTALCMD\TOTALCMD64.EXE',0
sinfo    STARTUPINFO
pinfo    PROCESS_INFORMATION
section '.code' code readable executable
start:
        invoke  CreateProcessA,0,totCom,0,0,0,0,0,0,sinfo,pinfo
@@:
        invoke  Sleep,20
        invoke  FindWindow,szNag,NULL
        or      eax,eax
        jz       @b
        mov     [nag],eax
        invoke  Sleep,200
Ret (28.12.2012 в 18:10):
а я вспомнил про AutoHotkey)
www.autohotkey.com
vox13 (21.02.2012 в 21:37):
Согласен с автором: одно дело скрипт, а другое самому на асме замутить. Тем более можно применить для разных программ, не только для TC.
ManHunter (13.02.2012 в 10:46):
Ну то s0m, а тут Ассемблер, все-таки разные вещи. Простой кликер\лоадер - да, можно на и на s0m нарисовать, а когда требуется управлять из своей программы какой-нибудь другой, то придется применять уже такие методы. К тому же s0m очень не любят многие антивири.
semenov (13.02.2012 в 09:53):
Тоже сразу вспомнил про Sign 0f Misery http://s0m.narod.ru/
RedElf (13.02.2012 в 01:26):
можно воспользоваться готовым решением типа SignOfMisery

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

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2025
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0077 сек.) / Память: 4.5 Mb
Наверх