Страницы

Поиск по вопросам

среда, 26 февраля 2020 г.

c# и debug режим

#c_sharp #cpp #clr


Можно ли в с# CLR код, запустить в "трассировочном" режиме? Можно ли  как-то перехватить
вызов Invoke? Как можно написать дебаггер под с# код? Единственное что пока-что приходит
в голову - емулировать выполнение CLR-кода, но тут нужно тонна кода на емулятор, а
потом... перехватывать код готовых библиотек, ведь есть мостики CLR-Native-CLR, поэтому
с емулятором не всё так просто. На с++-cli это чудо думаю было бы реально реализовать...
но... Я думаю есть какой-то особый режим, который легко включается. Видел уже есть
"кривые" аналоги VS... Как эта проблема решена в других проэктах? 
    


Ответы

Ответ 1



Как написать дебагер, а точнее трейсер. С чего начать Создание ICorDebug и ICorProcess Создание обработчика Апгрейт обработчика Статья предполагает что базовые знания с++ есть. Дебагер можно писать на с++, можно даже на с#, но я предпочитаю с++. Покажу базу - как написать трейсер. С чего начать. Давайте создадим простенький с# using System; public class Demo { public static void Main(string[] args) { Console.WriteLine("Hello world"); } } И соберём его c:\windows\Microsoft.NET\Framework\v2.0.50727\csc.exe /platform:x86 /target:1.exe 1.cs /pdb:1.pdb /debug У студии есть папка SDK в которой можно найти нужные хедеры. У меня тут SDK\v2.0\include. Понадобятся следующие библиотеки #include #include "COR\CorHdr.h" #include "COR\cor.h" #include "COR\cordebug.h" ICorDebug* dbg; // Библиотека дебаггера ICorProcess* process;//Процесс для отладки. Будет для одного процесса Поначалу я думал - создам CoCreateIntance, там есть ф-ция CreateProcess - и будет всё ок, но нет. Создание ICorDebug и ICorProcess. Второй создаётся легко, если создался первый. Есть несколько способов его создать, покажу один из. Если mscoree.dll не получается прилинковать - подключайте ёё через LoadLibrary и GetProcAddress. Есть две ф-ции, одна проверяет версию, другая - создает ICorDebug. void main(){ wchar_t* module = "c:\\yourdebug\\1.exe"; wchar_t ver[20]; GetRequestedRuntimeVersion(module,ver,sizeof(ver),&dw);//mscoree.dll hr=CreateDebugginInterfaceFromVersion(CorDebugVersion_2_0,ver,&dbg); // hr=CreateDebugginInterfaceFromVersion(CorDebugVersion_2_0+1,ver,&dbg); // для .NET 4 // Если не подходит версия - пробуйте менять первый аргумент //TODO: проверка hr hr = dbg->Initialize(); STARTUPINFOW si = {sizeof(STARTUPINFOW),0,}; PROCESS_INFORMATION pi = {0,}; // dbg->SetUnmanagedHandler dbg->SetManagedHandler(MgrHandler); // ниже будет // TODO: параметры запуска можно будет доделать hr = dbg->CreateProcess(module,module,0,0,true, CREATE_NEW_CONSOLE, L"\0\0\0\0", L".", &si, &pi,0,&process); // TODO: цикл ожидания конца работы дебагера for (int i=0;i<100;i++) WaitForSingleObject(pi.hProcess,1000); } Ну... почти готово. Или почти не считается. Долго мучался с SetManagedHandler, если ф-ция не работает, значит не все хандлеры вы описали. Теперь перейдем к... Создание обработчика. В версии FrameWork 2.0 оказывается нужно поддерживать два каллбека. Ну и... коечего повписывать в обработчик. Нужно везде где можно написать pAppDomain->Continue(); вписать его в каждый обработчик. Весь код приводить не буду, приведу главное. Дальше речь идет только об этом обработчике class MGRHandler:public ICorDebugManagedCallback,ICorDebugManagedCallback2{ // 1 Рассказать какие у нас калбеки HRESULT __stdcall QueryInterface(REFIID riid,void**ppvObject){ if (riid==IID_ICorDebugManagedCallback){ *ppvObject=(ICorDebugManagedCallback*)this;//Первый return 0; } if (riid==IID_ICorDebugManagedCallback2){ *ppvObject=(ICorDebugManagedCallback2*)this;//Второй return 0; } return (HRESULT)-1; } // 2 Вначале нас бросит сюда HRESULT __stdcall CreateProcess(ICorDebugProcess* pProcess){ pProcess->Continue(0); } // 3 Потом будет создан "домен" HRESULT __stdcall CreateAppDomain(ICorDebugProcess *pProcess, ICorDebugAppDomain *pAppDomain) { pAppDomain->Attach(); // Переводим домен в дебаг режим pProcess->Continue(0); return 0;}; // 4 Ставим Continue тут обязательно, желательно везде HRESULT __stdcall LoadAssembly(ICorDebugAppDomain *pAppDomain, ICorDebugAssembly *pAssembly) { pAppDomain->Continue(0);return 0; } HRESULT __stdcall CreateThread(ICorDebugAppDomain *pAppDomain, ICorDebugThread *thread) { pAppDomain->Continue(0);return 0; }; Аналогично ф-ции Breakpoint,NameChange и другие. Перехват функций нужно делать в модуле, например так: HRESULT __stdcall LoadModule(ICorDebugAppDomain *pAppDomain, ICorDebugModule *pModule) { ICorDebugFunction* fn=0;//ф-ция номер 1 имеет такой код 6000001 pModule->GetFunctionFromToken(0x6000001,&fn); if (fn!=0) fn->CreateBreakpoint(&bp1); // TODO: очистить bp1 pAppDomain->Continue(0); return 0; } И добавим обработчик BreakPoint HRESULT __stdcall Breakpoint(ICorDebugAppDomain *pAppDomain, ICorDebugThread *pThread, ICorDebugBreakpoint *pBreakpoint) { ICorDebugFrame * f = 0; pThread->GetActiveFrame(&f); ICorDebugStepper * step = 0; f->CreateStepper(&step); step->Step(0); // Заставляем работать в шаговом режиме // TODO: step освободить когда не нужен будет f->Release(); pAppDomain->Continue(0); return 0; } Да, добавьте MGRHandler MgrHandler; под обработчиком, когда обработчик доконца допишите. Если компилятор ругается пишет слова abstract и error значит не все функции реализованы. Нужно все добавить (современные компиляторы умеют реализовать абстрактные классы сами, если дополнительно сделать два три клика в нужных местах). Теперь дебагер может кое-как отлаживать программу. Столкнулся с тем... Ой, а где же мой ip.... Апгрейт обработчика. Есть интерфейс ICorDebugILFrame - в нем куча полезностей, локальные переменные, аргументы, и ip. Последний "штрих" программы, правим в обработчике ф-цию: HRESULT __stdcall StepComplete( ICorDebugAppDomain *pAppDomain, ICorDebugThread *pThread, ICorDebugStepper *pStepper, CorDebugStepReason reason) { ICorDebugFrame * f = 0; ICorDebugILFrame * ff = 0; unsigned ip,tok; pThread->GetActiveFrame(&f); f->QueryInterface(IID_ICorDebugILFrame , &ff); ff->GetIP(&ip); f->GetFunctionToken(&tok); char buf[30]; wsprintfA("tok:%x ip:%x\r\n",tok,buf); // TODO: вывод на екран pStepper->Step(0); // пусть ещё шагает pAppDomain->Continue(0); return 0; } Теперь получился "трейсер", который позволяет по-шагам выполнить ф-цию. Я не упомянул IMetaDataImport - через него можно получить имена всех ф-ций и параметров, остальную нужную информацию найти относительно легко. Я постарался показать 4-ре шага, которые мне было не очень просто найти. Показан чисто "скелет". P.S. Тесты. Модуль собран в borland c++ x86. win2003 x86 - модуль работает (студия 2005) win7 x64 - модуль работает, но нужно создать 32-битную сборку отлаживаемой программы, т.е. добавить в csc.exe параметр /platform:x86 (иначе ловим ошибку DebuggerError 0x80131C30 ) Полезные ссылки MS debug api http://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/debugging/ Создание ICorDebug http://lowleveldesign.org/2010/10/11/writing-a-net-debugger-part-1-starting-the-debugging-session/ Часть 2 Часть 3 Часть 4 Коды ошибoк HRESULT http://github.com/mrfearless/UASM-with-RadASM/blob/master/UASM64/ErrorCodes.dat Файл с кодами ошибок есть corerror.h но... он не удобный.

Комментариев нет:

Отправить комментарий