SEH,DEP, Compiler,FS:[0], LOAD_CONFIG and PE format

SEH,DEP, Compiler,FS:[0], LOAD_CONFIG and PE format

原贴地址:
http://spaces.msn.com/eparg/blog/cns!59BFC22C0E7E1A76!712.entry
原贴时间:
2006-04-16
原贴作者:
eparg


Recently I am planning some user mode troubleshooting paper. In the exception section, I tried the following code to explain how SEH works in VS2005
 
 
BOOL EXCEPTION=0; //Exception flag.
class ExcepStackMsg //Create local variable of this class to record function info
{
 char* pmsg;
public:
 ExcepStackMsg(char *msg)
 {
  pmsg=(char*)malloc(strlen(msg)+1);
  strcpy(pmsg,msg);
 };
 ~ExcepStackMsg()
 {
  if(EXCEPTION)
   printf("Exception happens: %s/n",pmsg);
  free(pmsg); 
 };
};
EXCEPTION_DISPOSITION
__cdecl
_except_handler(
    struct _EXCEPTION_RECORD *ExceptionRecord,
    void * EstablisherFrame,
    struct _CONTEXT *ContextRecord,
    void * DispatcherContext )
{
 EXCEPTION=1; 
 //printf("got it/n");
    return ExceptionContinueSearch; //Allow C++ handler to run
}

void foo1()

 //printf("in foo1/n");
 //ExcepStackMsg e("foo1");
 throw 1;
};
void foo2()
{
 //printf("in foo2/n");
 //ExcepStackMsg e("foo2");
 foo1(); 
};
void foo3()
{
 //printf("in foo3/n");
 //ExcepStackMsg e("foo3");
 foo2();
};
DWORD WINAPI ThreadProc( LPVOID lpParameter)
{
 
 DWORD myESP;
 void * test=_except_handler;
 DWORD handler = (DWORD)_except_handler;
 //_except_handler(0,0,0,0);
 printf("1/n");
 getchar();
  __asm
     {
   mov  myESP,ESP  //Save Stack register pointers
         push    _except_handler         // Address of handler function
  // push    test         // Address of handler function
         push    FS:[0]          // Address of previous handler
         mov     FS:[0],ESP      // Install new EXECEPTION_REGISTRATION
  // mov  FS:[0],100
     }
 try
 {
 printf("2/n");
  getchar();
 
  foo3();
 }
 catch(...)
 {
  EXCEPTION=0;
  printf("catch/n");
 };
 __asm
    {                          
  mov ESP,myESP   //Restore Stack register pointers
 }
   
 return 0;
}
 
The problem is that, in debug mode, my _except_handler is injected and called fine. However, in release mode, the application exists silently. No AV, just inform me 2nd chance C++ exception. How could it happen?
 
I set bp on kernel32!RaiseException, and then use !exchain to exam, but the result is under my expectation. I have to trace the assembly code step by step.
 
At last, the cause is the ntdll!RtlIsValidHandler function. My _except_handler function is passed in to exam. In release mode, the return value is NULL. However, by dumping the paramer before calling RtlIsValidHanlder to check, everything is fine and the address of my _except_handler is exactly there. The only difference is that in debug mode, the handler is wrapped by a stub jmp; while in release mode, the naked function start address is passed in. Is there any restriction for the exception handler?
 
Checking the assembly code to find out the root cause is not the most effective way. Let me discuss with xwlan the expert tomorrow.
 
PS: I tried to turn off the DEP without help :(
 
 
======30 mins later=========
 
After idle around for a while without any other intresting things, I decied to trace on. I found the RtlIsValidHandler goes into RtlLookupFunctionTable. ok, let me try google, and then I get:
 
https://www.xfocus.net/bbs/index.php?act=ST&f=2&t=58881
 
U know what? This article is from:
 
http://nop.cnblogs.com/
 
The bloger is xwlan....
 
 
=========18 hours later=======
 
xwlan is OOF so I have to go on by myself.
 
After 2.5 hours, I found the problem is related to PE header. During the exception handler dispatch, the OS checks the PE header to find out if LOAD_CONFIG section is avaliable. I am not quite sure if the LOAD_CONFIG section is the exception handler table. If the section is avaliable, the handler must exist in the section. Otherwise, the handler must be in Read_Execute page.
 
The call stack is:
 
0012f5f8 7c832742 ntdll!RtlpImageDirectoryEntryToData32+0x44
0012f618 7c8155dc ntdll!RtlImageDirectoryEntryToData+0x57
0012f634 7c815638 ntdll!RtlCaptureImageExceptionValues+0x32
0012f670 7c813fe2 ntdll!RtlLookupFunctionTable+0xd0
0012f6c4 7c8140b3 ntdll!RtlIsValidHandler+0x24
0012f738 7c82ecc6 ntdll!RtlDispatchException+0x78
0012f738 77e55dea ntdll!KiUserExceptionDispatcher+0xe
0012fa90 10243990 kernel32!RaiseException+0x53
0012fad0 0041154c MSVCR80D!_CxxThrowException+0x50
 
The difference between debug version and release version is explict with windbg output and the PE tool from Matt Pietrek's PE tool in MSDN.
 
Release version
-------------------- 
0:000> dt NtHeaders -r -b
Local var @ 0x12fa6c Type _IMAGE_NT_HEADERS*
0x004000f8
   +0x000 Signature        : 0x4550
   +0x004 FileHeader       : _IMAGE_FILE_HEADER
      +0x000 Machine          : 0x14c
      +0x002 NumberOfSections : 4
      +0x004 TimeDateStamp    : 0x4442f434
      +0x008 PointerToSymbolTable : 0
      +0x00c NumberOfSymbols  : 0
      +0x010 SizeOfOptionalHeader : 0xe0
      +0x012 Characteristics  : 0x103
   +0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER
      +0x000 Magic            : 0x10b
      +0x002 MajorLinkerVersion : 0x8 ''
      +0x003 MinorLinkerVersion : 0 ''
      +0x004 SizeOfCode       : 0xc00
      +0x008 SizeOfInitializedData : 0xe00
      +0x00c SizeOfUninitializedData : 0
      +0x010 AddressOfEntryPoint : 0x1473
      +0x014 BaseOfCode       : 0x1000
      +0x018 BaseOfData       : 0x2000
      +0x01c ImageBase        : 0x400000
      +0x020 SectionAlignment : 0x1000
      +0x024 FileAlignment    : 0x200
      +0x028 MajorOperatingSystemVersion : 4
      +0x02a MinorOperatingSystemVersion : 0
      +0x02c MajorImageVersion : 0
      +0x02e MinorImageVersion : 0
      +0x030 MajorSubsystemVersion : 4
      +0x032 MinorSubsystemVersion : 0
      +0x034 Win32VersionValue : 0
      +0x038 SizeOfImage      : 0x5000
      +0x03c SizeOfHeaders    : 0x400
      +0x040 CheckSum         : 0x8862
      +0x044 Subsystem        : 3
      +0x046 DllCharacteristics : 0
      +0x048 SizeOfStackReserve : 0x100000
      +0x04c SizeOfStackCommit : 0x1000
      +0x050 SizeOfHeapReserve : 0x100000
      +0x054 SizeOfHeapCommit : 0x1000
      +0x058 LoaderFlags      : 0
      +0x05c NumberOfRvaAndSizes : 0x10
      +0x060 DataDirectory    :
       [00] _IMAGE_DATA_DIRECTORY
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [01]
         +0x000 VirtualAddress   : 0x23a4
         +0x004 Size             : 0x3c
       [02]
         +0x000 VirtualAddress   : 0x4000
         +0x004 Size             : 0x1ac
       [03]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [04]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [05]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [06]
         +0x000 VirtualAddress   : 0x20e0
         +0x004 Size             : 0x1c
       [07]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [08]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [09]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [10]
         +0x000 VirtualAddress   : 0x2148
         +0x004 Size             : 0x40
       [11]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [12]
         +0x000 VirtualAddress   : 0x2000
         +0x004 Size             : 0xc4
       [13]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [14]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [15]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
 

Dump of file RSE.EXE
File Header
  Machine:                      014C (I386)
  Number of Sections:           0004
  TimeDateStamp:                4442F434 -> Mon Apr 17 09:49:40 2006
  PointerToSymbolTable:         00000000
  NumberOfSymbols:              00000000
  SizeOfOptionalHeader:         00E0
  Characteristics:              0103
    RELOCS_STRIPPED
    EXECUTABLE_IMAGE
    32BIT_MACHINE
Optional Header
  Magic                         010B
  linker version                8.00
  size of code                  C00
  size of initialized data      E00
  size of uninitialized data    0
  entrypoint RVA                1473
  base of code                  1000
  base of data                  2000
  image base                    400000
  section align                 1000
  file align                    200
  required OS version           4.00
  image version                 0.00
  subsystem version             4.00
  Win32 Version                 0
  size of image                 5000
  size of headers               400
  checksum                      8862
  Subsystem                     0003 (Windows character)
  DLL flags                     0000
  stack reserve size            100000
  stack commit size             1000
  heap reserve size             100000
  heap commit size              1000
  RVAs & sizes                  10
Data Directory
  EXPORT           rva: 00000000  size: 00000000
  IMPORT           rva: 000023A4  size: 0000003C
  RESOURCE         rva: 00004000  size: 000001AC
  EXCEPTION        rva: 00000000  size: 00000000
  SECURITY         rva: 00000000  size: 00000000
  BASERELOC        rva: 00000000  size: 00000000
  DEBUG            rva: 000020E0  size: 0000001C
  ARCHITECTURE     rva: 00000000  size: 00000000
  GLOBALPTR        rva: 00000000  size: 00000000
  TLS              rva: 00000000  size: 00000000
  LOAD_CONFIG      rva: 00002148  size: 00000040
  BOUND_IMPORT     rva: 00000000  size: 00000000
  IAT              rva: 00002000  size: 000000C4
  DELAY_IMPORT     rva: 00000000  size: 00000000
  COM_DESCRPTR     rva: 00000000  size: 00000000
  unused           rva: 00000000  size: 00000000
 
 
Debug version
------------------- 
0:000> dt NtHeaders -r -b
Local var @ 0x12f610 Type _IMAGE_NT_HEADERS*
0x004000f0
   +0x000 Signature        : 0x4550
   +0x004 FileHeader       : _IMAGE_FILE_HEADER
      +0x000 Machine          : 0x14c
      +0x002 NumberOfSections : 6
      +0x004 TimeDateStamp    : 0x4442f2da
      +0x008 PointerToSymbolTable : 0
      +0x00c NumberOfSymbols  : 0
      +0x010 SizeOfOptionalHeader : 0xe0
      +0x012 Characteristics  : 0x103
   +0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER
      +0x000 Magic            : 0x10b
      +0x002 MajorLinkerVersion : 0x8 ''
      +0x003 MinorLinkerVersion : 0 ''
      +0x004 SizeOfCode       : 0x5000
      +0x008 SizeOfInitializedData : 0x5000
      +0x00c SizeOfUninitializedData : 0
      +0x010 AddressOfEntryPoint : 0x1108c
      +0x014 BaseOfCode       : 0x1000
      +0x018 BaseOfData       : 0x1000
      +0x01c ImageBase        : 0x400000
      +0x020 SectionAlignment : 0x1000
      +0x024 FileAlignment    : 0x1000
      +0x028 MajorOperatingSystemVersion : 4
      +0x02a MinorOperatingSystemVersion : 0
      +0x02c MajorImageVersion : 0
      +0x02e MinorImageVersion : 0
      +0x030 MajorSubsystemVersion : 4
      +0x032 MinorSubsystemVersion : 0
      +0x034 Win32VersionValue : 0
      +0x038 SizeOfImage      : 0x1b000
      +0x03c SizeOfHeaders    : 0x1000
      +0x040 CheckSum         : 0
      +0x044 Subsystem        : 3
      +0x046 DllCharacteristics : 0
      +0x048 SizeOfStackReserve : 0x100000
      +0x04c SizeOfStackCommit : 0x1000
      +0x050 SizeOfHeapReserve : 0x100000
      +0x054 SizeOfHeapCommit : 0x1000
      +0x058 LoaderFlags      : 0
      +0x05c NumberOfRvaAndSizes : 0x10
      +0x060 DataDirectory    :
       [00] _IMAGE_DATA_DIRECTORY
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [01]
         +0x000 VirtualAddress   : 0x19000
         +0x004 Size             : 0x3c
       [02]
         +0x000 VirtualAddress   : 0x1a000
         +0x004 Size             : 0x459
       [03]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [04]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [05]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [06]
         +0x000 VirtualAddress   : 0x16620
         +0x004 Size             : 0x1c
       [07]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [08]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [09]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [10]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [11]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [12]
         +0x000 VirtualAddress   : 0x191cc
         +0x004 Size             : 0x190
       [13]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [14]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
       [15]
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
 
Dump of file DBG.EXE
File Header
  Machine:                      014C (I386)
  Number of Sections:           0006
  TimeDateStamp:                4442F2DA -> Mon Apr 17 09:43:54 2006
  PointerToSymbolTable:         00000000
  NumberOfSymbols:              00000000
  SizeOfOptionalHeader:         00E0
  Characteristics:              0103
    RELOCS_STRIPPED
    EXECUTABLE_IMAGE
    32BIT_MACHINE
Optional Header
  Magic                         010B
  linker version                8.00
  size of code                  5000
  size of initialized data      5000
  size of uninitialized data    0
  entrypoint RVA                1108C
  base of code                  1000
  base of data                  1000
  image base                    400000
  section align                 1000
  file align                    1000
  required OS version           4.00
  image version                 0.00
  subsystem version             4.00
  Win32 Version                 0
  size of image                 1B000
  size of headers               1000
  checksum                      0
  Subsystem                     0003 (Windows character)
  DLL flags                     0000
  stack reserve size            100000
  stack commit size             1000
  heap reserve size             100000
  heap commit size              1000
  RVAs & sizes                  10
Data Directory
  EXPORT           rva: 00000000  size: 00000000
  IMPORT           rva: 00019000  size: 0000003C
  RESOURCE         rva: 0001A000  size: 00000459
  EXCEPTION        rva: 00000000  size: 00000000
  SECURITY         rva: 00000000  size: 00000000
  BASERELOC        rva: 00000000  size: 00000000
  DEBUG            rva: 00016620  size: 0000001C
  ARCHITECTURE     rva: 00000000  size: 00000000
  GLOBALPTR        rva: 00000000  size: 00000000
  TLS              rva: 00000000  size: 00000000
  LOAD_CONFIG      rva: 00000000  size: 00000000
  BOUND_IMPORT     rva: 00000000  size: 00000000
  IAT              rva: 000191CC  size: 00000190
  DELAY_IMPORT     rva: 00000000  size: 00000000
  COM_DESCRPTR     rva: 00000000  size: 00000000
  unused           rva: 00000000  size: 00000000
 
 
However, I am not sure if it is related to DEP, and why we need to check LOAD_CONFIG. Also, if we compile the code in VC6, we may get different results. Any hint?
 
======
Move xwlan's comment here:
 
if load config is available, os assume the exception handlers can be looked up in the table, if load config is not available, the os is not sure about whether the handler is valid or not, so it will simply invoke the handler.
my guess is:
it actually relate with safe exception handlers. compiler/linker generated handlers, new MS C compiler will add security cookie to ensure the stack is ok when invoke exception handler, the corresponding RVA is added to the look up table. so manually embedded assembler seh handler won't appear in the table,
os will consider it's an invalid handler.
 
a workaround is to sweep the load config directory in PE image, dynamically programming...
 
a trick to fool the OS...
modify image file as your own exe name.
 
// =========================================================================
 HMODULE h = GetModuleHandle(L"exchain.exe");
 PUCHAR Base = (PUCHAR)h;
 ULONG OldProtect;
 VirtualProtect(Base, 0x1000, PAGE_READWRITE, &OldProtect);
 IMAGE_NT_HEADERS *NtHeader = ImageNtHeader(Base);
 NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress = 0;
 NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size = 0;
 VirtualProtect(Base, 0x1000, PAGE_READONLY, &OldProtect);
 //
===========30 hours later==================
 
Thanks Mike Marcelais for the following explanation:
 

Basically, you're running afowl of /SafeSEH.  /SafeSEH is incompatable with incremental linking, so it is probably disabled in debug builds.

 

What /SafeSEH is designed to do is prevent a buffer overrun (or other exploitable code) from overwriting the exception handler on the stack and point it at the hacker's code.  The way they do this is at compile time, a list of all valid exception handlers is built up, and at runtime, before a handler is called, it is checked to see if it is on the list.  [The loadconfig structure you find gives that list, and a bit in the header says whether the binary was built with /SafeSEH or not.]

 

If your ASM code was in an .asm file, then you'd use the .safeseh directive to tell the assembler (and hence, the linker), that this function is an exception handler function.  I don't know any mechansim in inline asm that will achieve the same effect.

 

If you compile with warnings enabled, you get warning C4733 telling you that you're going to explode at runtime. 

你可能感兴趣的:(exception,image,header,compiler,linker,Pointers)