|
Модераторы: Poseidon, Snowy, bems, MetalFan |
|
Rrader |
|
||||||||||||||
Inspired =) Профиль Группа: Экс. модератор Сообщений: 1535 Регистрация: 7.5.2005 Репутация: 70 Всего: 191 |
Внимание! Настоятельно рекомендую ознакомиться с материалом Получение данных из TStringGrid (D3-DXE), если Вы озадачены получением данных из этой таблицы На этом форуме и на многих других не раз возникал вопрос о том, как получить данные из компонента TStringGrid, созданного в "чужом" процессе. API-функция GetWindowText не дает нужного результата, поскольку текстовые данные таблицы хранятся в памяти обособленно от каких-либо оконных API. Более того, все, что мы можем получить о таблице в "чужом" процессе, это лишь её дескриптор (Handle). Задача подобного рода является достаточно сложной, и стандартного её решения не существует. Хочу предложить Вам одно нестандартное, но эффективное решение. Задача о получении данных из TStringGrid прямым инжектом [align=justify]Особенности метода: 1) Используется прямое внедрение в процесс (без DLL) с запуском удаленного потока. Обычно данная техника обнаруживается антивирусами (практика показывает, что для людей, которым нужно получить данные из TStringGrid, это не является проблемой). Если мы откажемся от CreateRemoteThread, то перед нами встанет еще одна проблема - мы не сможем напрямую получить доступ к необходимым данным (которые могли бы передать через параметр CreateRemoteThread). Данная проблема имеет эффективное решение, о котором говорится во второй части статьи. 2) Для обеспечения универсальности в некоторых местах мы не можем использовать код Delphi, только ассемблер. Это связано с тем, что, во-первых, я стремился сделать код как можно более компактным (его можно портировать на WinAPI с минимальными модификациями), а во-вторых, что самое главное, сделать его работоспособным для разных версий Delphi. Код (работоспособность) зависим от версии Delphi, на котором скомпилировано целевое приложение. Что это значит? Дело в том, что от версии к версии в Delphi вносятся модификации не только в компилятор и IDE, но и в VCL. Стандартные классы расширяются, появляются новые методы и свойства. Используя знания анатомии класса Delphi, можно получить доступ ко многим членам класса без ограничений, но все это возможно только через ассемблер. Некоторые сведения об устройстве классов в Delphi В ассемблерном представлении классы почти ничем не отличаются от записей (records). Отличия следующие - каждая такая запись вбирает в себя не только свои собственные поля, но и поля всех предков. Также добавляется поле с адресом таблицы VMT - оно всегда первое по счёту. Рассмотрим пример:
Эквивалентная запись для класса TMyClassEx будет выглядеть следующим образом:
Для доступа к полям Вам будет необходим "указатель на класс". Загрузив его в регистр (скажем, EAX), можно организовать доступ к полям, учитывая порядок их объявления: [EAX + 0] - первое поле (VMT) [EAX + 4] - второе поле FField1 [EAX + 8] - третье поле FField2 [EAX + 12] - четвертое поле FField3 Здесь используются непосредственные значения при адресации (hard-coded values). При работе со встроенным ассемблером так делать не рекомендуется, но при работе с классами у нас нет другого выхода. Как видите, секций private и public больше нет - они имеют только высокоуровневую реализацию. Это касается всех секций класса. Реализация метода получения данных Давайте рассмотрим, как можно получить данные из удаленного процесса. У нас есть лишь только дескриптор целевой TStringGrid. Зная его, мы сможем определить PID процесса через функцию GetWindowThreadProcessId. Открыв процесс, мы сможем записать в него функцию (выделив память), и запустить ее на исполнение через создание удаленной нити (будем считать, что процесс разрешает нам выполнять все описанные действия). Нам также понадобятся дополнительные данные (см. ниже), которые можно передать через указатель в параметр функции. Следует учесть, что на момент создания нити данные для параметра уже должны быть подготовлены и размещены в чужом процессе (для этого используется функция VirtualAllocEx). Что следует размещать в данных? Ну, во-первых, дескриптор TStringGrid, так как он у нас один известный и будет использоваться Далее - все адреса API-функций, которые будут использоваться в "чужом" процессе. Для многих API-функций адреса можно получить непосредственно в нашем процессе, потому что модули ntdll.dll, kernel32.dll и user32.dll для user-mode приложений, в которых используется TStringGrid, загружаются по одинаковым адресам. Все строки, которые будут использоваться для вызовов API, также следует передавать удаленной функции (для удобства через массив символов, если это возможно). Последнее, что нужно передать - это данные, определяющие особенности работы функции. В нашем случае - это смещения 4-х свойств и одного метода StringGrid для получения данных в программах, написаных на разных версиях Delphi. Об этом см. ниже. Как работает функция DataGrabber Так называется удаленная функция, которая осуществляет всю работу по получению данных и их передаче. Получить данные - дело одно, а передать - совсем другое. Дело в том, что функция работает не в нашем процессе, но получает там строки, которые нужно передать в наш процесс. TStringGrid может быть очень большим, и для непрерывной передачи данных я выбрал такое средство IPC, как именованные каналы (Named Pipes). Работает функция в несколько этапов:
Вот часть кода главного модуля: </div>
|
||||||||||||||
|
|||||||||||||||
Qu1nt |
|
|||
Опытный Профиль Группа: Участник Сообщений: 602 Регистрация: 13.1.2007 Репутация: 18 Всего: 50 |
Кто тебя научил так программировать?! Браво.
|
|||
|
||||
Rrader |
|
|||
Inspired =) Профиль Группа: Экс. модератор Сообщений: 1535 Регистрация: 7.5.2005 Репутация: 70 Всего: 191 |
Задача о сабклассинге "чужих" окон прямым инжектом Оригинальный метод был предложен программистом по имени Robert Custer. Сабклассинг - это контроль сообщений окон, реализуемый посредством смены оконной процедуры. Следующий пример показывает, как можно осуществить удаленный сабклассинг без внедрения DLL. Пример будет менять оконную процедуру у родителя окна любого SysListView32 (TListView) так, чтобы элементы списка с нечетными номерами отображались красным цветом, а с четными - зеленым. Сначала стоит разобраться, как это сделать в своем приложении на чистом API и не очень Чтобы изменить цвет элемента, нужно обрабатывать сообщение WM_NOTIFY, которое будет посылаться родителю SysListView32. Получив это сообщение, мы должны проанализировать параметр lParam, приведя его к типу TNMHdr (запись, содержащая данные сообщения). Если параметр code этой записи будет равен NM_CUSTOMDRAW, значит сейчас следует отрисовать элемент. Чтобы изменить цвет элемента, мы должны отправить значение CDRF_NOTIFYITEMDRAW как результат в ответ на WM_NOTIFY в том случае, когда член dwDrawStage записи PNMCustomDraw(PNMHdr(lParam))^ равен CDDS_PREPAINT. Затем мы снова получим WM_NOTIFY, и когда dwDrawStage будет равен CDDS_ITEMPREPAINT, то через член clrText записи PNMLVCustomDraw(PNMHdr(lParam))^ мы уже сможем изменить цвет на нужный. Индекс элемента передается в члене dwItemSpec записи PNMCustomDraw(PNMHdr(lParam))^. Доступ к данным из оконной процедуры Данные нам понадобятся, чтобы вызывать оригинальную оконную процедуру Способ доступа к ним заключается в следующем: когда мы записывали данные и новую оконную процедуру, мы записали их рядом. Т.е. сначала идут данные, а за ними сразу NewProc. В ней самой нам необходимо узнать свой адрес, а затем, отняв размер данных из этого адреса, мы получим адрес данных! Как получить этот адрес? Мы знаем, что адрес текущей инструкции находится в регистре EIP. Но ассемблер в Delphi не дает возможности его считывать. Его можно прочитать неявно, зная, как работает, скажем, команда call. При вызове данная команда заносит на стек адрес возврата, чтобы вернуться на следующую за call инструкцию, когда будет выполнена команда ret. Мы можем прочитать адрес возврата, вытолкнув его из стека командой pop в регистр (в примере это ECX). После этого у нас в регистре ECX будет находиться адрес текущей инструкции. Чтобы получить @NewProc, нам нужно из ECX вычесть определенное значение - размер команд между точкой входа в функцию и нашей командой pop. Эти команды - код входа (по созданию фрейма стека) и инициализации, генерируемый Delphi. Смещение можно посчитать на основе ассемблерного листинга функции. Далее мы вычитаем из ECX еще и размер данных - и получаем адрес данных. Привожу код главного модуля. Процедуры внедрения те же. Проект находится в аттаче. В примере меняется окно ListView у стандартного приложения "Сведения о системе" (через сабклассинг родителя). Будет затронут и TreeView, так как тот также использует схожую технику отрисовки. Пример проверен на работоспособность в Windows XP, Windows Vista, Windows 7.
Надеюсь, данный материал поможет Вам в программировании! Присоединённый файл ( Кол-во скачиваний: 264 ) Attach.zip 11,40 Kb |
|||
|
||||
THandle |
|
|||
Хранитель Клуба Награды: 1 Профиль Группа: Админ Сообщений: 3639 Регистрация: 31.7.2007 Где: Moscow, Dubai Репутация: 65 Всего: 372 |
||||
|
||||
psychedeler |
|
||||
Новичок Профиль Группа: Участник Сообщений: 4 Регистрация: 22.8.2008 Репутация: нет Всего: нет |
День добрый! Пытаюсь вникнуть в суть, т.к. озадачился проблемой "чужого" StringGrid'a...
Делаю на С++, поэтому с переходом от всего выше сказанного к С++ позникают проблемы. 1) PInjectData = ^TInjectData; TInjectData = packed record Что это означает? packed record, по-моему, сильно смахивает на С++ "struct", а ^ - это указатель (символ "*") И по мне кажется, что фрагмент
на С++ выглядит как
2) LEA EDX, (TInjectData)[EBX].FPipeName И это что такое? Могу я получить какое-нибудь пояснение? Перевод с Delphi на С++ дает ошибку синтаксиса при обработке 2-го случая. Это сообщение отредактировал(а) Rrader - 8.5.2009, 12:41 |
||||
|
|||||
ama_kid |
|
||||||||
АСУТП-кодер Профиль Группа: Комодератор Сообщений: 1460 Регистрация: 5.3.2007 Где: Москва Репутация: 15 Всего: 95 |
на С++ это выглядит совсем не так, а примерно следующим образом:
LEA - Load Effective Address, загрузка эффективного адреса строки в регистр EDX, если тебе это поможет. В данном случае это делается для того, чтобы передать эту строку параметром через стек в нижеследующий вызов
По сабжу: Rrader, молодец, интересная статья со стандартно нестандартным подходом -------------------- самурай без меча подобен самураю с мечом, но только без меча |
||||||||
|
|||||||||
Rrader |
|
||||||
Inspired =) Профиль Группа: Экс. модератор Сообщений: 1535 Регистрация: 7.5.2005 Репутация: 70 Всего: 191 |
|
||||||
|
|||||||
froloff |
|
|||
Новичок Профиль Группа: Участник Сообщений: 1 Регистрация: 10.6.2009 Репутация: нет Всего: нет |
Браво Rrader, давно не видел столь изящно написанного кода.
Добавил поддержку C Builder 6.0.
И сохранение в файл (это думаю каждый сможет сделать на свой вкус). Это сообщение отредактировал(а) Rrader - 19.6.2009, 17:38 |
|||
|
||||
Rrader |
|
|||
Inspired =) Профиль Группа: Экс. модератор Сообщений: 1535 Регистрация: 7.5.2005 Репутация: 70 Всего: 191 |
froloff, спасибо
На самом деле у меня в принципе готов код, который получает эти данные без внедрения и без зависимости от компилятора Delphi (C++Builder). Т.е. эта статья - примитив, метод громоздок (но иногда оправдан для конкретных задач, потому что универсальный метод сложнее реализовать). |
|||
|
||||
Rrader |
|
|||
Inspired =) Профиль Группа: Экс. модератор Сообщений: 1535 Регистрация: 7.5.2005 Репутация: 70 Всего: 191 |
||||
|
||||
hexware |
|
|||
Новичок Профиль Группа: Участник Сообщений: 1 Регистрация: 9.6.2010 Репутация: нет Всего: нет |
ни у кого копии статьи не сохранилось? а то сайт wmsdk.com умер (
|
|||
|
||||
hhhhhhhhhhhh |
|
|||
Новичок Профиль Группа: Участник Сообщений: 12 Регистрация: 24.7.2008 Репутация: нет Всего: нет |
Офигенный пример о сабклассинге чужих окон.
А нельзя ли куски асемблера переписать на WinApi? Хотел подправить пример под свои нужды, но блин обломился... Не понятно в этом месте... |
|||
|
||||
Rrader |
|
|||
Inspired =) Профиль Группа: Экс. модератор Сообщений: 1535 Регистрация: 7.5.2005 Репутация: 70 Всего: 191 |
hhhhhhhhhhhh, можно, правда, придется использовать библиотеку DLL, если охота полностью отказаться от asm-вставок и зашитых вручную значений. http://wmsdk.com/2010/02/01/vcl-components...ering-part-one/ - тут используется сабклассинг окна в "чужом" процессе, причем без asm. Там много кода, не относящегося к сабклассингу, надо посмотреть и взять лишь нужное Показано, как работать со строками, как обрабатывать сообщения. Надо сказать, что Delphi почти всегда генерирует работоспособный код для использования в "чужом" процессе. Разумеется, есть потенциально опасные конструкции, например, блок case. Автоматические типы, над которыми покровительствует Delphi, также в группе риска. P.S. Немного поправил код |
|||
|
||||
_ls_ |
|
|||
Новичок Профиль Группа: Участник Сообщений: 7 Регистрация: 23.2.2008 Репутация: нет Всего: нет |
Подскажите, пожалуйста, смещения для дельфи 7. Вставил смещения от C Builder 6.0. Число строк/столбцов определят верно, но вместо текста знаки вопросов в основном.
|
|||
|
||||
giao |
|
|||
Новичок Профиль Группа: Участник Сообщений: 1 Регистрация: 15.10.2009 Репутация: нет Всего: нет |
Доброго времени суток! У кого-то есть рабочая ссылка на эту статью "Получение данных из TStringGrid (D3-DXE)"? |
|||
|
||||
Правила форума "Delphi: Общие вопросы" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |