《C++反汇编与逆向分析技术揭秘》--认识启动函数,找到用户入口

《C++反汇编与逆向分析》和《程序员的自我修养》都是以VC6的代码作为例子讲解的。这里是在vs2017下,CRT代码有些区别,但整体流程上都是初始化环境,设置参数,最后转到用户main函数。

class COne
{
public:
    COne()
    {
        printf("COne \r\n");
    }
    ~COne()
    {
        printf("~COne \r\n");
    }
};

COne g_One;

int main()
{
    printf("main函数识别 \r\n");
    return 0;
}

// The implementation of the common executable entry point code.  There are four
// executable entry points defined by the CRT, one for each of the user-definable
// entry points:
//
//  * mainCRTStartup     => main
//  * wmainCRTStartup    => wmain
//  * WinMainCRTStartup  => WinMain
//  * wWinMainCRTStartup => wWinMain
//
// These functions all behave the same, except for which user-definable main
// function they call and whether they accumulate and pass narrow or wide string
// arguments.  This file contains the common code shared by all four of those
// entry points.
//
// The actual entry points are defined in four .cpp files alongside this .inl
// file.  At most one of these .cpp files will be linked into the resulting
// executable, so we can treat this .inl file as if its contents are only linked
// into the executable once as well.

mainCRTStartup()

extern "C" int mainCRTStartup()
{
    return __scrt_common_main();
}

__scrt_common_main()

// This is the common main implementation to which all of the CRT main functions 在处理任何异常之前,必须初始化/GS cookie
// delegate (for executables; DLLs are handled separately).
static __forceinline int __cdecl __scrt_common_main()
{
    // The /GS security cookie must be initialized before any exception handling
    // targeting the current image is registered.  No function using exception
    // handling can be called in the current image until after this call:
    __security_init_cookie();

    return __scrt_common_main_seh();
}

__scrt_common_main_seh()

static __declspec(noinline) int __cdecl __scrt_common_main_seh()
{
    if (!__scrt_initialize_crt(__scrt_module_type::exe))//初始化crt 运行库
        __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);

    bool has_cctor = false;
    __try
    {
        bool const is_nested = __scrt_acquire_startup_lock();

        if (__scrt_current_native_startup_state == __scrt_native_startup_state::initializing)
        {
            __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);
        }
        else if (__scrt_current_native_startup_state == __scrt_native_startup_state::uninitialized)
        {
            __scrt_current_native_startup_state = __scrt_native_startup_state::initializing;

            if (_initterm_e(__xi_a, __xi_z) != 0)//访问函数指针表并将其初始化的内部方法。第一个指针位于表中的起始位置,第二个指针位于结束位置。
//_initterm_e  如果初始化失败并引发错误,则返回一个非零的错误代码;如果未发生错误,则返回 0。
                return 255;

            _initterm(__xc_a, __xc_z);//函数执行时,_initterm( __xc_a, __xc_z )函数调用所有的初始化函数。构造全局对象。

            __scrt_current_native_startup_state = __scrt_native_startup_state::initialized;
        }
        else
        {
            has_cctor = true;
        }

        __scrt_release_startup_lock(is_nested);

        // If this module has any dynamically initialized __declspec(thread)
        // variables, then we invoke their initialization for the primary thread
        // used to start the process:
        _tls_callback_type const* const tls_init_callback = __scrt_get_dyn_tls_init_callback();//tls init
        if (*tls_init_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_init_callback))
        {
            (*tls_init_callback)(nullptr, DLL_THREAD_ATTACH, nullptr);
        }

        // If this module has any thread-local destructors, register the
        // callback function with the Unified CRT to run on exit.
        _tls_callback_type const * const tls_dtor_callback = __scrt_get_dyn_tls_dtor_callback();//tls destructor
        if (*tls_dtor_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_dtor_callback))
        {
            _register_thread_local_exe_atexit_callback(*tls_dtor_callback);
        }

        //
        // Initialization is complete; invoke main...
        //

        int const main_result = invoke_main();//初始化完成,返回用户入口 main

        //
        // main has returned; exit somehow...
        //

        if (!__scrt_is_managed_app())
            exit(main_result);

        if (!has_cctor)
            _cexit();

        // Finally, we terminate the CRT:
        __scrt_uninitialize_crt(true, false);
        return main_result;
    }
    __except (_seh_filter_exe(GetExceptionCode(), GetExceptionInformation()))
    {
        // Note:  We should never reach this except clause.
        int const main_result = GetExceptionCode();

        if (!__scrt_is_managed_app())
            _exit(main_result);

        if (!has_cctor)
            _c_exit();

        return main_result;
    }
}

其中C模式来编译程序的时候  函数指针数组被放到__xi_a和__xi_z 之间   ;使用C++编译链接程序时,这些函数指针被放到了__xc_a和__xc_z 之间。

static int __cdecl pre_c_initialization()
{
    main_policy::set_app_type();

    file_policy::set_fmode();
    file_policy::set_commode();

    if (!__scrt_initialize_onexit_tables(__scrt_module_type::exe))
        __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);

    #ifdef _M_IX86
    // Clear the x87 exception flags.  Any other floating point initialization
    // should already have taken place before this function is called.
    _asm { fnclex }
    #endif

    #ifdef _RTC
    _RTC_Initialize();
    atexit(_RTC_Terminate);
    #endif

    if (argv_policy::configure_argv() != 0)
        __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);

    __scrt_initialize_type_info();

    // If the user provided a _matherr handler, register it with the Universal
    // CRT.  Windows OS components cannot set a custom matherr handler (this is
    // a policy decision, to reduce complexity).
    #ifndef _CRT_WINDOWS
    if (__scrt_is_user_matherr_present())
    {
        __setusermatherr(_matherr);
    }
    #endif

    _initialize_invalid_parameter_handler();
    _initialize_denormal_control();

    #ifdef _M_IX86
    _initialize_default_precision();
    #endif

    _configthreadlocale(_get_startup_thread_locale_mode());

    if (_should_initialize_environment())
        environment_policy::initialize_environment();

    __scrt_initialize_winrt();

    if (__scrt_initialize_mta() != 0)
    {
        __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);
    }

    return 0;
}

static int __cdecl post_pgo_initialization()
{
    // This function calls the __local_stdio_{printf,scanf}_options() functions.
    // These functions are defined in public headers with external linkage and
    // thus may be PGO-instrumented.  We must not call these functions before the
    // PGO instrumentation library is initialized.
    __scrt_initialize_default_local_stdio_options();

    return 0;
}

static void __cdecl pre_cpp_initialization()
{
    // Before we begin C++ initialization, set the unhandled exception
    // filter so that unhandled C++ exceptions result in std::terminate
    // being called:
    __scrt_set_unhandled_exception_filter();

    _set_new_mode(_get_startup_new_mode());
}

// When both the PGO instrumentation library and the CRT are statically linked,
// PGO will initialize itself in XIAB.  We do most pre-C initialization before
// PGO is initialized, but defer some initialization steps to after.  See the
// commentary in post_pgo_initialization for details.
_CRTALLOC(".CRT$XIAA") static _PIFV pre_c_initializer    = pre_c_initialization;
_CRTALLOC(".CRT$XIAC") static _PIFV post_pgo_initializer = post_pgo_initialization;
_CRTALLOC(".CRT$XCAA") static _PVFV pre_cpp_initializer  = pre_cpp_initialization;

全局对象构造

21: COne g_One;
00957100 55                   push        ebp
00957101 8B EC                mov         ebp,esp
00957103 81 EC C0 00 00 00    sub         esp,0C0h
00957109 53                   push        ebx
0095710A 56                   push        esi
0095710B 57                   push        edi
0095710C 8D BD 40 FF FF FF    lea         edi,[ebp-0C0h]
00957112 B9 30 00 00 00       mov         ecx,30h
00957117 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
0095711C F3 AB                rep stos    dword ptr es:[edi]
0095711E B9 41 9E A1 00       mov         ecx,offset g_One (0A19E41h)
00957123 E8 B6 C0 FF FF       call        COne::COne (09531DEh)  //构造函数
00957128 68 D0 2A 9F 00       push        offset `dynamic atexit destructor for ‘g_One‘‘ (09F2AD0h)
0095712D E8 DD BC FF FF       call        _atexit (0952E0Fh)  //构造对象完毕,调用atexit函数来保证析构函数的调用。
00957132 83 C4 04             add         esp,4
00957135 5F                   pop         edi
00957136 5E                   pop         esi
00957137 5B                   pop         ebx
00957138 81 C4 C0 00 00 00    add         esp,0C0h
0095713E 3B EC                cmp         ebp,esp
00957140 E8 42 BD FF FF       call        __RTC_CheckEsp (0952E87h)
00957145 8B E5                mov         esp,ebp
00957147 5D                   pop         ebp
00957148 C3                   ret  

invoke_main()

static int __cdecl invoke_main()
    {
        return main(__argc, __argv, _get_initial_narrow_environment());
    }
static int __cdecl invoke_main()
    77:     {
0095AB50 55                   push        ebp
0095AB51 8B EC                mov         ebp,esp
    78:         return main(__argc, __argv, _get_initial_narrow_environment());
0095AB53 E8 DE 92 FF FF       call        __get_initial_narrow_environment (0953E36h)
0095AB58 50                   push        eax
0095AB59 E8 4F 8A FF FF       call        ___p___argv (09535ADh)
0095AB5E 8B 00                mov         eax,dword ptr [eax]
0095AB60 50                   push        eax
0095AB61 E8 78 77 FF FF       call        ___p___argc (09522DEh)
0095AB66 8B 08                mov         ecx,dword ptr [eax]
0095AB68 51                   push        ecx
0095AB69 E8 A0 88 FF FF       call        _main (095340Eh)
0095AB6E 83 C4 0C             add         esp,0Ch
    79:     }
0095AB71 5D                   pop         ebp
0095AB72 C3                   ret  

原文地址:https://www.cnblogs.com/DirWang/p/12128900.html

时间: 2024-11-09 21:07:29

《C++反汇编与逆向分析技术揭秘》--认识启动函数,找到用户入口的相关文章

《C++反汇编与逆向分析技术揭秘》之十——析构函数

局部对象 当对象所在作用域结束之后,销毁栈空间,此时析构函数被调用. 举例: 函数返回时自动调用析构函数: 堆对象 调用析构代理函数来处理析构函数: 为什么使用析构代理函数来调用析构函数?考虑到如果delete的对象是一个对象数组,可以使用析构代理函数依次析构所有的对象.举例: 调用了构造代理函数: vector deleting destructor函数中先对标志位进行判断: 如果没有跳转,表明delete的是一个数组,则会调用调用析构代理函数对对象进行逐一的析构.如果进行了跳转,则只进行一次

《C++反汇编与逆向分析技术揭秘》之十一——虚函数

虚函数的机制 当类中定义有虚函数时,编译器会将该类中所有虚函数的首地址保存在一张地址表中,这张表被称为虚函数地址表.编译器还会在类中添加一个虚表指针. 举例: CVirtual类的构造函数中没有进行任何操作,但是我们来看构造函数内部,还是有一个赋初值的操作: 这个地址指向的是一个数组: 这些数组中的内容就是虚函数的指针: 值得注意的是,如果没有虚指针的存在,那么CVirtual大小就是4字节.有了这个指针存在就是8字节. 本例子中,使用了一个空的构造函数,但是编译器自己擅自插入了代码,实现了对虚

《C++反汇编与逆向分析技术揭秘》--数据类型

  浮点数类型 IEEE标准从逻辑上采用一个三元组{S, E, M}来表示一个数N,它规定基数为2,符号位S用0和1分别表示正和负,尾数M用原码表示,阶码E用移码表示.根据浮点数的规格化方法,尾数域的最高有效位总是1,由此,该标准约定这一位不予存储,而是认为隐藏在小数点的左边,因此,尾数域所表示的值是1.M(实际存储的是M),这样可使尾数的表示范围比实际存储多一位.为了表示指数的正负,阶码E通常采用移码方式来表示,将数据的指数e 加上一个固定的偏移量后作为该数的阶码,这样做既可避免出现正负指数,

《C++反编译与逆向分析技术揭秘》之学习笔记01--浮点数转IEEE编码

※浮点数转IEEE编码 1.float类型的IEEE编码(31,30~23,22~0=>符号位,指数位,尾数位)eg1:12.25 经过IEEE编码后的各位情况:符号位:0指数位:3+127, 10000010尾数位:100010000000000000000004字节二进制:0x41440000vc6.0截图: eg2:-0.125 经过IEEE编码后的各位情况:符号位:1指数位:-3+127, 01111100尾数位:000000000000000000000004字节二进制:0xBE000

《C++反编译与逆向分析技术揭秘》之学习笔记03--函数的调用方式

※函数的调用方式 EBP:扩展基址指针寄存器(extended base pointer) 其内存放一个指针,该指针指向系统栈最上面一个栈帧的底部. ESP:(Extended stack pointer)是指针寄存器的一种,用于指向栈的栈顶. _cdecl:C/C++默认的调用方式,调用方平衡栈,不定参数的函数可以试用. 调用方:1.参数压栈.esp-=42.调用函数.3.实现栈平衡.esp+=4 此处的printf也是同样道理0x004010CB.0x004010CC两处压入参数,共8个字节

《C++反编译与逆向分析技术揭秘》之学习笔记02--结构体和类之内存分布

※结构体和类之内存分布 1.空类的大小空类:其实空类至少会占用1个字节的长度. 2.字节对齐在为结构体和类中的数据成员分配内存时,结构体中的当前数据成员类型长度为M,指定对齐值为N,那么实际对齐值位q=min(M,N),其成员的地址安排在q的倍数上. vc6.0缺省对齐8个字节sShort占用2个字节,所以安排的地址0x0012FF70为2的倍数.nInt占用4个字节,所以安排的地址0x0012FF74为4的倍数.因为结构体中最大的字段长度为4,所以对齐值调整为4个字节.因为test对象为8个字

c++反汇编与逆向分析 小结

第一章  熟悉工作环境和相关工具1.1 熟悉OllyDBG  操作技巧1.2 反汇编静态分析工具 IDA(最专业的逆向工具)    快捷键    功能     Enter     跟进函数实现     Esc       返回跟进处    A         解释光标处的地址为一个字符串的首地址     B         十六进制数与二进制数转换    C         解释光标处的地址为一条指令     D         解释光标处的地址为数据,没按一次将会转换这个地址的数据长度   

逆向分析技术

1 . 函数参数传递 调用约定 _cdecl _stdcall  _fastcall 传参顺序 左->右 右->左 寄存器和堆栈传参 平衡堆栈者 调用者 子程序 子程序 int _cdecl Add(char a, long b, int c, int d) { return (a + b + c + d); } push 4 push 3 push 2 push 1 call Add (1161A60h) add esp,10h //调用者平衡堆栈 int _stdcall Add(char

自制反汇编逆向分析工具 迭代第三版本

将一个函数的分支跳转逆向生成cpp文件,使逆向分析过程从分支跳转的分析中解放出来,工具首要的作用是应当解放生产力. 下面是效果图: 然后附上上面效果图对应的反汇编源代码和工具逆向生成的cpp文件. 有了这个逆向出来的文件,接下来的逆向工作有点像在做填空,但已经帮大忙. 不能上传附件,贴代码. 某比较大的函数的反汇编代码 QuartzCore`CA::Context::commit_transaction: -> <+0>: pushq %rbp <+1>: movq %rsp