Невидимый TeamViewer. Часть первая

Часть первая

Введение

TeamViewer отличная программа для удалённого управления компьютером. У программы нет скрытого режима т.е пользователь всегда видит когда к нему подключились и может разорвать соединение.
В этой статье пойдёт речь как реализовать такой режим превратив TeamViewer в "программу-шпион". Существует несколько версий TeamViewer под разные платформы. Дальше речь пойдёт о версии под Windows под названием QuickSupport. Я выбрал её потому что она меньше полной версии т.к в неё нет клиентской части (т.е можно подключаться только к ней, с неё нельзя), она представляет собой простой exe файл тогда как полная может работать как сервис, не требует установки и админ-прав. В частности у меня версия 10.0.41495.0 Моя копия ТУТ

TeamViewer работает следующим образом. Вы запускаете его на компьютере которым хотите удалённо управлять, он проверяет наличие интернета и если всё ок то генерирует уникальный номер "ID" и временный пароль. Дальше со второго компьютера или телефона/планшета вы запускаете полную версию TeamViewer вводите номер "ID"/пароль первого компьютера и после подключения видите рабочий стол, можете управлять указателем мыши, открывать/закрывать окна - всё как буд-то вы непосредственно находитесь за компьютером. При этом на первом компьютере справа внизу появляется окно в котором видно кто подключился, есть возможность закрыть подключение, открыть окно чата и прочие функции. Всё хорошо работает, программа не требовательна к ресурсам, не нагружает компьютер, не виснет/не вылетает - явно сделана качественно. Бесплатная для некоммерческого использования. В этом случае при закрытии соединения появляется окно "Завершён бесплатный сеанс.." и иногда ещё одно рекламное окно но это мелочи.. В общем пользовать легко, удобно и надёжно. Но если хочется последить за кем-то то эти окна не кстати. О том как скрыть все окна и пойдёт речь. В результате хочется получить TeamViewer который бы запускался при включении компьютера работал не заметно и не выводил никаких окон. Программа коммерческая надеяться на исходные коды не приходится. Поэтому ниже я составил примерный план.

План:

  1. дизассемблировать приложение и модифицировать так что бы окна не появлялись или были не видны
  2. найти код отвечающий за генерацию или отображение ID/пароля и сделать перехват
  3. написать DLL которая будет подключаться к исполняемому файлу и после перехвата ID/пароля отправлять их на сервер (см.следующий пункт)
  4. написать Web сервис который будет получать и сохранять ID/пароль
  5. написать приложение которое будет устанавливать модифицированный TeamViewer и прописывать в автозагрузку
  6. скрыть модифицированный TeamViewer в диспетчере задач. Скажу сразу этот пункт я не делал

Инструменты:

  1. OllyDbg - дизассемблер и отладчик. У меня версия 2.1.0.4. Скачать тот что у меня ТУТ.
  2. Эту версию я не помню где скачал, точно не с официального сайта но она оказалась самой стабильной. (пробовал несколько версий с официального сайта). Был в комплекте хелп но я его удалил. Это мой первый опыт использования OllyDbg до этого пользовался другим но он отказался запускать TeamViewer. Единственное что нужно сделать в OllyDbg это в опциях в Debugging - Exceptions надо поставить галки игнорировать исключения и добавить игнорировать диапазон 00000000..FFFFFFFF. Совет: что бы OllyDbg меньше подвисал лучше при отладке TeamViewer отключать интернет и включать только когда нужно проверить подключение (я запускал на виртуальной машине поэтому проблем включить/выключить сеть не было).
  3. Visual Studio. У меня версия Ultimate 2013. DLL из плана п.2 буду делать на С++, Web сервис на C#, и приложение из п.5 тоже на C++
  4. HEdit 2.1 - это простой HEX-редактор. Скачать тот что у меня ТУТ.
  5. Spy++ он входит в Visual Studio. С помощью него я смотрел какие сообщения отправляются окнам что бы потом искать вызовы в отладчике
  6. Process Monitor/Process Explorer - для изучения активности приложения. В частности я смотрел куда и что TeamViewer пишет в реестр, какие процессы создаёт/запускает, какие порты открывает..

Убираем Warning:

И так у нас есть файл TeamViewerQS.exe. Это просто контейнер. После запуска он создаёт в директории TEMP папку "TeamViewer" копирует из своих ресурсов кучу файлов в том числе и TeamViewer.exe и запускает его. Это легко определить с помощью Process Explorer или если просто включен UAC (вылазит UAC Prompt: Do you want to allow following programm to make changes.. и в нём есть путь к Temp. Только странно если нажимаю НЕТ то программа всё равно запускается и так же работает. Windows 8.1)
Для нормальной работы достаточно оставить 4-е файла (выяснил экспериментально), остальные удаляем :

  • TeamViewer.exe
  • TeamViewer_Desktop.exe
  • TeamViewer_Resource_en.dll
  • TeamViewer_StaticRes.dll

В этом случае при запуске начинает появляться сообщение: Начнём с того что уберём это сообщение. Запускаем OllyDbg и открываем TeamViewer.exe. Если посмотреть на это сообщение в Spy++ то видно что это диалог - стандартное окно с именем класса '#32770'. Такие диалоги создаются WinAPI функцией MessageBox (В нашем случае unicode-версией MessageBoxW) из библиотеки User32.dll. Запускаем TeamViewer.exe в OllyDbg и ждём появления этого сообщения, затем в OllyDbg открываем окно Expression (Ctrl+G или через контекстное меню Go To -> Expression) и набираем в поле Enter address expression: USER32.MessageBoxW. В поле Matching labels нужно выбрать "USER32.MessageBoxW" и нажать Follow label. OllyDbg перейдёт в библиотеку User32.dll на адрес функции MessageBoxW. Осталось поставить точку останова (F2) на этот адрес и перезапустить (Restart Ctrl+F2). Когда сработает точка останова нас интересует адрес откуда был вызов - он будет на вершине стека (выделен красным). Нажимаем правой кнопкой на этом адресе и выбираем Follow in disassembler (или просто Enter). Будет выполнен переход назад в модуль TeamViewer на строку отмеченную красным на следующем рисунке. Предыдущей инструкцией будет Call - вызов USER32.MessageBoxW. Если посмотреть ещё чуть выше там будет инструкция условного перехода JNZ. Это проверка результата выполнения вышестоящей функции: если в регистре AL будет не 0 то будет переход и не будет выполнен код вызывающий сообщение. Скорее всего функция которая выше JNZ загружает какие-то библиотеки из тех что удалили, у неё это не получается и она возвращает 0 в результате переход JNZ не выполняется и появляется сообщение. Можно поставить точку останова на JNZ вернуть удалённые файлы и проверить реакцию программы. Всё что остаётся это поменять инструкцию JNZ на JMP - безусловный переход, т.е независимо от того как завершится функция код сообщения выполняться не будет. Что бы в OllyDbg заменить инструкцию нужно установить курсор на неё и нажать пробел. Откроется окно Assemble с полем редактирования в котором JNZ нужно заменить на JMP. После нужно записать изменения в исполняемый файл: окно CPU -> контекстное меню -> Edit -> Copy all modifications to executable -> откроется ещё одно окно -> контекстное меню -> Save file.. -> Yes -> Yes Перед сохранением изменений OllyDbg делает копию с расширением bak если сравнить её с изменённым файлом будет видно что отличается всего один байт - это как раз код JNZ / JMP Если запустить модифицированный файл то сообщение не появится - лекарство оказалось верным.

Убираем "No connection":

Выше я писал что лучше отключать интернет во время отладки TeamViewer - так OllyDbg меньше виснет но в этом случае появляется следующий диалог: Диалог более сложный с иконкой с кнопками с чек-боксом - явно не стандартный, такой диалог надо делать руками. Обычно созданные диалоги хранят в ресурсах самой программы или внешней dll. Просмотреть ресурсы можно открыв exe/dll в Visual Studio. Я начал с файла TeamViewer_Resource_en.dll и уже в пятом по счёту диалоге (точнее шаблоне) узнал наш. Диалог создаётся из шаблона путём указания номера шаблона в нашем случае это 10088 или в шестнадцатеричном виде 0x2768. Поискав в OllyDbg константы (окно CPU -> контекстное меню -> Search for -> Constant..) я нашёл всего два вхождения "2768". Поставив на каждом точки останова я быстро нашёл нужное место. Как видно на рисунке константа 2768 помещается в стек инструкцией PUSH и чуть ниже идёт вызов функции User32.DialogBoxParamW. Что бы обойти это место нужно выполнить JMP который выше, а что бы попасть на этот JMP нужно что бы не сработал переход JNZ который ещё выше. Самый простой вариант заменить JNZ на JMP и поменять адрес перехода на 0162175B. В OllyDbg это делается просто: ставим курсор на JNZ, нажимаем пробел, открывается окно Assemble где меняем "JNZ SHORT TeamViewer.0162173A" на "JMP SHORT TeamViewer.0162175B" (Каждый раз будут разные адреса).
! Важно помнить что короткие переходы осуществляются не на конкретный адреc, а на смещение (т.е количество байт) относительно следующей инструкции. Это связано с тем что программа всегда загружается в разные адреса виртуальной памяти и заранее эти адреса не известны. Смещение указывается в операндах команд переходов. В данном случае OllyDbg сама высчитывает смещение. Так до изменения код команды JNZ был 75 OD, где 75 это собственно JNZ, а OD это операнд - смещение в байтах. После замены код меняется на EB 2E, где EB означает JMP, а 2E смещение высчитанное как адрес назначения 0x0162175B минус адрес следующей за JNZ инструкции 0x0162172D. В результате переход будет на плюс 2Е байт (46 в десятичной системе счисления) от адреса следующей за JMP инструкции
Всё, записываем изменения в исполняемый файл и окно "No connection" больше не появится.

Убираем HTA-окно:

После закрытия TeamViewer появляется вот такое окно: Самое интересное что сам TeamViewer закрывается, а окно продолжает существовать. Сначала я подумал что TeamViewer запускает какое-то своё отдельное приложение через WinAPI функции как-то: CreateProcess или ShellExecute но посмотрев в ProcessExplorer (и погуглив) понял в чём дело Как видно TeamViewer запустил mshta.exe с одним параметром - путь к файлу 7.hta.
Вот ссылка где всё понятно написано:

Начиная с Internet Explorer 5.0 появилась возможность создавать HTML-приложения с любым пользовательским интерфейсом при помощи JavaScript или VBScript. Причем, созданное html-приложение будет запускаться вне браузера и ничем не будет отличаться от стандартных Windows-приложений.
Данные html-приложения должны иметь расширение *.hta.
HTA-файлы обрабатывается программой mshta.exe, задача которой - обеспечить связь вашей программы с браузером.

Будем искать по тексту "mshta.exe". Запускаем TeamViewer в OllyDbg, даём ему запуститься что бы он загрузил все библиотеки и полностью инициализировался затем ищем ссылки на строку (окно CPU -> контекстное меню -> Search for -> All referenced strings). Откроется окно со всеми ссылками на строки которые OllyDbg удалось найти, в этом окне ищем строку "mshta.exe" (контекстное меню -> Search for text (Ctrl+F)). Будет найдена всего одна ссылка на эту строку по двойному клику на ней переходим в модуль TeamViewer. Кстати, если отключить интернет то это окно не появляется. Я установил точку останова на начало функции но без интернета программа вообще не доходила до этой функции. Тогда я начал искать места откуда вызывается эта функция (окно CPU -> контекстное меню -> Find references to -> Selected command (Ctrl + R)) таких мест оказалось два т.е нужно было менять уже в двух местах.. в итоге я решил вернуться и менять непосредственно функцию в которой вызывается mshta.exe. В ней есть два перехода которые ведут в самый низ - очевидно это выход из функции. Заменим самый верхний из них JG (переход если больше) на безусловный JMP, а что бы всегда доходить до этого перехода чуть выше заменим JNZ на "пустую" инструкцию NOP. Т.к длинна новых команд меньше чем оригинальных OllyDbg сама заполняет пустоту NOP'ами.
До
После Записываем изменения в исполняемый файл и HTA-окно больше не будет появляться при выходе

Убираем SPONSORED SESSION:

После завершения соединения иногда выскакивает окно "Завершён бесплатный сеанс.. Благодарим вас за честность..". Конечно достойное уважения решение сделать TeamViewer бесплатным для некоммерческого использования но от этого окна так же придётся избавиться. Это такое же сообщение как "No connection" и выше я писал что было два вхождения константы 0x2768 вот второе это как раз это сообщение. Находим это место и видим что ниже идёт вызов WinAPI CreateDialogParam смотрим в msdn что функция принимает 5 параметров. Параметры в С/С++ передаются через стек поэтому отсчитываем 5 PUSH и полностью удаляем вызов этой функии заменив NOP'ами, затем первые два NOP'а заменяем на переход JMP на оригинальный код. Зачем нужен этот JMP когда программа по NOP'ам и так дойдёт до оригинального кода. Важный момент: в процессе загрузки exe/dll загрузчик меняет абсолютные адреса которые встречаются в коде в зависимости от фактического адреса по которому загрузчик загрузил базовый адрес exe/dll. Это называется "Базовые поправки", почитать можно например тут Форматы РЕ и COFF объектных файлов секция "Базовые поправки РЕ-файла".
Список адресов которые нужно поменять содержится в секции .reloc (можно посмотреть в OllyDbg в меню -> View -> Memory Map).
Сейчас на этом примере посмотрим как загрузчик меняет адреса. Мы заполнили всё NOP'ами код 0x90, записали в исполняемый файл, перезапускаем программу и смотрим на это место: Загрузчик внёс поправки в три места - это операнды инструкций PUSH и CALL. Какие именно адреса нужно поместить в секцию .reloc решает компоновщик (или компилятор, точно не знаю) на стадии разработки exe/dll. В этих трёх местах NOP'ы превратились в мусор и чтобы обойти его нужен JMP.

Убираем окно передачи файлов:

Это окно появляется когда подключаетесь для передачи файлов. Ищем диалог в ресурсах: Дальше находим константу в OllyDbg и устанавливаем точку останова. Включаем режим передачи файлов и ждём когда сработает точка останова. Следующий CALL будет вызов User32.CreateDialogParamW. В отладчике выполняем этот CALL без захода в функцию (F8) и смотрим в аккумулятор (регистр EAX) там должен быть дескриптор (Handle) только что созданного диалога. Теперь открываем окно Expression (Ctrl+G) вводим User32.ShowWindow. Так, потому что в msdn про CreateDialogParam сказано:

The function displays the dialog box if the template specifies the WS_VISIBLE style.
..
After CreateDialogParam returns, the application displays the dialog box (if it is not already displayed) using the ShowWindow function.

В шаблоне свойство VISIBLE = False (Можно посмотреть в Visual Studio в свойствах шаблона диалога) поэтому ожидаем что будет вызов ShowWindow.
Переходим в OllyDbg к User32.ShowWindow и в контекстном меню выбираем Breakpoint -> Conditional...(Shift+F2). Пишем [ESP+4]==ЗНАЧЕНИЕ_ИЗ_EAX это значит выполнить остановку когда второе DWORD слово от вершины стека (регист ESP это указатель стека) будет равно введённому значению (в нашем случае это дескриптор окна передачи файлов). Второе слово потому что первым всегда идёт адрес возврата из функции, затем параметры или адреса параметров передаваемые в функцию. Функция ShowWindow принимает два параметра: дескриптор окна (по которому мы установили условие остановки) и второй параметр это константа что делать с окном: показать/скрыть/свернуть и т.д. В нашем случае это значение 1 = SW_SHOWNORMAL Запускаем программу на дальнейшее выполнение и точка останова почти сразу срабатывает: Как видно в стеке вторым словом идёт введённый дескриптор, OllyDbg так же показывает имя класса шаблона, заголовок окна, понятное обозначение константы SW_SHOWNORMAL - всё информативно и понятно. Нас интересует адрес возврата, после перехода по нему оказывается в мест которое нужно править. Тут я поступил так же как с предыдущим окном - полностью затёр вызов ShowWindow, я не помню пробовал я менять SW_SHOWNORMAL на SW_HIDE и к какому результату это приводило. На этом с лёгкими окнами закончено. Наверняка остались ещё на у меня пока не вылазили. Осталось два окна: главное и то что появляется справа внизу при подключении но с ними чуть позже.

Делаем постоянный пароль

Меня не устраивает что пароль постоянно меняется, лучше бы он был константой. TeamViewer ID уникален поэтому думаю он формируется на сервере, а пароль генерируется локально поэтому достаточно найти эту функции и можно поменять.
Но тут возникли проблемы. Найти функцию в которой генерируется пароль не получалось. Если нажать на поле в котором выводится пароль то появляется кнопка которая открывает меню: "Создать новый случайный пароль" и "Скопировать в буфер обмена". Нужно было найти обработчик первой кнопки. Но это так же не получалось. Если бы это была обычная кнопка на форме можно было бы в OllyDbg посмотреть список окон (View - List of Windows), найти там кнопку, найти функцию класса, дальше искать обработчик конкретной кнопки, но меню это не окно и в списке его нет.. В общем я решил найти обработчик методом Brute-Force или полного перебора. В OllyDbg есть функция найти все распознанный процедуры, т.е она анализирует код видит вызовы/возвраты и по какому-то алгоритму определяет что этот кусок кода функция с n-параметрами. Список таких процедур-функций вызывается через контекстное меню -> Search for -> All found procedures. Дальше в этом списке есть возможность установить на все функции точки останова. У меня таких получилось 24198 штук. Дальше нужно было запускать программу на выполнение и снимать точки останова до тех пор пока программа перестанет останавливаться - т.е убирать лишние путём отсеивания. Затем нужно нажать на кнопку и где сработает одна из оставшихся точек останова там и будет обработчик. Минут через ~5 постоянного нажимания F9 (продолжить) - F2 (снять точку останова) я понял что делать это руками не вариант.
На Delphi я написал простую программу имитирующие нажатие F9/F2 в цикле.
Внешний вид приводить не буду - там всего одно поле ввода Edit1 - это для ввода дескриптора окна OllyDbg CPU в HEX-виде, чек-бокс (галка) "продолжить" для контроля цикла и кнопка которая запускает. В процессе добавил задержки что бы OllyDbg успевал обрабатывать "нажатия".

procedure TForm1.Button1Click(Sender: TObject);
var r:integer;
begin
  repeat
    PostMessage(StrToInt('$'+Edit1.Text), WM_KEYDOWN, StrToInt('$71'), StrToInt('$003C0001'));
    PostMessage(StrToInt('$'+Edit1.Text), WM_KEYUP, StrToInt('$71'), StrToInt('$003C0001'));
    //sleep(1000);
    PostMessage(StrToInt('$'+Edit1.Text), WM_KEYDOWN, StrToInt('$78'), StrToInt('$00430001'));
    PostMessage(StrToInt('$'+Edit1.Text), WM_KEYUP, StrToInt('$78'), StrToInt('$00430001'));

    Application.ProcessMessages;

    sleep(50);
  until not cbRepeat.Checked;
end;

Избавившись от лишних точек останова я достаточно быстро нашёл функцию куда попадала программа при нажатии "Создать новый случайный пароль". Дальше "шагая" по-фунциям я увидел в стеке ссылки на текст UNICODE с паролем, ещё немного и нашёл цикл который формировал 4-х значный пароль В отладчике видно как цифра-за-цифрой формируется пароль. На рисунке точка останова после выхода из цикла. Остаётся только в этом месте установить свой пароль. Адресацию я поставил относительно базового адреса. Свой пароль буду ставить так: сделаю свою функцию в незанятой части кода в которой поменяю пароль на свой, например на 0000, затем перенесу две инструкции MOV что в конце цикла в начало моей функции (первая помещает адрес строки пароля в EAX в качестве возвращаемого значения - как раз этот адрес нам нужен), а на их место вставлю вызов моей функции. Моя функция короткая всего 5 строк. Первые две это MOV'ы которые пришлось затереть что бы вписать CALL моей функции (! их операнды указывают на стек, за счёт инструкции CALL стек сдвинулся на 4 байта поэтому пришлось операнды увеличить на 4), затем две инструкции помещают UNICODE '0000' по адресу что в EAX и пятая это возврат назад.
Запускаем и проверяем что теперь пароль всегда один и тот же '0000'

Убираем окно подключения

Это окно справа-внизу появляется при подключении. Убрать его оказалось не просто. Я рассчитывал что можно будет найти место где устанавливается свойство VISIBLE и убрать его, но в ходе экспериментов выяснил что процедура подключения/отключения связана с обработчиком OnShow/OnHide. Если убрать вызов ShowWindow для данного окна то начавшаяся процедура подключения прекращалась. Или если сразу после того как окно показывалось вызывать ShowWindow с SW_HIDE подключение завершалось. Получалось что нельзя не показывать это окно и нельзя его скрывать.
В spy++ заметил сообщения отправляемые окну с координатами и размерами, так я вышел на функцию SetWindowPos. Она вызывалась для всех контролов главной формы и для этого окна в одной функции. Возникла идея сделать окно размером 1 пиксель или сдвинуть за размеры рабочего стола. Фильтровать именно это окно не было желания в результате все контролы на главном окне так же уменьшились и сдвинулись.

Убираем главное окно

Главное окно так же нельзя скрыть, поэтому я пошёл по тому же пути. Нашёл место где создаётся окно через CreateDialogParam и дописал вызов SetWindowPos где установил размер в 1 пиксель. Сам вызов SetWindowPos взял из предыдущего места. На скриншоте есть комментарии поэтому не буду расписывать. После этой процедуры окно исчезло но остался ярлык в таскбаре. Что бы его убрать нужно поменять свойства шаблона System Menu = False, Title Bar = False, Tool Window = True. Найти какие именно биты отвечают за эти свойства можно в Visual Studio открыв шаблон как двоичные данные. Редактировать шаблон в Visual Studio не получается - скорее всего она сохраняет чуть в другом формате, библиотека получается другого размера и TeamViewer выдаёт ошибку. Но можно найти и установить биты в Hedit'е, так же можно например "стереть" надпись www.teamviewer.com - выделена рамкой.
Но упорное главное окно не хотело исчезать полностью: при подключении показывался кусок, пришлось поменять ещё пару мест. Возможно что-то лишнее но в результате главное окно исчезло.

Внедряем свою DLL

Теперь когда главное окно скрыто нужно как-то узнавать уникальный TeamViewer ID который выводится в поле "Ваш ID". Для этого я решил перехватывать текст который записывается в поле "Ваш ID" и передавать его в качестве параметра в мою процедуру расположенную во внешней DLL. Процедура получала ID, запрашивала имя компьютера и отправляла на мой Web-сервис который просто сохранял данные в xml-файл. О процедуре и веб-сервисе напишу в следующей части, а в этой разберёмся с вызовом. Найти место в котором записывается ID в поле "Ваш ID" дело 1 минуты. Это делается WinAPI функцией User32.SetWindowTextW. Достаточно установить на неё точку останова и найти адрес откуда идёт вызов На скриншоте я выделил 5 байт которые нужно заменить на переход JMP на пустое место достаточное что бы вписать внего загрузку нашей DLL и вызов процедуры из этой DLL. Эти 5 байт это параметр hWnd поля "Ваш ID" и код команды CALL их можно затирать т.к главное окно всё равно скрыто. Операнд команды CALL трогать нельзя - это адрес который будет корректироваться базовыми поправками, т.е если записать туда наш JMP то он превратится в мусор. Нужно помнить что мы затёрли User32.SetWindowTextW которая принимает два параметра, но так же затёрли один PUSH т.е перед тем как прыгнуть назад надо будет уменьшить стек на одно DWORD слово.
ОК, теперь о загрузке DLL. Допустим она будет называться tvlib.dll, функцию будем вызывать не по имени, а по порядковому номеру @1. В секции данных только_для_чтения .rodata в свободном месте пропишем имя нашей библиотеки без расширения (LoadLibrary сама допишет расширение по-умолчанию .dll):

$+BBDBBO : 74 00 76 00 6C 00 69 00 62 00 : t v l i b  (UNICODE, адресация от базового адреса)

Теперь алгоритм:

  1. Проверяем если ID = "-" сразу выходим
  2. Получаем полный путь к нашей библиотеке. (текущая директорию + \tvlib)
  3. Изначально указывал только имя библиотеки рассчитывая что LoadLibrary сама найдёт её в каталоге в котором находится вызывающий файл, но тут я натолкнулся на грабли. Есть чётко описанный алгоритм по которому LoadLibrary ищет библиотеку если не указан путь. Каталог содержащий вызывающий файл идёт там первым пунктом. Но Windows 7 и Windows 8 отказывалась находить DLL. Если помещал DLL в system32 то Windows 7 находила, а Windows 8 нет. Архитектуры exe и dll одинаковые - 32 бита, обе Windows 64 бита. Что бы находила Windows 8 нужно было поместить dll в SysWOW64 (про путаницу что 64 битные dll должны быть в system32, а 32 битные в SysWOW64 я читал). Оптимальный вариант указывать полный путь тогда всё работает.
  4. Загружаем dll через LoadLibrary, если ошибка то выходим
  5. Находим адрес процедуры с порядковым номером @1 через GetProcAddress, если ошибка то выходим
  6. Вызываем процедуру @1 в качестве параметра передаём адрес строки с ID

Т.к таблица базовых поправок ничего не знает о моём коде и соответственно не будет корректировать мои вызовы WinAPI то их абсолютные адреса мне пришлось получать в процессе выполнения. Для этого я находил первое попавшееся место в оригинальном коде где вызывается нужная мне функция, высчитывал и прибавлял смещение до этого места к текущему адресу (EIP) и брал значение по этому адресу. В результате получался указатель на указатель на нужную функцию. Во второй части покажу как делал DLL, веб-сервис и автозапуск TeamViewer.

comments powered by Disqus
Яндекс.Метрика