什么代码生成具有零 TryBlocks 和零 UnwindMap 的 FuncInfoV1?

逆向工程 x86 反编译 微信
2021-06-14 14:46:53

主题是使用 msvc 编译的 WINdows x86 二进制文件中的 C++ 异常内部表示。

我发现了一个代码,FuncInfo除了EHFlags设置为 5之外,什么都没有(所有字段都为零)

这是拆卸:

.text:00407010 ; Attributes: bp-based frame
.text:00407010
.text:00407010 ; int __cdecl std::_Deallocate<8,0>(void *block, unsigned int __formal)
.text:00407010 ??$_Deallocate@$07$0A@@std@@YAXPAXI@Z proc near
.text:00407010                                         ; CODE XREF: std::_Deallocate<8,0>(void *,uint)↑j
.text:00407010
.text:00407010 var_C           = dword ptr -0Ch
.text:00407010 block           = dword ptr  8
.text:00407010 __formal        = dword ptr  0Ch
.text:00407010
.text:00407010 ; FUNCTION CHUNK AT .text:00458370 SIZE 0000001B BYTES
.text:00407010
.text:00407010 ; __unwind { // ??$_Deallocate@$07$0A@@std@@YAXPAXI@Z_SEH
.text:00407010                 push    ebp
.text:00407011                 mov     ebp, esp
.text:00407013                 push    0FFFFFFFFh
.text:00407015                 push    offset ??$_Deallocate@$07$0A@@std@@YAXPAXI@Z_SEH
.text:0040701A                 mov     eax, large fs:0
.text:00407020                 push    eax
.text:00407021                 mov     eax, ___security_cookie
.text:00407026                 xor     eax, ebp
.text:00407028                 push    eax
.text:00407029                 lea     eax, [ebp+var_C]
.text:0040702C                 mov     large fs:0, eax
.text:00407032                 cmp     [ebp+__formal], 1000h
.text:00407039                 jb      short loc_40704B
.text:0040703B                 lea     eax, [ebp+__formal]
.text:0040703E                 push    eax             ; unsigned int *
.text:0040703F                 lea     ecx, [ebp+block]
.text:00407042                 push    ecx             ; void **
.text:00407043                 call    j_?_Adjust_manually_vector_aligned@std@@YAXAAPAXAAI@Z ; std::_Adjust_manually_vector_aligned(void * &,uint &)
.text:00407048                 add     esp, 8
.text:0040704B
.text:0040704B loc_40704B:                             ; CODE XREF: std::_Deallocate<8,0>(void *,uint)+29↑j
.text:0040704B                 mov     edx, [ebp+__formal]
.text:0040704E                 push    edx             ; __formal
.text:0040704F                 mov     eax, [ebp+block]
.text:00407052                 push    eax             ; block
.text:00407053                 call    j_??3@YAXPAXI@Z ; operator delete(void *,uint)
.text:00407058                 add     esp, 8
.text:0040705B                 mov     ecx, [ebp+var_C]
.text:0040705E                 mov     large fs:0, ecx
.text:00407065                 pop     ecx
.text:00407066                 mov     esp, ebp
.text:00407068                 pop     ebp
.text:00407069                 retn
.text:00407069 ; } // starts at 407010
.text:00407069 ??$_Deallocate@$07$0A@@std@@YAXPAXI@Z endp

FuncInfo数据是:

FuncInfo <19930522h, 0, 0, 0, 0, 0, 0, 0, 5>

这是直接来自 CRT 源文件(xmemory)的代码片段:

//template <size_t _Align>
void _Deallocate(void* _Ptr, size_t _Bytes) noexcept {
    // deallocate storage allocated by _Allocate when !_HAS_ALIGNED_NEW || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__
#if defined(_M_IX86) || defined(_M_X64)
    if (_Bytes >= std::_Big_allocation_threshold) { // boost the alignment of big allocations to help autovectorization
        std::_Adjust_manually_vector_aligned(_Ptr, _Bytes);
    }
#endif // defined(_M_IX86) || defined(_M_X64)

    ::operator delete(_Ptr, _Bytes);
}

我尝试修改它,我发现在这种情况下,奇怪的 FuncInfo 的主要原因std::_Adjust_manually_vector_aligned是在同一文件中定义的调用

// FUNCTION TEMPLATE _Adjust_manually_vector_aligned
inline void _Adjust_manually_vector_aligned(void*& _Ptr, size_t& _Bytes) {
    // adjust parameters from _Allocate_manually_vector_aligned to pass to operator delete
    _Bytes += _Non_user_size;

    const uintptr_t* const _Ptr_user = reinterpret_cast<uintptr_t*>(_Ptr);
    const uintptr_t _Ptr_container   = _Ptr_user[-1];

    // If the following asserts, it likely means that we are performing
    // an aligned delete on memory coming from an unaligned allocation.
    _STL_ASSERT(_Ptr_user[-2] == _Big_allocation_sentinel, "invalid argument");

    // Extra paranoia on aligned allocation/deallocation; ensure _Ptr_container is
    // in range [_Min_back_shift, _Non_user_size]
#ifdef _DEBUG
    constexpr uintptr_t _Min_back_shift = 2 * sizeof(void*);
#else // ^^^ _DEBUG ^^^ // vvv !_DEBUG vvv
    constexpr uintptr_t _Min_back_shift = sizeof(void*);
#endif // _DEBUG
    const uintptr_t _Back_shift = reinterpret_cast<uintptr_t>(_Ptr) - _Ptr_container;
    _STL_VERIFY(_Back_shift >= _Min_back_shift && _Back_shift <= _Non_user_size, "invalid argument");
    _Ptr = reinterpret_cast<void*>(_Ptr_container);
}

这就是我进入这个的程度。

1个回答

因此,EHFlags位定义如下(参见ehdata_values.h):

#define FI_EHS_FLAG             0x00000001
#define FI_DYNSTKALIGN_FLAG     0x00000002
#define FI_EHNOEXCEPT_FLAG      0x00000004

即 5 是 FI_EHS_FLAG|FI_EHNOEXCEPT_FLAG

crt\src\vcruntime\frame.cpp我们可以找到这个片段:

auto tryBlockMap = T::TryBlockMap(pFuncInfo, pDC);
if (tryBlockMap.getNumTryBlocks() != 0
    //
    // If the function has no try block, we still want to call the
    // frame handler if there is an exception specification
    //
    || (T::getMagicNum(pFuncInfo) >= EH_MAGIC_NUMBER2 && (T::getESTypes(pFuncInfo) != nullptr))
    || (T::getMagicNum(pFuncInfo) >= EH_MAGIC_NUMBER3 && (T::isNoExcept(pFuncInfo) != 0)))
{

似乎该评论有点过时,应该阅读“如果有异常规范或没有指定”。

cppreference

允许非抛出函数调用潜在抛出函数。每当抛出异常并且搜索处理程序遇到非抛出函数的最外层块时,函数 std::terminate 被调用。

因此,这里似乎使用空的 FuncInfo 作为标记来标记noexcept函数,因此如果最终确实发生了异常,则程序会正确终止。

这是一个生成此类 FuncInfo 的小示例:

int f() noexcept
{
    static const int arr[5] = {1,2,3,4,6};
    for (int i = 0; i < sizeof(arr)/sizeof(*arr); i++)
    {
        std::cout << arr[i] << std::endl;
    }

    return 0;
}

int main()
{
 return f();
}