[Программирование, C++, C#] Как подружить .NET и IDA Pro (о дружбе C# и C++)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Приветствую,
Сначала о проблеме/задаче:
Начав писать очередной плагин-отладчик для ретро-платформы под IDA Pro (по секрету: это будет SNES), я столкнулся со следующим набором, который требовалось подружить:
- Ядро эмулятора написано на C++ и компилируется в DLL
- GUI эмулятора написано на C# и использует DLL-ку ядра для управления эмуляцией
- IDA Pro, которая использует плагины либо на питоне, либо на C++ в виде DLL (а отладчики только на C++)
Мне нужно было как-то обращаться из IDA к ядру эмуляции (пошаговая отладка, регистры, память и т.п.) так, чтобы и IDA и графический интерфейс имели одно общее состояние ядра.
Вариантов решения проблемы на старте виделось несколько (не все из них правильные), но о каждом по порядку:
1) Сделать LoadLibrary("emu_core.dll"), затем GetProcAddress(core, "Pause") и pause(), чтобы, например, сделать в эмуляторе паузу.
Я тогда думал: вот оно — идеальное решение с наименьшим количеством изменений в код (и от ядра, и от GUI исходники у меня имеются)! Но, проблемой здесь стало то, что состояние ядра, опять же, хранится в DLL-ке, которая, в свою очередь, загружена в процесс UI, и только там и происходят изменения.
В итоге, загрузив DLL-ку в плагине для IDA, я получил непроинициализированное состояние ядра. Нужно было придумывать другое решение.
2) Сделать что-то типа хост-процесса, который бы и работал с единственным экземпляром ядра, а все остальные — Ида и GUI, обращались бы к этому процессу через RPC-протокол.
Идея вполне здравая, только имелось несколько препятствий в её использовании:
- IDA должна реагировать на события эмуляции (брейкоинты, пауза, reset), т.е. хостовое приложение должно быть активным, а отладчик должен всё время ожидать сообщения от ядра, через хост, себе.
- Колбэки. На самом деле, это уже заложенный в ядре эмулятора функционал, позволяющий решить первый пункт, но, как решать его через RPC я не знал.
Решение
Тогда мне посоветовали взглянуть в сторону COM. Ведь у него, действительно, уже имеется своя замечательная реализация RPC с колбэками и интерфейсами. Которую, к тому же, можно использовать через WinAPI (т.е. C/C++) и нативно — в C#.
А т.к. GUI эмулятора был на шарпе, дело было за малым. Оставалось только воспользоваться его возможностями по работе с COM-интерфейсами.
Схема получается следующей:
1) Классы и типы данных, которые могут быть использованы через COM, помечаем как [ComVisible(true)]. К счастью, если вдруг какой-то тип вы всё же забудете пометить как видимый, используемые далее инструменты вам об этом скажут (иногда явно, иногда нет, но скажут)
2) Создаю интерфейс, в котором будут описаны все методы, необходимые внешнему пользователю (отладчику в IDA). Помечаю его ещё двумя тегами (к уже имеющемуся ComVisible):
[Guid("7710855F-D37B-464B-B645-972BAB2706D4")] // Visual Studio умеет генерировать новые GUID-ы из коробки
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
public interface IDebugApi
3) Реализую этот интерфейс, в виде класса DebugCOM:
[Guid("6AF9F5DD-6626-4E35-BEF4-29A73E18A739")]
[ComVisible(true)]
public class DebugCOM : IDebugApi
4) Выношу отладочный интерфейс (без реализации) и все его зависимости в отдельный проект (Class Library), компилирую. Получаю DebugCOM.dll
5) С помощью утилиты tlbexp.exe из поставки Visual Studio (доступна через Developer Command Prompt) конвертирую DLL в TLB. В этом файле содержится полное описание интерфейсов, используемых типов данных и т.д.
Командная строка:
tlbexp DebugCOM.dll /out:DebugCOM.tlb
Если всё сделано правильно и все типы данных помечены как видимые для COM, то мы получим красивое сообщение:
Assembly exported to 'DebugCOM.tlb'
Иначе, нас будет ждать ад. Ад зависимостей:
Выхлоп TlbExp
SPL
TlbExp : warning TX801311B0 : Type library exporter warning processing 'AddressInfo.Type, DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'InteropBreakpoint.MemoryType, DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemorySize(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemoryValue(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.SetMemoryValue(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.SetMemoryValues(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.SetMemoryState(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemoryState(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemoryAccessCounts(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemoryAccessCounts2(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
Видим, что тип данных SnesMemoryType не помечен как ComVisible(true). Исправляем.
6) Получившийся TLB-файл открываем (File->View TypeLib...) в ComView. Утилита находится здесь:
c:\Windows\Microsoft.NET\Framework\v4.0.30319\ComView.exe
Нам открывается окно просмотра нашего файла в виде уже сконвертированного IDL:
Жмём File->Save as... и сохраняем как IDL-файл.
7) Сгенерированный на предыдущем шаге IDL-файл добавляем в C++ проект, в котором мы, собственно, и будем обращаться к DebugCOM:
8) Теперь обращаемся к контекстному меню IDL-файла и жмём Compile Ctrl+F7. Если всё хорошо, мы получим два файла: DebugCOM_h.h и DebugCOM_i.c. Если же нет (у меня именно так и случилось), придётся решать ещё одну проблему.
Дело в том, что в C# порядок объявления типов и мест где они используются, совершенно не важен. Чего не скажешь про C/C++. Здесь пришлось упорядочивать объявления типов для C#.
9) Наконец-то получив долгожданные .h и .c файлы, инклудим их в наш код, и обращаемся к нужным методам отладочного интерфейса. Пример кода:
Пример использования COM в C++
SPL
#include <atlbase.h>
#include <iostream>
#include "DebugCOM_h.h"
#include "DebugCOM_i.c"
int main(int argc, char* argv[]) {
HRESULT hr;
hr = ::CoInitializeEx(0, COINITBASE_MULTITHREADED);
if (FAILED(hr))
{
std::cout << "CoInitializeEx failure: " << std::hex << std::showbase << hr << std::endl;
return EXIT_FAILURE;
}
CLSID CLSID_server;
hr = ::CLSIDFromString(L"{6AF9F5DD-6626-4E35-BEF4-29A73E18A739}", &CLSID_server);
if (FAILED(hr))
{
std::cout << "CLSIDFromString failure: " << std::hex << std::showbase << hr << std::endl;
return EXIT_FAILURE;
}
CComPtr<IDebugApi> server;
hr = ::CoCreateInstance(CLSID_server, nullptr, CLSCTX_LOCAL_SERVER, __uuidof(IDebugApi), (void**)&server);
if (FAILED(hr))
{
std::cout << "CoCreateInstance failure: " << std::hex << std::showbase << hr << std::endl;
return EXIT_FAILURE;
}
DebugState state = {};
hr = server->GetState(&state);
if (FAILED(hr))
{
std::cout << "GetState failure: " << std::hex << std::showbase << hr << std::endl;
return EXIT_FAILURE;
}
std::cout << "PC Address: " << std::hex << std::showbase << state.Cpu.PC << std::endl;
::CoUninitialize();
return EXIT_SUCCESS;
return 0;
}
И здесь мы плавно подошли к особенностям использования COM.
Особенности COM-интерфейсов
Они бывают двух типов:
- Inprocess
- Out-of-process
- Первый тип используется, если требуется использовать COM в том же приложении, где уже есть реализация интерфейса. Например, я мог бы управлять отладчиком по COM из GUI, но не из IDA.
Данный тип использует реализацию COM в виде DLL.
- Второй тип используется, если нужно иметь доступ к COM-интерфейсу извне приложения. В этом случае регистрируется исполняемый файл программы, который, к тому же, будет являться тем самым хост-приложением. Т.е. оно будет хранить состояние ядра, а COM поможет нам транслировать запросы.
Осталось найти описание того, как правильно регистрировать out-of-process COM. Оказалось, на GitHub есть самый что ни на есть "из первых рук" пример: https://github.com/dotnet/samples/tree/master/core/extensions/OutOfProcCOM. Он легко адаптируется и используется под нужды ваших проектов.
Выводы
В итоге, мне удалось подружить IDA Pro и .NET Framework приложение средствами Windows, не городя велосипеды из собственных RPC-протоколов. К тому же, я решил проблему наличия всего одной копии ядра эмулятора и шаринга его состояния между несколькими приложениями.
===========
Источник:
habr.com
===========
Похожие новости:
- [Информационная безопасность, Python, Программирование] Побег из песочницы с Python (перевод)
- [Информационная безопасность] Security Week 45: тандем уязвимостей в Windows 10 и Chrome
- [Софт, Настольные компьютеры, Ноутбуки] К 2021 году Microsoft готовит новый редизайн интерфейса Windows 10 под кодовым названием Sun Valley
- [Программирование, Функциональное программирование, TypeScript] Функциональное программирование на TypeScript: полиморфизм родов высших порядков
- [Программирование, Алгоритмы, Математика] Точные и быстрые вычисления для чисел с плавающей точкой на примере функции синуса. Введение и часть 1
- [Программирование микроконтроллеров, Схемотехника, Производство и разработка электроники, DIY или Сделай сам, Электроника для начинающих] STM32 DoomBoy SDRAM ILI9341
- [C++, Разработка игр, Звук] Как не надо разрабатывать звуковые движки
- [Высокая производительность, Программирование, Алгоритмы] Быстрая медианная фильтрация с использованием AVX-512
- [Анализ и проектирование систем, Разработка под Windows] Анализ проблем, а так же разработка АИС в складском учёте
- [Интерфейсы, Разработка под Windows, Дизайн, Софт] Microsoft планирует крупное обновление пользовательского интерфейса Windows 10 (перевод)
Теги для поиска: #_programmirovanie (Программирование), #_c++, #_c#, #_outofprocess, #_com_interface, #_windows, #_sharp (шарп), #_pljusy (плюсы), #_programmirovanie (
Программирование
), #_c++, #_c#
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:13
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Приветствую, Сначала о проблеме/задаче: Начав писать очередной плагин-отладчик для ретро-платформы под IDA Pro (по секрету: это будет SNES), я столкнулся со следующим набором, который требовалось подружить:
Мне нужно было как-то обращаться из IDA к ядру эмуляции (пошаговая отладка, регистры, память и т.п.) так, чтобы и IDA и графический интерфейс имели одно общее состояние ядра. Вариантов решения проблемы на старте виделось несколько (не все из них правильные), но о каждом по порядку: 1) Сделать LoadLibrary("emu_core.dll"), затем GetProcAddress(core, "Pause") и pause(), чтобы, например, сделать в эмуляторе паузу. Я тогда думал: вот оно — идеальное решение с наименьшим количеством изменений в код (и от ядра, и от GUI исходники у меня имеются)! Но, проблемой здесь стало то, что состояние ядра, опять же, хранится в DLL-ке, которая, в свою очередь, загружена в процесс UI, и только там и происходят изменения. В итоге, загрузив DLL-ку в плагине для IDA, я получил непроинициализированное состояние ядра. Нужно было придумывать другое решение. 2) Сделать что-то типа хост-процесса, который бы и работал с единственным экземпляром ядра, а все остальные — Ида и GUI, обращались бы к этому процессу через RPC-протокол. Идея вполне здравая, только имелось несколько препятствий в её использовании:
Решение Тогда мне посоветовали взглянуть в сторону COM. Ведь у него, действительно, уже имеется своя замечательная реализация RPC с колбэками и интерфейсами. Которую, к тому же, можно использовать через WinAPI (т.е. C/C++) и нативно — в C#. А т.к. GUI эмулятора был на шарпе, дело было за малым. Оставалось только воспользоваться его возможностями по работе с COM-интерфейсами. Схема получается следующей: 1) Классы и типы данных, которые могут быть использованы через COM, помечаем как [ComVisible(true)]. К счастью, если вдруг какой-то тип вы всё же забудете пометить как видимый, используемые далее инструменты вам об этом скажут (иногда явно, иногда нет, но скажут) 2) Создаю интерфейс, в котором будут описаны все методы, необходимые внешнему пользователю (отладчику в IDA). Помечаю его ещё двумя тегами (к уже имеющемуся ComVisible): [Guid("7710855F-D37B-464B-B645-972BAB2706D4")] // Visual Studio умеет генерировать новые GUID-ы из коробки
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [ComVisible(true)] public interface IDebugApi 3) Реализую этот интерфейс, в виде класса DebugCOM: [Guid("6AF9F5DD-6626-4E35-BEF4-29A73E18A739")]
[ComVisible(true)] public class DebugCOM : IDebugApi 4) Выношу отладочный интерфейс (без реализации) и все его зависимости в отдельный проект (Class Library), компилирую. Получаю DebugCOM.dll 5) С помощью утилиты tlbexp.exe из поставки Visual Studio (доступна через Developer Command Prompt) конвертирую DLL в TLB. В этом файле содержится полное описание интерфейсов, используемых типов данных и т.д. Командная строка: tlbexp DebugCOM.dll /out:DebugCOM.tlb
Если всё сделано правильно и все типы данных помечены как видимые для COM, то мы получим красивое сообщение: Assembly exported to 'DebugCOM.tlb'
Иначе, нас будет ждать ад. Ад зависимостей: Выхлоп TlbExpSPLTlbExp : warning TX801311B0 : Type library exporter warning processing 'AddressInfo.Type, DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types.
TlbExp : warning TX801311B0 : Type library exporter warning processing 'InteropBreakpoint.MemoryType, DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemorySize(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemoryValue(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.SetMemoryValue(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.SetMemoryValues(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.SetMemoryState(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemoryState(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemoryAccessCounts(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. TlbExp : warning TX801311B0 : Type library exporter warning processing 'IDebugApi.GetMemoryAccessCounts2(type), DebugCOM'. Warning: Non COM visible value type 'SnesMemoryType' is being referenced either from the type currently being exported or from one of its base types. Видим, что тип данных SnesMemoryType не помечен как ComVisible(true). Исправляем. 6) Получившийся TLB-файл открываем (File->View TypeLib...) в ComView. Утилита находится здесь: c:\Windows\Microsoft.NET\Framework\v4.0.30319\ComView.exe
Нам открывается окно просмотра нашего файла в виде уже сконвертированного IDL: Жмём File->Save as... и сохраняем как IDL-файл. 7) Сгенерированный на предыдущем шаге IDL-файл добавляем в C++ проект, в котором мы, собственно, и будем обращаться к DebugCOM: 8) Теперь обращаемся к контекстному меню IDL-файла и жмём Compile Ctrl+F7. Если всё хорошо, мы получим два файла: DebugCOM_h.h и DebugCOM_i.c. Если же нет (у меня именно так и случилось), придётся решать ещё одну проблему. Дело в том, что в C# порядок объявления типов и мест где они используются, совершенно не важен. Чего не скажешь про C/C++. Здесь пришлось упорядочивать объявления типов для C#. 9) Наконец-то получив долгожданные .h и .c файлы, инклудим их в наш код, и обращаемся к нужным методам отладочного интерфейса. Пример кода: Пример использования COM в C++SPL#include <atlbase.h>
#include <iostream> #include "DebugCOM_h.h" #include "DebugCOM_i.c" int main(int argc, char* argv[]) { HRESULT hr; hr = ::CoInitializeEx(0, COINITBASE_MULTITHREADED); if (FAILED(hr)) { std::cout << "CoInitializeEx failure: " << std::hex << std::showbase << hr << std::endl; return EXIT_FAILURE; } CLSID CLSID_server; hr = ::CLSIDFromString(L"{6AF9F5DD-6626-4E35-BEF4-29A73E18A739}", &CLSID_server); if (FAILED(hr)) { std::cout << "CLSIDFromString failure: " << std::hex << std::showbase << hr << std::endl; return EXIT_FAILURE; } CComPtr<IDebugApi> server; hr = ::CoCreateInstance(CLSID_server, nullptr, CLSCTX_LOCAL_SERVER, __uuidof(IDebugApi), (void**)&server); if (FAILED(hr)) { std::cout << "CoCreateInstance failure: " << std::hex << std::showbase << hr << std::endl; return EXIT_FAILURE; } DebugState state = {}; hr = server->GetState(&state); if (FAILED(hr)) { std::cout << "GetState failure: " << std::hex << std::showbase << hr << std::endl; return EXIT_FAILURE; } std::cout << "PC Address: " << std::hex << std::showbase << state.Cpu.PC << std::endl; ::CoUninitialize(); return EXIT_SUCCESS; return 0; } И здесь мы плавно подошли к особенностям использования COM. Особенности COM-интерфейсов Они бывают двух типов:
Осталось найти описание того, как правильно регистрировать out-of-process COM. Оказалось, на GitHub есть самый что ни на есть "из первых рук" пример: https://github.com/dotnet/samples/tree/master/core/extensions/OutOfProcCOM. Он легко адаптируется и используется под нужды ваших проектов. Выводы В итоге, мне удалось подружить IDA Pro и .NET Framework приложение средствами Windows, не городя велосипеды из собственных RPC-протоколов. К тому же, я решил проблему наличия всего одной копии ядра эмулятора и шаринга его состояния между несколькими приложениями. =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_c++, #_c# |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:13
Часовой пояс: UTC + 5