Exetools

Exetools (https://forum.exetools.com/index.php)
-   General Discussion (https://forum.exetools.com/forumdisplay.php?f=2)
-   -   Reverse engineering MS C++ exception handling (https://forum.exetools.com/showthread.php?t=19194)

jonwil 04-09-2019 20:19

Reverse engineering MS C++ exception handling
 
I have a function (compiled originally with VS .NET 2003) in IDA that looks like this:
Code:

.text:10038AD0 ; int __thiscall W3dExportClass::DoExport(W3dExportClass *this, const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options)
.text:10038AD0 W3dExportClass__DoExport proc near      ; DATA XREF: .rdata:1007AE04↓o
.text:10038AD0
.text:10038AD0 anonymous_0    = dword ptr -0E58h
.text:10038AD0 var_E50        = dword ptr -0E50h
.text:10038AD0 var_23C        = RawFileClass ptr -23Ch
.text:10038AD0 var_220        = dword ptr -220h
.text:10038AD0 Caption        = byte ptr -21Ch
.text:10038AD0 Dir            = byte ptr -118h
.text:10038AD0 var_14          = dword ptr -14h
.text:10038AD0 var_10          = dword ptr -10h
.text:10038AD0 var_C          = dword ptr -0Ch
.text:10038AD0 var_4          = dword ptr -4
.text:10038AD0 name            = dword ptr  8
.text:10038AD0 ei              = dword ptr  0Ch
.text:10038AD0 i              = dword ptr  10h
.text:10038AD0 suppressPrompts = dword ptr  14h
.text:10038AD0 options        = dword ptr  18h
.text:10038AD0
.text:10038AD0 ; FUNCTION CHUNK AT .text:10072A00 SIZE 0000000B BYTES
.text:10038AD0
.text:10038AD0 ; __unwind { // SEH_10038AD0
.text:10038AD0                push    ebp
.text:10038AD1                mov    ebp, esp
.text:10038AD3                push    0FFFFFFFFh
.text:10038AD5                push    offset SEH_10038AD0
.text:10038ADA                mov    eax, large fs:0
.text:10038AE0                push    eax
.text:10038AE1                mov    large fs:0, esp
.text:10038AE8                sub    esp, 0E44h
.text:10038AEE                mov    eax, ___security_cookie
.text:10038AF3                push    ebx
.text:10038AF4                push    esi
.text:10038AF5                mov    esi, ecx
.text:10038AF7                push    edi
.text:10038AF8                mov    edi, [ebp+i]
.text:10038AFB                xor    ebx, ebx
.text:10038AFD                mov    [ebp+var_14], eax
.text:10038B00                mov    eax, [ebp+ei]
.text:10038B03                mov    ecx, edi        ; this
.text:10038B05                mov    [esi+W3dExportClass.ExpInt], eax
.text:10038B0B                mov    [esi+W3dExportClass.Int], edi
.text:10038B11                mov    [esi+W3dExportClass.field_47C], ebx
.text:10038B17                mov    [esi+W3dExportClass.OriginNodeList], ebx
.text:10038B1D                mov    [esi+W3dExportClass.DamageNodeList], ebx
.text:10038B23                mov    [esi+W3dExportClass.HierarchyStruct], ebx
.text:10038B29                mov    edx, [ecx]
.text:10038B2B                mov    [ebp+var_10], esp
.text:10038B2E                mov    [ebp+var_220], esi
.text:10038B34 ;  try {
.text:10038B34                mov    [ebp+var_4], ebx
.text:10038B37                call    [edx+InterfaceVtable.GetTime]
.text:10038B3D                mov    [esi+W3dExportClass.Time], eax
.text:10038B43                call    ds:GetFrameRate(void)
.text:10038B49                mov    [esi+W3dExportClass.FrameRate], eax
.text:10038B4F                push    ebx            ; Ext
.text:10038B50                lea    eax, [ebp+Caption]
.text:10038B56                push    eax            ; Filename
.text:10038B57                mov    eax, [ebp+name]
.text:10038B5A                lea    ecx, [ebp+Dir]
.text:10038B60                push    ecx            ; Dir
.text:10038B61                lea    edx, [ebp+ei]
.text:10038B64                push    edx            ; Drive
.text:10038B65                push    eax            ; FullPath
.text:10038B66                mov    [esi+W3dExportClass.field_478], 2
.text:10038B70                call    __splitpath
.text:10038B75                lea    ecx, [ebp+Dir]
.text:10038B7B                push    ecx
.text:10038B7C                lea    edx, [ebp+ei]
.text:10038B7F                push    edx
.text:10038B80                push    offset aSS      ; "%s%s"
.text:10038B85                push    offset buf ; char *
.text:10038B8A                call    _sprintf
.text:10038B8F                lea    eax, [ebp+Caption]
.text:10038B95                add    esp, 24h
.text:10038B98                lea    edx, [eax+1]
.text:10038B9B                jmp    short loc_10038BA0
.text:10038B9B ; ---------------------------------------------------------------------------
.text:10038B9D                align 10h
.text:10038BA0
.text:10038BA0 loc_10038BA0:                          ; CODE XREF: W3dExportClass__DoExport+CB↑j
.text:10038BA0                                        ; W3dExportClass__DoExport+D5↓j
.text:10038BA0                mov    cl, [eax]
.text:10038BA2                inc    eax
.text:10038BA3                cmp    cl, bl
.text:10038BA5                jnz    short loc_10038BA0
.text:10038BA7                sub    eax, edx
.text:10038BA9                cmp    eax, 0Fh
.text:10038BAC                jbe    short loc_10038BC5
.text:10038BAE                cmp    [ebp+suppressPrompts], ebx
.text:10038BB1                jnz    short loc_10038BC5
.text:10038BB3                push    ebx            ; uType
.text:10038BB4                push    offset aWarning ; "Warning"
.text:10038BB9                push    offset aWarningW3dFile ; "Warning: W3D filenames should be 15 cha"...
.text:10038BBE                push    ebx            ; hWnd
.text:10038BBF                call    ds:MessageBoxA
.text:10038BC5
.text:10038BC5 loc_10038BC5:                          ; CODE XREF: W3dExportClass__DoExport+DC↑j
.text:10038BC5                                        ; W3dExportClass__DoExport+E1↑j
.text:10038BC5                mov    eax, [edi]
.text:10038BC7                mov    ecx, edi        ; Interface *
.text:10038BC9                call    [eax+InterfaceVtable.GetCurFilePath]
.text:10038BCF                mov    ecx, eax
.text:10038BD1                call    ds:CStr::operator char *(void)
.text:10038BD7                push    ebx            ; Ext
.text:10038BD8                push    ebx            ; Filename
.text:10038BD9                lea    ecx, [ebp+Dir]
.text:10038BDF                push    ecx            ; Dir
.text:10038BE0                lea    edx, [ebp+ei]
.text:10038BE3                push    edx            ; Drive
.text:10038BE4                push    eax            ; FullPath
.text:10038BE5                call    __splitpath
.text:10038BEA                lea    eax, [ebp+Dir]
.text:10038BF0                push    eax
.text:10038BF1                lea    ecx, [ebp+ei]
.text:10038BF4                push    ecx
.text:10038BF5                lea    edx, [esi+W3dExportClass.Path]
.text:10038BF8                push    offset aSS      ; "%s%s"
.text:10038BFD                push    edx            ; char *
.text:10038BFE                call    _sprintf
.text:10038C03                mov    eax, [ebp+suppressPrompts]
.text:10038C06                add    esp, 24h
.text:10038C09                push    eax            ; suppressPrompts
.text:10038C0A                mov    ecx, esi        ; this
.text:10038C0C                call    W3DExportClass__DoExportDialog
.text:10038C11                cmp    al, bl
.text:10038C13                jz      loc_10038DA5
.text:10038C19                cmp    [esi+W3dExportClass.ExportStruct.ExportSkeleton], bl
.text:10038C1F                jnz    short loc_10038C35
.text:10038C21                cmp    [esi+W3dExportClass.ExportStruct.ExportAnimation], bl
.text:10038C27                jnz    short loc_10038C35
.text:10038C29                cmp    [esi+W3dExportClass.ExportStruct.ExportGeometry], bl
.text:10038C2F                jz      loc_10038DA5
.text:10038C35
.text:10038C35 loc_10038C35:                          ; CODE XREF: W3dExportClass__DoExport+14F↑j
.text:10038C35                                        ; W3dExportClass__DoExport+157↑j
.text:10038C35                push    ebx
.text:10038C36                call    CreateLogDialog
.text:10038C3B                mov    ecx, [ebp+name]
.text:10038C3E                add    esp, 4
.text:10038C41                push    ecx            ; filename
.text:10038C42                lea    ecx, [ebp+var_23C] ; this
.text:10038C48                call    RawFileClass__RawFileClass
.text:10038C4D                push    2              ; a2
.text:10038C4F                lea    ecx, [ebp+var_23C] ; this
.text:10038C4F ;  } // starts at 10038B34
.text:10038C55 ;  try {
.text:10038C55                mov    byte ptr [ebp+var_4], 1
.text:10038C59                call    RawFileClass::Open(int)
.text:10038C5E                cmp    eax, ebx
.text:10038C60                jnz    short loc_10038C8B
.text:10038C62                push    10000h          ; uType
.text:10038C67                push    offset aError  ; "Error"
.text:10038C6C                push    offset aUnableToOpenFi ; "Unable to open file."
.text:10038C71                push    ebx            ; hWnd
.text:10038C72                call    ds:MessageBoxA
.text:10038C78                lea    ecx, [ebp+var_23C] ; this
.text:10038C78 ;  } // starts at 10038C55
.text:10038C7E ;  try {
.text:10038C7E                mov    byte ptr [ebp+var_4], bl
.text:10038C81                call    RawFileClass::~RawFileClass()
.text:10038C86                jmp    loc_10038DA5
.text:10038C8B ; ---------------------------------------------------------------------------
.text:10038C8B
.text:10038C8B loc_10038C8B:                          ; CODE XREF: W3dExportClass__DoExport+190↑j
.text:10038C8B                lea    edx, [ebp+var_23C]
.text:10038C91                push    edx            ; _DWORD
.text:10038C92                lea    ecx, [ebp+var_E50]
.text:10038C98                call    ChunkSaveClass::ChunkSaveClass(FileClass *)
.text:10038C9D                mov    ecx, esi        ; this
.text:10038C9F                call    W3dExportClass__CreateOriginNodeList
.text:10038CA4                cmp    eax, ebx
.text:10038CA6                jz      short loc_10038CBD
.text:10038CA8                lea    eax, [ebp+var_E50]
.text:10038CAE                push    eax            ; int
.text:10038CAF                lea    ecx, [ebp+Caption]
.text:10038CB5                push    ecx            ; lpCaption
.text:10038CB6                mov    ecx, esi
.text:10038CB8                call    W3dExportClass__ExportData
.text:10038CBD
.text:10038CBD loc_10038CBD:                          ; CODE XREF: W3dExportClass__DoExport+1D6↑j
.text:10038CBD                lea    ecx, [ebp+var_23C]
.text:10038CC3                call    RawFileClass::Close(void)
.text:10038CC8                mov    edi, [esi+48Ch]
.text:10038CCE                cmp    edi, ebx
.text:10038CD0                jz      short loc_10038CE8
.text:10038CD2                mov    ecx, edi        ; this
.text:10038CD4                call    HierarchySave__DHierarchySave
.text:10038CD9                push    edi            ; void *
.text:10038CDA                call    MAX_delete(void *)
.text:10038CDF                add    esp, 4
.text:10038CE2                mov    [esi+W3dExportClass.HierarchyStruct], ebx
.text:10038CE8
.text:10038CE8 loc_10038CE8:                          ; CODE XREF: W3dExportClass__DoExport+200↑j
.text:10038CE8                mov    edi, [esi+W3dExportClass.OriginNodeList]
.text:10038CEE                cmp    edi, ebx
.text:10038CF0                jz      short loc_10038D08
.text:10038CF2                mov    ecx, edi        ; this
.text:10038CF4                call    INodeListClass__destructor2
.text:10038CF9                push    edi            ; void *
.text:10038CFA                call    MAX_delete(void *)
.text:10038CFF                add    esp, 4
.text:10038D02                mov    [esi+W3dExportClass.OriginNodeList], ebx
.text:10038D08
.text:10038D08 loc_10038D08:                          ; CODE XREF: W3dExportClass__DoExport+220↑j
.text:10038D08                mov    edi, [esi+W3dExportClass.DamageNodeList]
.text:10038D0E                cmp    edi, ebx
.text:10038D10                jz      short loc_10038D28
.text:10038D12                mov    ecx, edi        ; this
.text:10038D14                call    INodeListClass__destructor2
.text:10038D19                push    edi            ; void *
.text:10038D1A                call    MAX_delete(void *)
.text:10038D1F                add    esp, 4
.text:10038D22                mov    [esi+W3dExportClass.DamageNodeList], ebx
.text:10038D28
.text:10038D28 loc_10038D28:                          ; CODE XREF: W3dExportClass__DoExport+240↑j
.text:10038D28                lea    ecx, [ebp+var_23C] ; this
.text:10038D28 ;  } // starts at 10038C7E
.text:10038D2E ;  try {
.text:10038D2E                mov    byte ptr [ebp+var_4], bl
.text:10038D31                call    RawFileClass::~RawFileClass()
.text:10038D36                jmp    short loc_10038D6D
.text:10038D38 ; ---------------------------------------------------------------------------
.text:10038D38
.text:10038D38 loc_10038D38:                          ; DATA XREF: .rdata:stru_10086C44↓o
.text:10038D38 ;  catch(ErrorClass) // owned by 10038B34
.text:10038D38 ;  catch(ErrorClass) // owned by 10038C55
.text:10038D38                mov    esi, [ebp+i]
.text:10038D3B                push    10000h          ; uType
.text:10038D40                push    offset aError  ; "Error"
.text:10038D45                push    esi            ; lpText
.text:10038D46                xor    ebx, ebx
.text:10038D48                push    ebx            ; hWnd
.text:10038D49                call    ds:MessageBoxA
.text:10038D4F                cmp    esi, ebx
.text:10038D51                jz      short loc_10038D5F
.text:10038D53                mov    edx, ds:void (*MAX_free)(void *)
.text:10038D59                push    esi
.text:10038D5A                call    dword ptr [edx]
.text:10038D5C                add    esp, 4
.text:10038D5F
.text:10038D5F loc_10038D5F:                          ; CODE XREF: W3dExportClass__DoExport+281↑j
.text:10038D5F                mov    eax, offset loc_10038D65
.text:10038D64                retn
.text:10038D65 ; ---------------------------------------------------------------------------
.text:10038D65
.text:10038D65 loc_10038D65:                          ; CODE XREF: W3dExportClass__DoExport+294↑j
.text:10038D65                                        ; DATA XREF: W3dExportClass__DoExport:loc_10038D5F↑o
.text:10038D65                mov    esi, [ebp+var_220]
.text:10038D6B                xor    ebx, ebx
.text:10038D6B ;  } // starts at 10038D2E
.text:10038D6D
.text:10038D6D loc_10038D6D:                          ; CODE XREF: W3dExportClass__DoExport+266↑j
.text:10038D6D                mov    [ebp+var_4], 0FFFFFFFFh
.text:10038D74                call    W3dExportClass__PrintTotalVertexCount
.text:10038D79                xor    eax, eax
.text:10038D7B                mov    al, [esi+W3dExportClass.ExportStruct.ReviewLog]
.text:10038D81                push    eax            ; a1
.text:10038D82                call    W3dExportClass__DestroyLogDialog
.text:10038D87                mov    ecx, [esi+W3dExportClass.Int] ; this
.text:10038D8D                mov    edi, [ecx]
.text:10038D8F                add    esp, 4
.text:10038D92                push    ebx
.text:10038D93                push    8
.text:10038D95                call    [edi+InterfaceVtable.GetTime]
.text:10038D9B                mov    ecx, [esi+W3dExportClass.Int] ; Interface *
.text:10038DA1                push    eax            ; t
.text:10038DA2                call    [edi+InterfaceVtable.RedrawViews]
.text:10038DA5
.text:10038DA5 loc_10038DA5:                          ; CODE XREF: W3dExportClass__DoExport+143↑j
.text:10038DA5                                        ; W3dExportClass__DoExport+15F↑j ...
.text:10038DA5                mov    ecx, [ebp+var_C]
.text:10038DA8                mov    large fs:0, ecx
.text:10038DAF                mov    ecx, [ebp+var_14]
.text:10038DB2                mov    eax, 1
.text:10038DB7                call    __security_check_cookie(x)
.text:10038DBC                pop    edi
.text:10038DBD                pop    esi
.text:10038DBE                pop    ebx
.text:10038DBF                mov    esp, ebp
.text:10038DC1                pop    ebp
.text:10038DC2                retn    14h
.text:10038DC2 ; } // starts at 10038AD0
.text:10038DC2 W3dExportClass__DoExport endp

.text:10072A0B SEH_10038AD0    proc near              ; DATA XREF: W3dExportClass__DoExport+5↑o
.text:10072A0B                                        ; .rdata:1008556C↓o
.text:10072A0B                mov    eax, offset stru_10086C68
.text:10072A10                jmp    ___CxxFrameHandler
.text:10072A10 SEH_10038AD0    endp

.rdata:10086C68 stru_10086C68  FuncInfo_V1 <19930520h, 3, offset stru_10086C2C, 1, \
.rdata:10086C68                                        ; DATA XREF: SEH_10038AD0↑o
.rdata:10086C68                              offset stru_10086C54, 0, 0>

.rdata:10086C2C stru_10086C2C  UnwindMapEntry <-1, 0>  ; DATA XREF: .rdata:stru_10086C68↓o
.rdata:10086C34                UnwindMapEntry <0, offset loc_10072A00>
.rdata:10086C3C                UnwindMapEntry <-1, 0>

.text:10072A00 loc_10072A00:                          ; DATA XREF: .rdata:10086C34↓o
.text:10072A00 ; __unwind { // SEH_10038AD0            ; this
.text:10072A00 ;  cleanup() // owned by 10038C55
.text:10072A00 ;  cleanup() // owned by 10038C7E
.text:10072A00 ;  cleanup() // owned by 10038D2E
.text:10072A00                lea    ecx, [ebp+var_23C]
.text:10072A06                jmp    RawFileClass::~RawFileClass()
.text:10072A06 ; } // starts at 10072A00

.rdata:10086C54 stru_10086C54  TryBlockMapEntry <0, 1, 2, 1, offset stru_10086C44>
.rdata:10086C54                                        ; DATA XREF: .rdata:stru_10086C68↓o

.rdata:10086C44 stru_10086C44  HandlerType <0, offset ErrorClass `RTTI Type Descriptor', 16, \
.rdata:10086C44                                        ; DATA XREF: .rdata:stru_10086C54↓o
.rdata:10086C44                              offset loc_10038D38>

I have a clone of the function (without any try/catch statements in there) as follows:
Code:

int __thiscall W3dExportClass::DoExport(W3dExportClass *this, const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options)
{
  this->ExpInt = ei;
  this->Int = i;
  this->field_47C = 0;
  this->OriginNodeList = NULL;
  this->DamageNodeList = NULL;
  this->HierarchyStruct = NULL;
  this->Time = i->GetTime(i);
  this->FrameRate = GetFrameRate();
  this->field_478 = 2;
  _splitpath(name, (char *)&ei, &Dir, &Caption, NULL);
  sprintf(buf, "%s%s", &ei, &Dir);
  if ( strlen(&Caption) > 0xF && !suppressPrompts )
    MessageBoxA(NULL, "Warning: W3D filenames should be 15 characters or less!", "Warning", 0);
  _splitpath((const char *)i->GetCurFilePath(), (char *)&ei, &Dir, NULL, NULL);
  sprintf(this->Path, "%s%s", &ei, &Dir);
  if ( W3DExportClass::DoExportDialog(this, suppressPrompts)
    && (this->ExportStruct.ExportSkeleton || this->ExportStruct.ExportAnimation || this->ExportStruct.ExportGeometry) )
  {
    CreateLogDialog(0);
    RawFileClass r(name);
    if ( r.Open(2) )
    {
      ChunkSaveClass::ChunkSaveClass(&r);
      if ( W3dExportClass::CreateOriginNodeList(this) )
        W3dExportClass::ExportData(this, &Caption, (int)&v21);
      r.Close();
      if ( this->HierarchyStruct )
      {
        delete this->HierarchyStruct;
        this->HierarchyStruct = NULL;
      }
      if ( this->OriginNodeList )
      {
        delete this->OriginNodeList;
        this->OriginNodeList = NULL;
      }
      if ( this->DamageNodeList )
      {
        delete this->DamageNodeList;
        this->DamageNodeList = NULL;
      }
      W3dExportClass::PrintTotalVertexCount();
      W3dExportClass::DestroyLogDialog(this->ExportStruct.ReviewLog);
      this->Int->RedrawViews(this->Int->GetTime() //not sure of full prototype for this function, may have more arguments);
    }
    else
    {
      MessageBoxA(NULL, "Unable to open file.", "Error", MB_SETFOREGROUND);
    }
  }
  return 1;
}

Can anyone tell me based on the ASM and the above clone where the try/catch statements and other EH stuff need to go to complete the clone? (if any further information is needed I can provide that)

chants 04-10-2019 03:11

It is strange because I see no references to those structures at the end in the code. The comments in the code seem to be inferred somehow from those data structures. Perhaps the compiler optimized out the try-catch already and just left in some extra metadata.

RTTI is Run-time Type Information, its basically a reflection metadata system for C++ especially for class hierarchy identification to allow for virtual destructors and the like. It does not necessarily mean it was referenced in the code as I think its merely a compiler option. Please refer to MSDN or even here:
Quote:

https://en.wikipedia.org/wiki/Run-time_type_information
So the real keys here are about compiler optimizations and whether they already optimized out unnecessary SEH, and whether the compiler is included extra and unneeded SEH data. Regardless, to resurrect the original source accurately, you would want to bring back those useless optimized out code and so this is definetely a bonus to have the information.

I don't understand the question further as the first dump shows where a try { block starts and also gives a comment as to its end so it should be easy for you to add those to your clone function. You can basically merge the contiguous try blocks as the close brace is misleading. There is just one try and one catch.

Code:

.text:10038B34 ;  try {
.text:10038C4F ;  } // starts at 10038B34
.text:10038C55 ;  try {
.text:10038C78 ;  } // starts at 10038C55
.text:10038C7E ;  try {
.text:10038D28 ;  } // starts at 10038C7E
.text:10038D2E ;  try {
.text:10038D38 ;  catch(ErrorClass) // owned by 10038B34
.text:10038D38 ;  catch(ErrorClass) // owned by 10038C55
.text:10038D6B ;  } // starts at 10038D2E

Basically because of the structure you already put in the clone you cannot match this easily. However again knowing what is safe and non-safe of the calls is crucial and the compiler theoretically uses heuristics to deduce and optimize based on this.

Possibly you just wrap the entire code block you show up to W3dExportClass::PrintTotalVertexCount(); in:
Code:

try {
 (original clone)
} catch (ErrorClass* ec) {
//.text:10038D38                mov    esi, [ebp+i] really [ebp+0x10] so some error class with a zero terminated ascii string at 0x10 bytes in which we call szMessage
  MessageBox(NULL, ec->szMessage, "Error", MB_SETFOREGROUND); //0x10000=MB_SETFOREGROUND
}
//.text:10038D5F                mov    eax, offset loc_10038D65
W3dExportClass::PrintTotalVertexCount();

The problem is you need to invert your control flow and return early or use goto's very similar to the way Microsoft does in a lot of their source code for large functions...
e.g.
Code:

if ( !r.Open(2) ) {
  MessageBoxA(NULL, "Unable to open file.", "Error", MB_SETFOREGROUND);
  return 1; //or goto return_loc
//...return_loc: return 1;
}

Code:

if ( !(W3DExportClass::DoExportDialog(this, suppressPrompts)
    && (this->ExportStruct.ExportSkeleton || this->ExportStruct.ExportAnimation || this->ExportStruct.ExportGeometry)) ) return 1; //or goto return_loc



All times are GMT +8. The time now is 03:23.

Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX