• This is a usual time of the year for summaries so let’s keep the tradition alive and write one. Here’s my 2017 achievements split between months. January Blogging for 18 days straight – nothing near gutek’s achievement (whole year!) but still a nice streak Microsoft MVP title (thx Konrad Kokosa for pointing this one ;)) February […]

  • “Advent of Code is a series of small programming puzzles for a variety of skill levels.” Each day was a fun an interesting coding challenge. I’ve decided to practice and code this in python to learn the skill. Some of them might not be the best python scripting as I was short on time in […]

  • Some time ago I’ve attended a .net developer days 2017 conference. I was quite busy since (traveling, teaching .net, working) that only now I got some time to share some thoughts about it. As a bonus I’m including a short interview I did during the event. I need to state here, just to be clear, […]

  • We developers don’t like documentation. We don’t like to read it, and we even more we don’t like to write it. But sometimes it is worth to read it. Like when you find out that by using datetime in SQL DB you got a milliseconds precision but only if it ends on 0,3,7 (link). That […]

  • In the previous post we’ve removed some of the technical debt that could be found in our NetDeveloperPoland Website application. In this one we will remove it even more. We can even maybe reach a B? Let’s see where we’ll end up at the end of this part.

Dawno już żadne techniczne posty nie świeciły na kartach tego bloga. Jako, że szybko nie zanosi się na zmianę (tak, ja też nad tym ubolewam) postanowiłem napisać trochę nietechnicznie – przynajmniej tyle się pojawi.

Dziś informacja o “konferencji”, którą organizujemy w ramach grupy Wroc.NET. Piszę “konferencji”, gdyż jest to bardziej rozbudowanie spotkanie grupy niż konferencja z wielkiego zdarzenia, tak czy inaczej warto o niej wspomnieć. Już 7-go grudnia będziemy mieli okazję posłuchać o 3 ciekawych tematach: będące ostatnio na topie Windows Phone 7, oraz Azure oraz bardziej corowo-techniczny o pisaniu wtycznek do .NETtwych aplikacji bez posiadania do nich źródeł. Zapowiada się niezwykle ciekawie.
Ja szczególnie czekam na ten 3 temat. Poniżej dokładne tematy sesji.

  • Bartłomiej Zass – Wprowadzenie do Windows Azure
  • Michał Czerwiński – Studium przypadku: wtyczka w .NET do dowolnego programu w .NET
  • Daniel Dudek – Cykl życia aplikacji WP7

Więcej informacji oraz rejestracja na: http://wroc.devday.pl. Gdyby, ktoś chciał odwiedzić podobne wydarzenie a do Wrocławia miał za daleko to koledzy z łódzkiej grupy organizują podobne wydarzenie 3 dni wcześniej. Szczegóły na http://devday.pl

Zapraszam!

Tydzień temu zaproponowałem mały konkurs, polegający na zmuszenia .NET’a do zrobienia rzeczy, której teoretycznie nie powinno dać się zrobić. Niestety trochę “przedobrzyłem” i jednym z dostępnych rozwiązań rozwiązań było użycie Reflection o co mnie w pytaniu nie chodziło. Świat się jednak nie wali, a jest nawet lepiej. Wiem, że są osoby, które o Reflection nie słyszały i dziwią się, że takie rzeczy w .NET można robić.
Tak więc zobaczmy jakie rozwiązania są dostępne:

Rozwiązania

Podstawowe czyli z użyciem Reflection:

    public class NaughtyByReflectionPlugin : IPlugin
    {       
        public void Execute(PluginData data)
        {
            var memebrs = data.GetType().GetMember(“ConstString”, BindingFlags.Instance | BindingFlags.NonPublic);
            var member = (FieldInfo)memebrs[0];
            member.SetValueDirect(__makeref(data), “Hacking .NET”);
        }       
    }

Takie (lub podobne – oparte na Reflection) rozwiązania nadesłali: Przemysław Mynarski, Marek Grabarz, Jarosław Dubrownik, Krystian Kulig, Paweł Szczygielski, Łukasz Jezior, Adam Rafałko oraz Bo i wiero. Jak już wspominałem nie wszyscy są świadomi Reflection, tak więc dobrze, że i takie rozwiązanie się pojawiło. Dodatkowo duży plus dla Marka za sprawienie, że teraz wiem o słowie kluczowym __makeref – choć i da się bez tego. Widać człowiek uczy się cały czas.
Jaki jest minus takiego rozwiązania? A no to, że w bardzo prosty sposób możemy się przed nim zabezpieczyć. Wystarczy, że nasz program będzie miał zabrane prawa do Reflection i już nie poszalejemy.

    [ReflectionPermission(SecurityAction.Deny, Unrestricted = true)]
    class Program
    {

No więc, co możemy zrobić? Da się to obejść?

Pointery. A co to takiego?

Da się obejść powyższe ograniczenie za pomocą pointerów, ale trzeba posiłkować się kodem unsafe, więc nie wiem czy takie rozwiązanie przeszłoby przy ładowaniu z osobnego assembly. Nie miałem chwili tego sprawdzić. Zobaczmy jednak kod:

public void Execute(PluginData data)
{
    string toWrite = “Hacking .Net”;
    string constdata = String.Intern(“Const data”);
    unsafe
    {
        GCHandle handleConst = GCHandle.Alloc(constdata, GCHandleType.Pinned);
        GCHandle hackString = GCHandle.Alloc(toWrite, GCHandleType.Pinned);
        byte* dst = (byte*)handleConst.AddrOfPinnedObject().ToPointer();
        byte* src = (byte*)hackString.AddrOfPinnedObject().ToPointer();
        //*(dst – 4) = (byte)toWrite.Length;
        for (int i = 0; i < toWrite.Length << 1; i++)
            *dst++ = *src++;
        *dst = 0;
    }
}

Takie rozwiązanie jako alternatywne nadesłał Adam Rafałko. Nie do końca ono spełniało, założenie bo Adam (chyba celowo ;)) użył krótszego stringu niż Hacking .NET i jego kod ucinał kawałek napisu. Aby wyświetlał się cały napis konieczna jest zmiana, którą już ja dodałem – zmiana długości łańcucha w pamięci (zakomentowana linia – którą trzeba by napisać prawidłowo). Dodatkowo plus za sprawienie, że wiem iż jest funkcja String.Intern, choć jeszcze nie wiem co ona daje 🙂

StructLayout

Każdy kto używał P/Invoke zetknął się z takowym atrybutem. Używało się, przy dodawaniu typów nieobecnych w .NET do framework’a aby można z nich było skorzystać z kodu C#. Standardowe użycie takiego parametru wyglądało mniej więcej tak:

    [StructLayout(LayoutKind.Explicit)]
    public struct _PROCESSOR_INFO_UNION
    {
        [FieldOffset(0)]
        internal uint dwOemId;
        [FieldOffset(0)]
        internal ushort wProcessorArchitecture;
        [FieldOffset(2)]
        internal ushort wReserved;
    }

Niby nic podejrzanego, ale co się stanie gdy nasz plugin napiszemy tak?

    [StructLayout(LayoutKind.Explicit)]
    class NaughtyByStructLayoutPlugin : IPlugin
    {
        internal class PluginHack
        {
            public string Text;
        }
        [FieldOffset(0)]
        private PluginData _original;
        [FieldOffset(0)]
        private PluginHack _hack;
        public void Execute(PluginData data)
        {
            _original = data;
            _hack.Text = “Hacking .NET”;
        }
    }

Przeanalizujemy ten kod. Atrybut StructLayout jest wymagany, aby móc ustawiać przesunięcia danych pól w obiekcie. Następnie ustalamy przesunięcia. Widzimy ten atrybut FieldOffset z ustawionym takim samym przesunięciem wynoszącym 0 dla obu obiektów? Wszystko jasne? Dzięki takiemu potraktowaniu obiekt typu PluginData oraz obiekt typu PluginHack będą okupować ten sam fragment pamięci. A dzięki temu, że Text jest publiczny możemy go sobie jawnie przeładować czym tylko chcemy.
O takie właśnie rozwiązanie chodziło. Adrian Ciura oraz Rafał Jasica przesłali właśnie takie rozwiązanie. Brawo!
Jest jeszcze jedno rozwiązanie, choć bardziej z kategorii humorystycznych 🙂

public void Execute(PluginData data)
{
    int cursorPosY = Console.CursorTop;
    new Thread(p =>
    {
        Thread.Sleep(100); // time may vary
        Console.SetCursorPosition(0, cursorPosY);
        Console.WriteLine(“Hacking .NET”);
        Console.SetCursorPosition(0, cursorPosY + 2);
    }).Start();
}

Interesujące? 🙂 Takie rozwiązanie nadesłał także Bo.
Niby takie małe hakowanie, ale czy da się napsuć coś więcej? Pisanie bezpośrednio do pamięci czy też dostawanie się do wewnętrznej pamięci za pomocą innych obiektów może nam już napsuć w kodzie dość mocno. No bo możemy sobie wskazać adres łańcucha na adres np. 0xbaadfeed :). Pytanie, czy za pomocą tych technik można napsuć coś w kodzie metod? Może komuś się uda? Nie wiem jak wy, ale ja uwielbiam analizować rzeczy trochę niżej niż to co mamy dostępne od ręki.
Post był inspirowany wpisem Is .NET Type-Safe? napisanym przez p. Wiktora Zychlę wykładowcę na UWr oraz byłego MVP w kategorii C#.
PS. Co do nagród to muszę jeszcze pomyśleć. Nie spodziewałem się tylko zgłoszeń 🙂

Może nie takie prawdziwe, ale takie malutkie i niewinne 🙂 (a może nie takie niewinne?). Do napisania tego postu zainspirował mnie niedawno przeczytany inny post, pokazujący, jak można zmusić .NET do zrobienia czegoś co nie powinniśmy mieć możliwości zrobić. Jako, że mój post będzie związany z małą zagadką/konkursem na razie linka do inspiracji nie podam. Sposób jest w pełni legalny w .NET bo i program się kompiluje bez żadnych sztuczek. F5 i działa….

Zadanie

Zadanie będzie z typu akademickich, nie będzie pokazane tu jak zrobić coś potencjalnie niebezpiecznego, ale można od tego wyjść już do bardziej groźniejszych przypadków. Tak więc nakreślmy nasze zadanie. Mamy takowe klasy i kod:
PluginData.cs:

    public class PluginData
    {
        private readonly string ConstString = “Const data”;
        public void Print()
        {
            Console.WriteLine(ConstString);
        }
    }

IPlugin.cs:

    interface IPlugin
    {
        void Execute(PluginData data);
    }

No i nasz Program.cs:

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var data = new PluginData();
                var plugin = CreatePlugin();
                plugin.Execute(data);
                data.Print();
            }
            catch (Exception)
            {
                Console.WriteLine(“Error!”);             
            }
            Console.WriteLine(“End”);
            Console.Read();
        }
        private static IPlugin CreatePlugin()
        {
            //jakaś fabryka pluginów i akurat twój jest ładowany
            return new NaughtyPlugin();
        }
    }

Twój kawałek kodu to NaughtyPlugin i w założeniu powinien być ładowany z zewnętrznej biblioteki. Dla uproszczenia można mieć go w tym samym projekcie. Normalny wynik działania takiego kodu z pluginem powinien wyglądać mniej więcej tak:
normal_output
W zależności czy plugin wypisuje coś dodatkowego na konsoli i czy nie rzuca wyjątkiem. Czy da się tak go napisać, aby wynikiem było coś takiego:
output
I oczywiście program kończył swoje działanie? Tak więc oczywiste rozwiązania w którym plugin wypisuje i Hacking .NET i End odpadają ;). End jest wypisywane przez Program.cs.
Czyli opisując słowami czy da się zmienić łańcuch znaków ConstString w obiekcie PluginData? Jest readonly – raczej Reflection nic nie da, a może da? A może dynamic? A może się nie da i tylko to sobie wymyśliłem, że działa mi to w .NET 3.5/4.0 i VS 2008/2010 (choć środowisko nie ma tu znaczenia).
Niech to będzie konkurs – deadline do 29.10.2010 czyli od dzisiaj tydzień – ja mam jedno rozwiązanie (“niskopoziomowe” – niech to będzie jako podpowiedź), ale jak ktoś znajdzie inne działające to tym lepiej. Nagród wielkich nie przewiduje – w zasadzie żadne Chwała i sława. Może jednak znajdę jakąś koszulkę dla zwycięzcy. Oczywiście swoje rozwiązanie także opublikuję. Odpowiedzi w komentarzu są moderowane, więc nie będę ich publikował przed końcem albo na maila (jest w profilu).

Ostatnimi czasy postanowiłem przyjrzeć się bliżej platformie Mono oraz środowisku Mono Develop. Z racji tego, iż będę miał potrzebę wytworzenia kawałka oprogramowanie na Mac OS postanowiłem skorzystać z tych narzędzi w tymże celu, a żeby lepiej mi to poszło trzeba na początku się z nimi zapoznać.
Uwaga: Jeśli chodzi o jakiekolwiek porównania wydajnościowe to testy były robione na wirtualnej maszynie z Windows 7 z 2 GB RAM.

Pobieranie i instalacja

Mono to tylko około 78 MB natomiast Mono Develop to tylko 20 MB. Pestka do przełknięcia na dzisiejszych łączach. Trzeba było jeszcze doinstalować gtk# i można śmigać. Żadnych problemów w tym miejscu.

Wygląd i działanie

Mono Develop po uruchomieniu prawie jak VS 2008:
MonoDevelop-Home
Projekty jakie możemy przy jego pomocy tworzyć to: konsola, konsola IL (więcej poniżej), Gtk# oraz biblioteka. Do tego dochodzi ASP.NET no i Moonlight (czyli Monowski odpowiednik SLa) Niestety nie ma projektów WPF. Możemy używać VB.NET (bez Moonlight’a) jak i C#.
W działaniu nie zauważyłem żadnych spowolnień, ani problemów. Wszystko uruchamia się i działa jak należy. InteliSense podpowiada co trzeba i bez zbędnego opóźnienia. Dostępne mamy jakieś namiastki funkcjonalności refaktoringu, ale nie jest tego tyle ile w samym gołym VS. A R# niestety dla MonoDevelop nie ma.
Prócz tego mamy oczywiście projekty do testów jednostkowych a także metryki kodu. Jest dość dobrze jeśli popatrzeć, że jest to całkowicie darmowe środowisko.

Ciekawostki

Mono Develop oferuje dość ciekawy projekt. Po stworzeniu Console IL, możemy sobie kodować w ILu bezpośrednio w IDE. Bardzo fajna funkcjonalność. Może nie do końca każdemu przydatna w codziennej pracy, ale ja jako maniak low-level stuff chętnie z takiej możliwości bym skorzystał w Visual Studio.
il

Plusy ujemne i minusy dodatnie

Co na plus:

  • Projekt IL. Możliwość kodowania w IL w IDE i debugowanie. Duży plus ode mnie!
  • Informuje o innym modelu programowania w Gtk#. Nie ma pozycjonowania absolutnego tylko pojemniki – WPF-like
  • RegEx toolkit – wiadomo co to 🙂

Co na minus:

  • Nadal nie wiem jak zmusić breakpoint aby był warunkowy. Niby opcje są, ale zawsze niedostępne.
  • Połowiczne spolszczenia. Często widzimy pomieszanie polskiego z angielskim. Jak dla mnie nie musi być polskiego, ale niech to będzie spójne. Da się to oczywiście zmienić na angielski.
  • Brak WPF ;(

Podsumowanie

Mono i Mono Develop to ciekawe alternatywy warte rozważenia i przyjrzenia się im z bliższa. Na razie planów na przejście całkowicie na to środowisko, ale z racji, że prawdopodobnie będę musiał stworzyć coś na MacOS będę miał z nim troszkę do czynienia.

Tytuł tego posta może być lekkim zeskoczeniem. Od premiery VS 2010 sporo czasu już minęło. Postanowiłem jednak spisać kilka nowości jakie udostępnia ono pod kątem debugowania. Zadanie jest o tyle trudne, że prawie wszystko co było do powiedzenia w tym temacie zostało już powiedziane przez pracowników MS w postach takich jak ten napisany przez ScottaGu. Postaram się jednak przedstawić temat z nowej perspektywy.

Dump

minidump
VS w najnowszej wersji umożliwia nam stworzenie dumpa procesu bezpośrednio z poziomu środowiska. Przydatne…

Dump summary

Skoro już jesteśmy przy samym dumpie to VS podaje nam ładne podsumowanie dumpa, którego właśnie zrzuciliśmy. Wystarczy wczytać go do VS aby pokazał nam całkiem ładne podsumowanie tego co w nim mamy.
minidump_summary

FirstChanceException

Czy czasem zastanawiasz się co oznaczają “tajemnicze” komunikaty wyświetlane w okienku Output o treści “A first chance exception of type ‘System.ApplicationException’ occurred in XXX.exe”? Nie będziemy się tu rozpisywać co on oznacza, bo można to przeczytać w poście What is a First Chance Exception? Natomiast to o czym napiszemy to to, że w obecnej wersji VS pozwala nam podpiąć się pod zdarzenie, gdy taki wyjątek jest generowany. Możemy to zrobić za pomocą następującego kawałka kodu:

AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;

Dzięki temu dostaniemy powiadomienia o takich zdarzeniach i możemy na nie reagować.

Dodatkowe Opcje

W sekcji Debugging w opcjach mamy dodatkową pozycję Output Window. Umożliwia ono nam skonfigurowanie kilku dodatkowych opcji ułatwiających znajdywanie błędów szczególnie przydatna jest część odnośnie WPF’a.
options
Możemy powiedzieć jakie informacje o WPF chcemy widzieć w okienku Output. Mamy także możliwość wyłączenia różnych opcji, aby był on bardziej czytelny. Nadal jednak błędny Bindingi nie powodują błędów kompilacji.

Okienko rejestrów

VS posiada (także w poprzednich wersjach) podgląd rejestrów procesora (Debug –> Windows –> Registers, CTRL+ALT+G). To co dołożyli w najnowszej wersji to podgląd wartości dla rejestrów AVX oraz AVX Float. Co to jest? Czytaj tutaj – Advanced Vector Extensions.

Podsumowując

Mam nadzieję, że te kilka “tricków” przyda się wszystkim, którzy od czasu do czasu muszą pobawić się z debuggerem na poziomie wyższym niż ustawienie kilku pułapek. Na pewno nie opisałem wszystkich nowości w tym temacie, np. IntelliTrace. Sporo już o tym napisano i nie ma sensu tego powielać. Za początek niech służą te 4 artykuły na dotnetomaniak.plIntelliTraceZachęcam jeszcze do przejrzenia rzeczy w poście Scott’a Gu. Jest tam także sporo fajnych drobnych usprawnień, które spowodują, że nasz związek z debuggerem będzie przyjemniejszy :).

Jako, że nadal nie mam czasu na pisanie nowych postów (tematów trochę jest – czasu mniej :(), postanowiłem udostępnić coś co kiedyś zostało nagrane, a jakoś nie zostało przedstawione szerszej publiczności.

Poniżej linki do trzech moich (AttachedProperties oraz Style – już był publikowany na blogu, ale na innym serwerze) filmów umieszczonych na Cyber-Flick odnośnie WPF’a. W zamyśle, miał powstać cały cykl filmów o WPF – na razie jest tylko tyle. Jak będzie pozytywny oddźwięk to może powrócę do ponownego nagrywania. Czy nagrywanie takich filmów ma sens? Czy wystarczą posty, które napisałem w ramach cyklu – WPF?

Wszelkie uwagi mile widziane!

PS. Będzie jeszcze 4, ale jak na razie coś źle się przetworzył.

256 Dziś 13. września – 256. dzień roku. Święto wszystkich programistów i programistek. Zatem wszystkiego najlepszego bracia i siostry programistki! Mniej błędów w programach i więcej ciekawych zadań i wyzwań na nadchodzące przyszłe dni!

Zacznę kontrowersyjnie. Lubię jak aplikacja nie działa, tak jak powinna. To znaczy – niekoniecznie lubię sam fakt niepoprawnego działania, ale dzięki temu mam możliwość odpalenia debugger’a (lub innego użytecznego narzędzia – głównie SysInternals), aby dowiedzieć się dlaczego i spróbować to naprawić.
Ostatniego czasu ciekawych wrażeń dostarcza(ło) mi IE8. Pewnego dnia, bez powodu, IE na moim laptopie zaczął raczyć mnie niniejszym błędem jeszcze nawet przed tym jak się dobrze uruchomił:
ie8_small

Jako, że jestem wielkim fanem tego co “uczy” Mark Russinovich oraz Tess Ferrandez oczywiście zabrałem się za zbadanie tego ciekawego przypadku. Pierwsza, rzecz w takich przypadkach to zrobienie dump’a (o ile nie masz ustawionego robienia tego automatycznie). Odpaliłem zatem Menadżera zadań (a raczej jego wersję na sterydach – Process Explorer) i wykonałem dump’a.
dump
Po chwili już mogłem zająć się dochodzeniem o co chodzi, że mój często używany 😉 IE nie może się uruchomić. Jeśli chodzi o badanie dump’ów to chyba(?) nie ma lepszego narzędzia nad WinDbg – część pakietu Debugging Tools For Windows. W zasadzie, przy jego pomocy nawet bardzo nie musimy się wysilać, aby zbadać ten zrzut pamięci. Po odpaleniu programu wybieramy Open crush dump i już po chwili możemy działać. Windbg posiada już polecenie, które przeprowadzi za nas wstępną analizę zrzutu: !analyze –v. Teraz już wystarczy chwilę poczekać na wynik jej działania, jednak już na samym początku widzimy, że coś ciekawego:
no_symbols_blog
Brak symboli przeważnie oznacza 3rd-part dll. Zobaczmy co będzie dalej. Pod koniec analizy mamy stos wywołań przed wywaleniem się aplikacji.
stack
Widzimy, że ostatni wpis, należy do biblioteki, do której brakowało nam symboli. Dowiedzmy się trochę więcej co to za biblioteka. Za pomocą polecenia lmD, możemy zobaczyć wszystkie załadowane biblioteki a klikając na nazwę – SynTPFcs więcej o niej.
lmD
Mamy więcej informacji. Producent – Synaptics, Inc. – acha! Sterownik do TouchPada to chyba właśnie coś od Synaptics. Widzimy też wersję biblioteki – dość stara jak na Win7. Jeśli udamy się na stronę zobaczymy, że istnieje nowszy sterownik niż ten z 2004 roku. Ponieważ jednak, nie używam “zaawansowanych” właściwości tego sterownika, postanowiłem go usunąć z systemu. Wystarczy zatem przejść do Dodaj lub usuń programy i usunąć Synaptics Pointing Device Driver. I znów możemy cieszyć się działającym IE8!

Witam po długiej przerwie wszystkich stęsknionych wpisów odnośnie TPLa.
Pamiętam moje przyrzeczenie odnośnie pisania przynajmniej 1 wpisu na tydzień z tego tematu, ale niestety inne rzeczy nie pozwalają poświęcić się temu tematowi w takim stopniu jakbym sobie tego życzył. No, ale nie tracąc czasu na smętne wstępy przejdźmy do treści…dziś krótko acz przydatnie.

Dziś chciałbym powiedzieć o dość ciekawej funkcjonalności a mianowicie kontynuacjach. Dzięki nim możemy napisać następujący fragment kodu:

Task t = Task.Factory.StartNew(() => DoSthImportant())
.ContinueWith(task => DoSthEvenMoreImportantAfterwards());

Dzięki takiej konstrukcji mamy zapewnione, iż po tym jak wykona się kod w DoSthImportant zostanie wykonany kod w DoSthEvenMoreImportantAfterwards. Jednak siła tego rozwiązania polega na dodatkowych parametrach. Dzięki nim możemy napisać coś takiego:

Task operate = Task.Factory.StartNew(() => Operate())
.ContinueWith(task => LogError(task.Exception), TaskContinuationOptions.NotOnRanToCompletion);

Co osiągamy dzięki takiej konstrukcji? Parametr NotOnRanToCompletion powoduje, że dany kawałek zadania zostanie wykonany, tylko jeśli zadanie je poprzedzająca nie zakończy się powodzeniem. Co to znaczy? A to, że np. pojawi wyjątek lub też zadanie zostanie anulowane. Pretty handy! Typ TaskContinuationOptions daje nam jeszcze kilka innych opcji takich jak: OnlyOnCanceled, OnlyOnFaulted itp. Oczywiście pierwszy przykład jest równoważny wywołaniu ContinueWith z parametrem OnlyOnRanToCompletion. Przydatne, nieprawdaż?

Windbg w akcji Ostatnimi czasu bawię się bardziej zaawansowanym (powiedzmy :-)) debuggowaniem przy użyciu WinDbg. Jak różne jest to od debuggowania w Visual Studio chyba nie trzeba tłumaczyć, ale jeśli ktoś nigdy nie uświadczył to zachęcam do zainstalowania i zobaczenia (link). Dzisiejszy wpis będzie bez C#, .NETów i innych wysokopoziomowych rzeczy. Jeśli od czasu do czasu lubisz pobawić się w niskopoziomowym kodem zapraszam. Wracając jednak do problemu…

Każdy kto bawił się w debuggowanie bez źródeł wie, że nie jest to łatwy kawałek chleba. Mało znaczące kawałki assemblera, brak nazw wywoływanych funkcji a zamiast tego call 450123. Czyż nie łatwiej byłoby nam analizować aplikację gdyby zamiast nieszczęsnego call 450123 widniałoby call ConvertToSth lub coś podobnego? Niestety my, w przeciwieństwie do maszyn, wolimy zapamiętywać nazwy niż ciągli liczb. Czy nie posiadając symboli możemy coś z tym zrobić?

Zastanawiałem się nad tym problemem przeglądając *ironia* bardzo kompletną dokumentację *ironia* windbg doszedłem do wniosku, że można a z pomocą przyjdzie nam funkcja AddSyntheticSymbol. Pozwala ona dodać sztuczny symbol dla danego adresu z przestrzeni danej aplikacji, którą właśnie debuggujemy.  Pierwsze co zrobiłem to spróbowałem znaleźć czy jakaś istniejąca komenda, która zrobiła by to dla mnie. Niestety – nie znalazłem jej na Common Windbg Commands. Prawdopodobnie jeśli takie polecenie istnieje nie jest ono takie common. Jeśli jednak znasz polecenie WinDbg, które potrafi dodać symbol dla danego adresu daj mi znać.

Dość tego bezproduktywnego pisania – przejdźmy do kodowania, ale wcześniej mała uwaga.
UWAGA: Na co dzień nie programuję w C/C++. Choć miałem przygodę z tymi językami – ekspertem w nich się nie czuję i na pewno nie powinieneś brać mojego kodu za przykład do naśladowania. Widzisz błąd w kodzie lub da się coś napisać lepiej? Pisz śmiało!

Rozszerzenia w WinDbg

WinDbg jak większość programów pozwala nam na rozszerzenie jego funkcjonalności za pomocą rozszerzeń/pluginów. Ten wpis nie będzie o tym jak zacząć ich pisanie – jeśli chcesz się tego dowiedzieć możesz przeczytać wpis napisanego przez Toby’iego Opfermana – Debug Tutorial Part 4: Writing WINDBG Extensions (i w ogóle cały kurs jeśli temat cię interesuje). Takie rozszerzenie to nic innego jak dllk’a z odpowiednimi metodami. Tak więc aby zacząć w miarę szybko możemy skorzystać z tego co juz jest przygotowane z tym tutorialu i zabrać się za wypełnianie metody naszym kodem.

ssymbol.add

Nasze rozszerzenie musi posiadać jakąś nazwę (oczywista oczywistość). Jako że, będziemy wykorzystywać metodę AddSyntheticSymbol postanowiłem nazwać ją ssymbol (syntheticsymbol jest za długa). Tak więc nazywamy nasz plik ssymbol a w nim dajemy metodę

/***********************************************************
* !add
*
* Purpose: WINDBG will call this API when the user types !ssymbol.add
*         
*
*  Parameters:
*    !ssymbol.add

*
*  Return Values:
*    N/A
*
***********************************************************/
DECLARE_API (add)
{
  //put code hear
}

Mamy naszą metodę oraz powyżej opisaliśmy jak chcielibyśmy jej użyć. Chcemy mieć możliwość tak jak w przypadku innych metod jako adres podać nie tylko wartość liczbową ale także wyrażenie np. ssymbol esp GetStringValue i chcielibyśmy aby esp zamieniło się na wartośc. Na szczęście nie musimy robić tego sami gdyż jest dostępna metoda GetExpression (oraz GetExpressionEx) – dzięki temu uzyskamy adres, który można zapisać w zmiennej.  Args jest argumentem, który otrzymujemy dzięki DECLARE_API.

DECLARE_API (add)
{
    ULONG64  GetAddress;
    PCSTR name; 
    name = new char[100];   
    GetExpressionEx(args, &GetAddress, &name);
}

Następnie spróbujmy dobrać się do naszej głównej metody – AddSyntheticSymbol. Jako, że jest metodą na interface (IDebugSymbols3) musimy posłużyć się COM (buuu :/).

    1hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    2if (FAILED(hr))
    3{
    4  dprintf(“Error: CoInitializeEx – 0x%08x\n”, hr);
    5  return;
    6}
    7hr = DebugCreate(IID_IDebugSymbols3, (void**)&pDebug);
    8if (FAILED(hr))
    9{     
   10  dprintf(“Error: DebugCreate(IID_IDebugSymbols3) – 0x%08x\n”, hr);
   11  return;
   12}
   13hr = pDebug->AddSyntheticSymbol(GetAddress, strlen(name)-1,   
                               name+1, DEBUG_ADDSYNTHSYM_DEFAULT, NULL);
   14if (FAILED(hr))
   15{
   16  dprintf(“Error: AddSyntheticSymbol – 0x%08x\n”, hr);
   17  return;
   18}

Z COM’ami za dużo do czynienia nie miałem tak więc trochę się wymęczyłem starając się napisać ten kod (i jakby coś było nie tak to także piszcie). Prześledźmy ten kod krok po kroku. Pracując z jakimkolwiek obiektem COM zawsze musimy zainicjalizować całą tą maszynkę a robimy to właśnie za pomocą CoInitializeEx(lub ConInitialize) – bez tego wszystkie wołania nie powiodą się. Z utworzeniem interface’ów IDebugClient oraz IDebugSymbols3. Na początku próbowałem uzyskać je za pomocą zwykłego CoCreateInstance ale wszystkie wywołania kończyły się błędem 0x80040154 – Class not registered. Ehh…COM. Przeglądając jeszcze helpa znalazłem funkcję, która wyglądała na coś co możemy użyć do stworzenia interface’u COM. Dzięki niej możemy stworzyć obiekt typu IDebugSymbol i na nim wywołać naszą metodę dodającą symbol. Pierwszym parametrem jest adres, który uzyskaliśmy z metody GetExpression, następnym długość symbolu (tu nie jestem do końca przekonany czy chodzi o długość łańcucha znaków) oraz samą nazwę. Dajemy +1, aby usunąć spację, która rozdziela argumenty.
Na zakończenie wystarczy po sobie posprzątać i gotowe.

pDebug->Release();
CoUninitialize();   
delete name;

Działanie

Zobaczmy jak działa nasze rozszerzenie. Aby dodatek został wczytany przez WinDbg wystarczy umieścić go w c:\windows. A jak sobie radzi w działaniu?

Poniżej kawałek kodu przed użyciem naszego dodatku:
windbg_before
Czy nie łatwiej byłoby nam się w tym połapać, gdyby zamiast call image00400000+0x5b71a widzieć coś bardziej przyjaznego? Wywołajmy nasze rozszerzenie:
!ssymbol.add 400000+0x5b71a GetStringExpr
windbg_after
Od razu czytelniej!

Epilog

Ten dodatek do Windbg na pewno nie jest kompletny i sporo mu jeszcze brakuje. Przydałaby się możliwość usuwania symboli, gdyż w przypadku gdy będziemy chcieli zmienić jakiś już nazwany, otrzymamy błąd. Funkcja zapisywania dodanych symboli do pliku i ich wczytywania przy kolejnej sesji lub przy przeładowaniu debuggowanego programu także byłaby przydatna. Sporo pracy do zrobienia jeszcze jest, ale pierwszy kroku już wykonany…
Zachęcam do pobierania dodatku oraz do komentowania.
Do pobrania – SyntheticSymbol.zip