About the / SAFESEH compiler option

Introduction

In this story, I will tell you about an exciting adventure that led me to solve a riddle that I asked myself. The solution is a small detail in the 32-bit application loader mechanism in Windows 7 and above, and the process of solving is a long journey of a warrior who follows the path of the heart.





If you came to this page looking for an answer to a question, then watch the spoiler below, because the main content may be of interest to those who are just trying to understand the SEH mechanism.





The whole problem was that I used the incorrect description of the dataDirectories structure, taken from Wikipedia. In place of the boundImport field, there is actually the loadConfigTable field , which points to the IMAGE_LOAD_CONFIG_DIRECTORY32 structure. This structure contains the SEHandlerTable field. If it is zero, then / SAFESEH is disabled. If it is a virtual pointer to the table of safe handlers, then only those handlers specified in the table will work. The table is a list of offsets relative to the virtual address of the .text section. The number of handlers is set in the SEHandlerCount field.





How it all started

Thinking about a way to protect my program from copying, I remembered the article by Chris Kaspersky, in which I learned about the existence of anti-debugging techniques. Trying in vain to implement the example Chris analyzed in my program, I ran into a wall of confusion on the part of Windows. The problem was that Windows refused to use my exception handler, considering it "unsafe".





More or less details about SEH I was able to glean from this article . But, nevertheless, until I myself examined this mechanism with a magnifying glass and tools, I had very vague ideas about its structure. I unwound the chains of handlers, set breakpoints on them, observed the order of their execution, considered the return values, etc.





Get to the point

  ( Microsoft Visual C++ )





int main()
{
    __asm
    {
        mov eax, DWORD PTR SS : [0]
    }
}
      
      



, eax 0.





, OllyDbg.





«access violation when reading 0x00000000».





, , 0 - 6 . Eip, ( ). 6 : 36 A1 00 00 00 00.





  , __try, __except .. .





  !





  , - :





    struct _EXCEPTION_RECORD* exceptionRecord,
    void* establisherFrame,
    struct _CONTEXT* contextRecord,
    void* dispatcherContext
      
      



exceptionRecord – , , , contextRecord – ( Eip). - __cdecl, :





typedef enum _EXCEPTION_DISPOSITION
{
    ExceptionContinueExecution,
    ExceptionContinueSearch,
    ExceptionNestedException,
    ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;
      
      



:





EXCEPTION_DISPOSITION __cdecl ExceptionHanler(
    struct _EXCEPTION_RECORD* exceptionRecord,
    void* establisherFrame,
    struct _CONTEXT* contextRecord,
    void* dispatcherContext
)
{
    contextRecord->Eip += 6;
    MessageBoxA(0, "Exteption was handled", "Success!", 0);
    return ExceptionContinueExecution;
}
      
      



Eip 6 , ! : ExceptionContinueExecution – . , Eip + 6 .





- . , . , , . - , : Next Handler. Next , ( , ). Handler -. TIB (thread information block), , fs:[0]. . 





main:





int main()
{
	__asm
	{
    push ExceptionHanler //    -
		push fs:[0]          //       
		                     //    ,  2 :  
    										 //     

		mov fs:[0] , esp	   //    ,                                                                                                                            
                         //   fs:[0]

		mov eax, DWORD PTR SS:[0] //    0
    
		add esp, 8			          // ,  8  ( )
	}
}

      
      



, - /SAFESEH , , - fs:[0]. , .





– 0 «» !





 , . . /SAFESEH:NO.





 , , :





«» . .





, -, , ? ? , … , , /SAFESEH, , , mov fs:[0], esp, -, , ? , /SAFESEH .





, - « ». , ? ? , , ? …





  , …





, , crt0 ( main), ntdll.dll. , fs:[0]! , , ?





. , /SAFESEH, , , fs:[0] JMP ExceptionHandler ( ). ! ! , «»? , . .





entry point main EXE , /SAFESEH /SAFESEH:NO, , WinApi , . . , , . .





crt0 -. , ! push main, ret. , main, ret «» main main , crt0! ( JMP, )





, ! , : EXE, , , .





  , PE ? - LoaderFlags? … , rdata. rdata , , ? , …





, - , . ! , , , -, , . , . , rdata , importTable, debug boundImport. , /SAFESEH! dataDirectories . , , - , /SAFESEH , « EXE ».





, «» , EXE . - . , 2 , , , . , , , ( 3-) .





, «» , mov eax, DWORD PTR SS : [0] play. . play.





! ntdll.dll.





. 0x1341d20 rdata 0x1be0, , EBX – 0x1000. , imageBase + 0x1000 ! - . 0x1be0, 0x2080 0x2351 . ! !





0x1341d20 0x1be0 0x1000 … !





! Windows, . ntdll.dll, !





, .





, . , - , – 3, .





- -.





! , /SAFESEH , /SAFESEH:NO!





, ?





… - , … Total Commander?!





, PE ? boundImport. Microsoft PE .





… , Microsoft boundImport. - -? ?





, .. , except… .. , .sxdata - . . sxdata? . boundImport, - -.





... , , pdata . , ? , boundImport, , .





... The Load Configuration Structure. ! , IMAGE_LOAD_CONFIG_DIRECTORY - , SEH.





, -. , , boundImport IMAGE_LOAD_CONFIG_DIRECTORY32 . ! , .





That's all, the whole secret was that boundImport in the PE header (decomposed according to the table from Wikipedia) points to the IMAGE_LOAD_CONFIG_DIRECTORY32 structure. This structure contains fields that radically change how the program works! Now we can confidently create our own handler table and add only what we think is necessary to it!





Maybe somewhere in the depths of the documentation there is information that boundImport is already called wrong on my platform? Or information that his appointment has changed? Maybe boundImport was excluded or shifted?








All Articles