14/04/2015

What I've learned about .NET from security recommendations for C/C++ applications

Home

Some time ago I had an occasion to read about security tips and tricks for C/C++. I don't use C/C++ in my day to day work however it was interesting. I also started looking for information if these recommendations apply to .NET and thanks to that I learned a few new things.

ASLR

ASLR (Address Space Layout Randomization) is a security feature introduced in Windows Vista (it is also common in other operating systems) that causes that locations of executables/libraries/stack/heap in the memory are selected randomly. For example it minimizes a chance to perform a successful buffer-overflow attack.

ASLR is not turned on for all programs but only for these that are ASLR-compatible. It is controlled by a linker option /DYNAMICBASE besides it can be enabled/disabled by editbin tool. By default this flag is set to ON in Visual Studio.

The good information is that ASLR has been supported by ngen since .NET 3.5 SP1.

VirtualAlloc vs HeapAlloc

Another recommendation says that in order to allocate memory VirtualAlloc method should be used instead of HeapAlloc because the later can bypass ASLR (for details see also this article).

I asked a question on Stack Overflow how it is implemented in .NET and the answer is that .NET uses VirtualAlloc. However, my understanding is that we shouldn't be worried because CLR effectively provides its own ASLR.

DEP

DEP (Data Execution Prevention) is another security feature that doesn't allow one to execute areas of memory that are marked as not-executable. i.e. they contain data and not code. Similarly to ASLR there is a linker flag /NXCOMPACT that enable/disable this feature and it has been used in .NET framework since .NET 2.0 SP1.

It is also worth mentioning that in practise NXCOMPACT affects only 32 bit processes. 64bit process always use DEP and it is not possible to disable it (see also this article or this article). As to 32bit processes, I heard the recommendation to explicitly call SetProcessDEPPolicy function at the beginning of 32bit program (also in .NET) to assure that DEP will be used.

EncodePointer and Decode Pointer

Everybody knows what are events and delegates in .NET and we use them everyday. The equivalent of delegates in C/C++ are function pointers. I was really surprised when I read that it is not recommended to use them directly, for example as callbacks.

Instead, they should obfuscated and de-obfuscated when needed by using EncodePointer/DecodePointer functions. It is a concept somehow similar to ASRL. The goal of this technique is to make it difficult to predict a pointer value and override it so that it will point some malicious code.

I couldn't find information if .NET uses these functions internally so I asked a question on Stack Overflow. The answer is that probably .NET doesn't use them..

Safe Structured Exception Handling

Simplifying, structured exceptions are exceptions on the operating system level. Every structured exception has a handler that is executed when the exception occurs. It is important that it is potentially possible to override an address of this handler and perform an attack.

Safe SEH is a security mechanism that doesn't allow one to do so by providing a table of possible handlers. It is controlled via /SAFESEH linker flag but again it does matter only for 32 bit processes.

It seems to me that .NET doesn't use this flag because I found this flag disabled in the make file of Core CLR. However, one of guys who answered my question on Stack Overflow says that .NET uses a table lookup for exception handlers, not pointers on the stack, what gives the same result as SAFESEH.

08/04/2015

dotPeek as a Symbol Server

Home

I think that you must have heard about .NET Source Stepping feature in Visual Studio which allow a developer to debug .NET. It is a cool thing but my experience shows that it sometimes works and sometimes not. I think that it happens because Visual Studio can have problems to download appropriate version of symbols from Microsoft Symbol Server. However, recenly I've figured out that it can be done in a different way i.e. we can use the free .NET decompiler dotPeek from JetBrains. To be honest I've been using dotPeek since .NET Reflector is not free and it works great but for a long time I wasn't aware that dotPeek can play a role of a symbol server. When I told about this to my friends they were also surprised (in a positive way) so here is a short How-to.
  1. Run dotPeek.
  2. Select Tools -> Start Symbol Server
  3. By default a server will be available under  http://localhost:33417
  4. Start and configure Visual Studio.



  5. Now Visual Studio will be trying to download symbols and source  code from dotPeek.
  6. dotPeek will be generating them (symbols and source code) in flight by decompiling assemblies .
  7. Visual Studio caches symbols on the disk so  dotPeek doesn't have to be running all the time.
  8. It is worth mentioning that you can set a breakpoint in decompiled source code!
  9. If for some reasons Visual Studio will not download symbols from dotPeek you can enforce this from Modules or Call Stack windows in Visual Studio. To do so select Load Symbols command from the context menu.
  10. You have to remember about one drawback. If you enable external symbol server in Visual Studio it may cause that starting a debugging session will take considerable longer time because IDE will try to download symbols for all assemblies.

07/04/2015

Polish -> English

Home

It looks like that I haven't been blogging for 2 months and it is the longest  break I've ever had. I decided to put aside blogging because I wanted to focus on something else. However, after these 2 months I realized that I was simply missing blogging. Therefore I decided to return to writing posts and to change something in my blog. I've been thinking about writing in English for some time so finally I decided to do this step. Why?

First and foremost English is lingua franca of IT world. Majority of books, articles, blogs etc. are in English, all or almost all IT guys know English to some extent so writing in this language means potentially wider audience. Secondly, I'm aware that my English is far from being perfect so blogging in English will be a good occasion to improve my skills in this area.

For some bloggers a decision to start blogging in English might be difficult because of one thing. They write very, very well in their native language. I mean that everybody can write something. It might be useful, it may help someone, it might be interesting and it might be  written generally well, grammatically correctly, without typos but nonetheless it is not written like a good book. There are people who can do that but unfortunately I'm not one of them. However, it also means that I'm not afraid that my style of writing will lost a lot after switching to English ;)

Another reason behind my decision is of different nature. I remember a few occasions when I was asked about my blog by head hunters or interviewers. They were interested in what I'm writing about, one of them even used Google Translate to read my posts! So why not to make their life easier :)

Last but not least I'd like to thank Piotr Sowa from Coding By To Design blog for sharing his thoughts about blogging in English with me. Thanks Piotr!

As to technical aspects of blogging in English. I decided to translate the layout of blog but I'll leave old posts and labels (tags) as they are. Maybe, in future I'll translate the most popular and interesting of them but for now I'd like to focus on producing a new content. What do you think about this approach?

28/01/2015

Jak znaleźć brakujące indeksy w bazie danych?

Home

Optymalizacja bazy danych i zapytań to temat rozległy i szeroki jak morze i nie jedną książka napisano na ten temat. Ja dzisiaj napiszę o dosyć prostej technice pozwalającej znaleźć brakujące indeksy w bazie danych MSSQL. Zapewne każdy korzystający z MSSQL Management Studio wie, że można poprosić o wyświetlenie planu wykonania zapytania (opcje Dispaly Actual Execution Plan oraz Include Actual Execution Plan). Dodatkowo po wykonaniu zapytania MSSQL zasugeruje nam jakich indeksów brakuje.

Fajnie, ale co w sytuacji kiedy widzimy, że nasza aplikacja działa wolno. Mamy podejrzenie, że problem dotyczy bazy danych, ale przecież nie będziemy uruchamiali każdego możliwego zapytania w SSMS. W takiej sytuacji możemy de facto użyć tej samej funkcjonalności co w przypadku uruchamiania zapytania z SSMS. Mam tutaj na myśli Missing Indexes Feature, która jest cechą MSSQL, a nie środowiska SSMS. Informacje o brakujących indeksach silnik bazy danych odkłada mianowicie w kilku widokach systemowych z rodziny sys.dm_db_missing_index_*. Wystarczy więc uruchomić aplikację i zobaczyć jakie indeksy sugeruje nam MSSQL. Ja w tym celu używam zapytania, które znalazłem na blogu SQL Authority.

Przykład z życia. Ostatnio musiałem zoptymalizować pewne obliczenia i postąpiłem dokładnie jak napisałem wyżej. Uruchomiłem w aplikację, zmierzyłem czas obliczeń, zapisałem czas ich uruchomienia i zakończenia, a następnie wyświetliłem listę sugerowanych indeksów do utworzenia. Było ich 6. Na początek odrzuciłem te o niskiej wartości w kolumnie Avg_Esitmated_Impact. Z pozostałych indeksów 2 różniły się tym, że jeden miał klauzulę INCLUDE, a drugi nie. Stwierdziłem, że w pierwszym podejściu skupię się na jednym.

W dalszej kolejności wykonałem testy aby zobaczyć jaki uzysk daje założenie każdego z tych 3 indeksów, a także 2 z nich czy wszystkich 3. Okazało się, że zastosowanie jednego z nich skrócił czas obliczeń o ponad 30%, a pozostałe dwa o małe kilka. Dla rzetelności testy powtórzyłem, a wyniki uśredniłem. Na koniec dokładnie przeanalizowałem proponowany indeks i porównałem go do indeksów już utworzonych na tabeli. Okazało się, że istniał już bardzo podobny indeks. Konkretnie, MSSQL zaproponował coś takiego:
CREATE INDEX IX_Test ON dbo.Table(Col_1, Col_2) INCLUDE (Col_4);
A istniejący indeks wyglądał tak:
CREATE INDEX IX_Test ON dbo.Table(Col_1, Col_2, Col_3);
Wystarczyło, wieć go zmodyfikować w następujący sposób:
CREATE INDEX IX_Test ON dbo.Table(Col_1, Col_2, Col_3) INCLUDE (Col_4);
Na koniec sprawdziłem jak taka modyfikacja wpływa na operacje wstawiania/aktualizacji danych do/w docelowej tabeli. W tym celu napisałem zapytania wstawiające setki tysięcy rekordów do tej tabeli, a także takie, które modyfikuje kolumnę Col_4.. Wyniki pokazały niewielkie spadek wydajności. Był on znacznie mniejszy niż zysk przy odczycie danych, a po drugie wiedziałem, że w praktyce omawiana tabela jest częściej czytana niż modyfikowana.

Przy pracy z Missing Indexes Feature warto wiedzieć o kilku dodatkowych rzeczach. MSSQL może nam zasugerować wiele brakujących indeksów i nie koniecznie wszystkie muszą dotyczyć zapytać wykonanych przez nas. Aby wyeliminować ten problem sugeruję wykonywanie takich ćwiczeń na dedykowanej bazie danych. Przydatne będą też kolumny last_user_seek oraz last_user_scan z widoku sys.dm_db_missing_index_group_stats. Zawierają one informacje o tym kiedy dany brakujący indeks był potrzebny. Po pierwsze podany czas możemy porównać z czasem uruchomienia/zakończenia obliczeń i odrzucić te indeksy, które nie mieszczą się w tym zakresie. Po drugie te czasy mogą zgrubnie wskazać, w którym momencie działania aplikacji występuje problem. Napisałem, że przy wyborze indeksów do dalszej analizy bazowałem na kolumnie Avg_Esitmated_Impact. Trzeba na to jednak uważać. Ta wartość to tylko pewne przybliżenie i może nas wyprowadzić na manowce. Z 3 indeksów jakie wybrałem do dalszej analizy największy zysk miał ten o najmniej wartości w tej kolumnie.

Końcowa uwaga jest taka, że Missing Indexes Feature to pomocna rzecz, ale nie jest to magiczna formuła, która rozwiąże wszystkie problemy za nas. Ma też swoje ograniczenia, o których należy wiedzieć.

Podsumowując:
  • MSSQL sugeruje brakujące indeksy.
  • Brakujące indeksy można odczytać z bazy danych.
  • Testy wydajności należy powtórzyć kilka razy.
  • Testy wydajności dobrze wykonywać w dedykowanym do tych celu środowisku.
  • Missing Indexes Feature to nie magiczna formuła i ma swoje ograniczenia.
  • Proponowane brakujące indeksy należy zawsze poddać analizie i porównać do istniejących indeksów.
  • Należy pamiętać, że indeksy spowalniają operacje aktualizacji i wstawiania danych.
  • Wartość w kolumnie Avg_Esitmated_Impact należy traktować ostrożnie.

22/01/2015

DateTime.Parse vs DateTime.ParseExact

Home

Ostatnio spotkałem się z taka sytuacją. Ktoś zdefiniował sobie dość specyficzny format daty na swoje potrzebny. Wszystko było ok kiedy był on używany do zamiany typu DateTime na ciąg znaków. Problemy zaczęły się przy próbie wykonania operacji odwrotnej. Okazało się, że rodzina metod DateTime.(Try)Parse sobie z tym nie radzi. Rozwiązaniem okazało się użycie metod z rodziny DateTime.(Try)ParseExact, która od tych wcześniejszych różni się tym, że jawnie wymaga podania formatu, który ma zostać użyty przy parsowaniu.

Postawione pytanie brzmiało czemu DateTime.(Try)Parse nie działa w tym przypadku, a w innych tak? Moja odpowiedź jest taka. Nie wiem czemu podjęto taką decyzję, ale DateTime.(Try)Parse nie obsługuje wszystkich możliwych formatów i to nawet jeśli kultura używana przez aplikację zawiera wszystkie potrzebne informacje. Oto fragment z dokumetnacji:

If you parse a date and time string generated for a custom culture, use the ParseExact method instead of the Parse method to improve the probability that the parse operation will succeed. A custom culture date and time string can be complicated, and therefore difficult to parse. The Parse method attempts to parse a string with several implicit parse patterns, all of which might fail.

A to jeszcze jeden:

The Parse and TryParse methods do not fully iterate all strings in patterns when parsing the string representation of a date and time. If you require a date and time string to have particular formats in a parsing operation, you should pass the array of valid formats to the DateTime.ParseExact...

W skrócie DateTime.(Try)Parse jest z założenia "upośledzone" i nie umie wszystkiego. Dlaczego? Może dlatego, że obsłużenie wszystkich możliwych przypadków jest bardzo trudne? A może dlatego, że gdyby napisano kombajn obsługujący wszystko to działałby wolno? To tylko zgadywanie, ale trzeba pamiętać, że:

Jeśli używamy własnych formatów daty i czasu to zaleca się użycie DateTime.(Try)ParseExact.