C/C++逆向:寻找mian函数(其他编译配置特征)

2024-09-02 03:52

本文主要是介绍C/C++逆向:寻找mian函数(其他编译配置特征),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在上篇文章中写了在逆向中定位main函数几种方法,其中有一种方法是通过编译器特征定位 main 函数(使用IDA分析简单demo程序获取特征,根据得到的特征可以定位相同编译器编译程序的main函数)。在上一篇文章中我们提取了VS环境(VS2017)中MSVC编译器编译配置与目标平台架构分别为Debugx86生成的程序特征,从而正确定位到了main函数所在位置。本篇文章主要写一下根据编译器特征定位main函数的方法,提取VS环境(VS2017)生成的编译配置与目标平台架构分别为Debug+x64Release+x86Release+x64以及GCC编译器编译生成的程序特征,从而定位到main函数。

1.Debug-x64程序特征提取

先准备一个Debug-x64的Hello World程序(以下称Demo程序);且准备程序的步骤都一样,后续就不再赘述:

接着右击项目生成程序即可;

打开程序所在文件夹,找到程序即可:

exe程序放入IDA(x64-bit)中进行加载解析;pdb文件也最好一并载入,即弹出以下窗口时选择yes:

.pdb文件包含了与可执行文件关联的调试符号和源代码信息。这些信息包括函数名、变量名、源代码行号等。在调试时,.pdb文件使得调试器能够将二进制代码映射回源代码,从而允许开发者查看变量的名称和类型、设置断点、查看调用堆栈以及源代码的确切行号等。没有.pdb文件,调试器只能展示汇编代码和原始的内存地址,极大地增加了调试的难度。

接着在函数窗口定位Demo程序main函数所在位置:

定位到main函数:

接着进行交叉引用(ctrl + x),提取特征(过程在上一篇文章中已经有详细说明,这篇文章就不再做过多赘述),这边补充一下小细节;若是在交叉引用的过程中出现了两个目标,且目标中包含Runtime_Function;此时一律不选Runtime_Function:

以下是笔者获取的程序(Debug-x64)特征:

第一个jmp
​
第一个call
​
第二个call
​
第一个jnz
sub     rsp, 68h
mov     ecx, 1
call    j___scrt_initialize_crt
movzx eax, al
test eax, eax
jnz short loc_140011FEF
​
​
第一个jmp
mov     [rsp+68h+var_48], 0
call    j___scrt_acquire_startup_lock
mov     [rsp+68h+var_47], al
cmp     cs:__scrt_current_native_startup_state, 1
jnz     short loc_140012012
mov     ecx, 7
call    j___scrt_fastfail
jmp     short loc_14001206A
​
​
第一个jz
movzx   ecx, [rsp+68h+var_47]
call    j___scrt_release_startup_lock
call    j___scrt_get_dyn_tls_init_callback
mov     [rsp+68h+var_38], rax
mov     rax, [rsp+68h+var_38]
cmp     qword ptr [rax], 0
jz      short loc_1400120C6
​
第一个jz
call    j___scrt_get_dyn_tls_dtor_callback
mov     [rsp+68h+var_30], rax
mov     rax, [rsp+68h+var_30]
cmp     qword ptr [rax], 0
jz      short loc_1400120F9
​
第一个call
​
第4个call
​
jmp main(跳转表)

接着根据特征进行动态调试定位main函数:

将程序拖如x64dbg中进行调试;按下F9来到自身程序模块中:

接着根据特征成功定位到main函数:

因为具体方法在上一篇文章中已经介绍过了,所以这篇文章主要的侧重点还是来比较一下生成的Debug-x86/x64程序的特征:

①Debug-x64特征
第一个jmp
​
第一个call
​
第二个call
​
第一个jnz
sub     rsp, 68h
mov     ecx, 1
call    j___scrt_initialize_crt
movzx eax, al
test eax, eax
jnz short loc_140011FEF
​
​
第一个jmp
mov     [rsp+68h+var_48], 0
call    j___scrt_acquire_startup_lock
mov     [rsp+68h+var_47], al
cmp     cs:__scrt_current_native_startup_state, 1
jnz     short loc_140012012
mov     ecx, 7
call    j___scrt_fastfail
jmp     short loc_14001206A
​
​
第一个jz
movzx   ecx, [rsp+68h+var_47]
call    j___scrt_release_startup_lock
call    j___scrt_get_dyn_tls_init_callback
mov     [rsp+68h+var_38], rax
mov     rax, [rsp+68h+var_38]
cmp     qword ptr [rax], 0
jz      short loc_1400120C6
​
第一个jz
call    j___scrt_get_dyn_tls_dtor_callback
mov     [rsp+68h+var_30], rax
mov     rax, [rsp+68h+var_30]
cmp     qword ptr [rax], 0
jz      short loc_1400120F9
​
第一个call
​
第4个call
​
jmp main(跳转表)
②Debug-x86特征:
第一个jmp
​
第一个call
push
mov 
call
​
第二个call
​
第一个jnz
mov     large fs:0, eax
mov     [ebp+ms_exc.old_esp], esp
push    1
call    j____scrt_initialize_crt
add     esp, 4
movzx   eax, al
test    eax, eax
jnz     short loc_411D7B
​
第一个jmp
mov     [ebp+var_1A], al
cmp     dword_41A158, 1
jnz     short loc_411DA0
push    7
call    j____scrt_fastfail
jmp     short loc_411E01
​
第一个jz
movzx   ecx, [ebp+var_1A]
push    ecx
call    j____scrt_release_startup_lock
add     esp, 4
call    sub_411195
mov     [ebp+var_20], eax
mov     edx, [ebp+var_20]
cmp     dword ptr [edx], 0
jz      short loc_411E51
​
第一个jz
call    sub_41123A
mov     [ebp+var_24], eax
mov     edx, [ebp+var_24]
cmp     dword ptr [edx], 0
jz      short loc_411E82
​
第一个call
call    ?invoke_main@@YAHXZ ; invoke_main(void)   
mov     [ebp+Code], eax
call    j____scrt_is_managed_app
movzx   ecx, al
test    ecx, ecx
jnz     short loc_411E9F
mov     edx, [ebp+Code]
​
第4个call
第一个jmp

前三个特征:Debug-x64与Debug-x86特征一致;

①Debug-x64特征
第一个jmp
​
第一个call
​
第二个call
②Debug-x86特征:
第一个jmp
​
第一个call
push
mov 
call
​
第二个call

第四个特征:主要的区别在于MSVC运行时初始化函数(j___scrt_initialize_crt)调用时所作的处理方式不同,以下是两者的具体区别分析:

①Debug-x64特征
第一个jnz
sub     rsp, 68h
mov     ecx, 1
call    j___scrt_initialize_crt
---------------------------------------
movzx eax, al
test eax, eax
jnz short loc_140011FEF
  1. 针对 x64架构(64位),使用了 rsp 这个x64 特有的寄存器。

  2. 使用寄存器传递函数参数(mov ecx, 1),不涉及栈操作来传递参数。该片段在调用函数前并没有通过 push 将参数压入栈,而是直接将参数放入寄存器 ecx,这是典型的 x64使用的fastcall调用约定。

在 x64 架构(64位)中,Windows 平台的调用约定通常使用 RCX, RDX, R8, R9 寄存器来传递前四个整数或指针类型的参数,返回值使用 RAX 寄存器。
②Debug-x86特征:
第一个jnz
mov     large fs:0, eax
mov     [ebp+ms_exc.old_esp], esp
push    1
call    j____scrt_initialize_crt
add     esp, 4
---------------------------------------
movzx   eax, al
test    eax, eax
jnz     short loc_411D7B

1.针对 x86架构(32位),使用了 espebpfs 等 x86 特有的寄存器和调用约定。

2.通过 push 指令将参数压入栈(push 1);参数通过 push 指令压栈,这是 x86 调用约定(如 stdcall、cdecl 等)的常见特征。

3.该片段代码 使用了add esp, 4代码进行栈清理操作,这说明该x86程序使用的是 cdecl 调用约定。

除了以上区别,其余的特征一致。接着来对比一下第五个特征:

①Debug-x64特征
mov     [rsp+68h+var_48], 0
call    j___scrt_acquire_startup_lock
-------------对比下面特征--------------------------------------
mov     [rsp+68h+var_47], al
cmp     cs:__scrt_current_native_startup_state, 1
jnz     short loc_140012012
-----------------------------------------
mov     ecx, 7
call    j___scrt_fastfail
jmp     short loc_14001206A
②Debug-x86特征:
mov     [ebp+var_1A], al
cmp     dword_41A158, 1
jnz     short loc_411DA0
-----------------------------------------
push    7
call    j____scrt_fastfail
jmp     short loc_411E01

通关观察可以看出,第五个特征中也是由于x86与x64程序所用的调用约定不同从而使得两个特征又些许差异,除了在使用call调用函数部分会存在区别以外,其他的特征基本是一致的。其余特征的区别也是一洋,这边就不做过多赘述了。

2.Release-x86程序特征提取

准备一个Release-x86程序:

将程序拖入IDA(32bit)进行解析;

再解析完成后,一样在函数窗口中搜索main函数:

在这里我们可以发现 IDA函数窗口中Release 模式下的程序显示的函数数量明显少于 Debug 模式下的程序;这种现象的主要原因在于编译器在 Release模式下会进行各种优化,导致一些函数被内联、删除或合并。以下是部分导致这种现象的具体原因:

①编译器优化
1>函数内联
编译器会将一些小的、频繁调用的函数直接替换为它们的代码实现,避免了函数调用的开销。这种优化会导致这些小函数在最终的可执行文件中不再作为单独的函数存在,而是被“内联”到调用它们的地方,结果是 IDA 在分析 Release 版本时,无法识别出这些函数,导致函数窗口中显示的函数数量减少。
2>死代码消除
编译器会删除在程序执行过程中永远不会被调用的代码和函数,减少可执行文件的大小,在 Debug 模式下,编译器不会执行这种优化,因此即使是未使用的函数也会出现在最终的可执行文件中。
3>代码折叠和合并:
对功能相似或相同的代码段进行折叠或合并,以减少代码的冗余度。这样会导致 IDA 将多个函数识别为一个函数或者直接将多个函数的代码合并成一个函数。
②去除调试信息
Release 模式下通常会去除调试信息,如符号信息(函数名、变量名等)和调试符号(如 PDB 文件),以保护程序的源代码结构和内部逻辑,并且减少可执行文件的大小。

接着回到正题,在定位到main函数后,接着就可以对Release-x86程序进行特征提取,这边步骤就省略了,最后得到的特征为:

第一个call
​
第二个call
​
第一个jmp
mov     [ebp+ms_exc.registration.TryLevel], 0
call    ___scrt_acquire_startup_lock
mov     [ebp+is_nested], al
cmp     ___scrt_current_native_startup_state, 1
jnz     short loc_4011D0
push    7               ; code
call    ___scrt_fastfail
jmp     short loc_401231
​
第一个jz
movzx   ecx, [ebp+is_nested]
push    ecx             ; is_nested
call    ___scrt_release_startup_lock
add     esp, 4
call    ___scrt_get_dyn_tls_init_callback
mov     [ebp+tls_init_callback], eax
mov     edx, [ebp+tls_init_callback]
cmp     dword ptr [edx], 0
jz      short loc_401281
​
第一个jz
call    ___scrt_get_dyn_tls_dtor_callback
mov     [ebp+tls_dtor_callback], eax
mov     edx, [ebp+tls_dtor_callback]
cmp     dword ptr [edx], 0
jz      short loc_4012B2
​
第一个call
​
第4个call

在这边可以看到Release程序的特征层数比较Debug程序的特征层数要来的少;最后根据特征定位到main函数:

3.Release-x64程序特征提取

生成Release-x64程序;

打开所在文件夹,并将其载入IDA(64-bit)中进行解析:

在IDA中一样先在函数窗口中定位main函数:

接着进行交叉引用,提取特征,步骤省略,最后得到的特征为:

第一个call
​
第二个call
​
第一个jmp
call    __scrt_acquire_startup_lock
mov     [rsp+68h+var_47], al
cmp     cs:__scrt_current_native_startup_state, 1
jnz     short loc_140001222
mov     ecx, 7          ; code
call    __scrt_fastfail
jmp     short loc_14000127A
​
第一个jz
movzx   ecx, [rsp+68h+var_47]
call    __scrt_release_startup_lock
call    __scrt_get_dyn_tls_init_callback
mov     [rsp+68h+target], rax
mov     rax, [rsp+68h+target]
cmp     qword ptr [rax], 0
jz      short loc_1400012D6
​
第一个jz
call    __scrt_get_dyn_tls_dtor_callback
mov     [rsp+68h+var_30], rax
mov     rax, [rsp+68h+var_30]
cmp     qword ptr [rax], 0
jz      short loc_140001309
​
第一个call
​
第四个call

接着根据特征成功定位main函数:

4.gcc程序特征定位main函数

gcc编译的Demo程序准备,在此之前系统中需要存在gcc环境;若没有的gcc环境的兄弟文章末尾领取。我这边也只记录64位的gcc特征定位main函数的过程。

1.准备一个C文件

接着在命令行中运行以下命令,生成exe程序:

gcc Hello.c -o Hello.exe

此时就可以开始进行特征提取了,将程序拖入IDA(64-bit)进行加载解析;

特征如下:

第二个call
​
第一个jnz
push    r13
push    r12
push    rbp
push    rdi
push    rsi
push    rbx
sub     rsp, 98h
xor     eax, eax
mov     ecx, 0Dh
lea     rdx, [rsp+0C8h+StartupInfo]
mov     rdi, rdx
rep stosq
mov     rdi, cs:_refptr_mingw_app_type
mov     r9d, [rdi]
test    r9d, r9d
jnz     loc_401470
​
​
第二个jmp
xor     eax, eax
xchg    rax, [rbx]
jmp     loc_40122C
mov     rcx, rdx        ; lpStartupInfo
call    cs:__imp_GetStartupInfoA
jmp     loc_4011B4
​
第一个jmp
mov     rax, gs:30h
mov     rbx, cs:_refptr___native_startup_lock
xor     ebp, ebp
mov     rsi, [rax+8]
mov     r12, cs:__imp_Sleep
jmp     short loc_4011E4
​
第一个jnz
mov     rax, rbp
lock cmpxchg [rbx], rsi
test    rax, rax
jnz     short loc_4011D3
​
第一个jz
xor     ebp, ebp
mov     rsi, [rax+8]
mov     r12, cs:__imp_Sleep
jmp     short loc_4011E4
cmp     rsi, rax
jz      loc_401410
​
第一个jnz
mov     rsi, cs:_refptr___native_startup_state
mov     ebp, 1
mov     eax, [rsi]
cmp     eax, 1
jnz     loc_401205
​
第2个jz
xor     ebp, ebp
mov     eax, [rsi]
cmp     eax, 1
jz      loc_401427
mov     eax, [rsi]
test    eax, eax
jz      loc_401480
​
第一个jmp
mov     rdx, cs:_refptr___xi_z
mov     dword ptr [rsi], 1
mov     rcx, cs:_refptr___xi_a
call    _initterm
jmp     loc_401219
​
第一个jz
mov     eax, [rsi]
test    eax, eax
jz      loc_401480
mov     cs:has_cctor, 
mov     eax, [rsi]
cmp     eax, 1
jz      loc_40143C
​
第一个jnz
mov     rdx, cs:_refptr___xc_z
mov     rcx, cs:_refptr___xc_a
call    _initterm
test    ebp, ebp
mov     dword ptr [rsi], 2
jnz     loc_40122C
​
第一个jz
mov     rax, cs:_refptr___dyn_tls_init_callback
mov     rax, [rax]
test    rax, rax
jz      short loc_401247
xor     r8d, r8d
mov     edx, 2
​
第一个jmp
call    __p__acmdln
xor     ecx, ecx
mov     rax, [rax]
test    rax, rax
jnz     short loc_4012B2
jmp     short loc_4012F7
​
第一个jz
mov     r8d, [rdi]
test    r8d, r8d
jz      short loc_401315
test    byte ptr [rsp+0C8h+StartupInfo.dwFlags], 1
mov     eax, 0Ah
jnz     loc_401400
​
​
第一个jle
mov     rcx, r12        ; Size
call    malloc
test    ebx, ebx
mov     rdi, cs:argv
mov     rbp, rax
jle     short loc_401387
​
第二个call:
mov     qword ptr [rax], 0
mov     cs:argv, rbp
call    __main
mov     rax, cs:_refptr___imp___initenv
mov     rdx, cs:envp
mov     ecx, cs:argc    ; argc
mov     rax, [rax]
mov     [rax], rdx
mov     r8, cs:envp     ; envp
mov     rdx, cs:argv    ; argv
call    main
mov     ecx, cs:managedapp
mov     cs:mainret, eax
test    ecx, ecx
jz      loc_40149E

gcc程序的特征定位比较诡异,所以这边简单做一个记录:

①首先按F9进入自身程序模块中

进入以后查看反汇编代码后发现并未存在特征:

此时先按F8进行程序运行,但是F8按着按着发现又回到了ntdll.dll模块当中,所以还需要按下F9;

回到自己领空后接着按F8运行代码,结果运行没一会儿又回到了ntdll.dll模块,继续按F9。

回到自己程序模块中后接着F8运行,若是又到ntdll.dll模块就再按F9以此类推,直到出现以下特征:

两个call,依照上述IDA中提取的特征,我们需要进入第二个call;接着就正常根据特征进行main函数的定位即可。

注:由于gcc编译程序最后一个特征比较明显,所以可以选择一直点击F8,直到最后一个特征出现后,即可根据最后一个特征成功定位到main函数。

第二个call:
mov     qword ptr [rax], 0
mov     cs:argv, rbp
call    __main
mov     rax, cs:_refptr___imp___initenv
mov     rdx, cs:envp
mov     ecx, cs:argc    ; argc
mov     rax, [rax]
mov     [rax], rdx
mov     r8, cs:envp     ; envp
mov     rdx, cs:argv    ; argv
call    main
mov     ecx, cs:managedapp
mov     cs:mainret, eax
test    ecx, ecx
jz      loc_40149E
GCC环境:关注VX_GZ号[风铃Sec]后台回复[mingw64]获取MinGW,后添加环境变量即可使用。如若不会,GZH后台回复。

这篇关于C/C++逆向:寻找mian函数(其他编译配置特征)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128912

相关文章

Zookeeper安装和配置说明

一、Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式。 ■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境; ■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例; ■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble) Zookeeper通过复制来实现

CentOS7安装配置mysql5.7 tar免安装版

一、CentOS7.4系统自带mariadb # 查看系统自带的Mariadb[root@localhost~]# rpm -qa|grep mariadbmariadb-libs-5.5.44-2.el7.centos.x86_64# 卸载系统自带的Mariadb[root@localhost ~]# rpm -e --nodeps mariadb-libs-5.5.44-2.el7

hadoop开启回收站配置

开启回收站功能,可以将删除的文件在不超时的情况下,恢复原数据,起到防止误删除、备份等作用。 开启回收站功能参数说明 (1)默认值fs.trash.interval = 0,0表示禁用回收站;其他值表示设置文件的存活时间。 (2)默认值fs.trash.checkpoint.interval = 0,检查回收站的间隔时间。如果该值为0,则该值设置和fs.trash.interval的参数值相等。

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

wolfSSL参数设置或配置项解释

1. wolfCrypt Only 解释:wolfCrypt是一个开源的、轻量级的、可移植的加密库,支持多种加密算法和协议。选择“wolfCrypt Only”意味着系统或应用将仅使用wolfCrypt库进行加密操作,而不依赖其他加密库。 2. DTLS Support 解释:DTLS(Datagram Transport Layer Security)是一种基于UDP的安全协议,提供类似于

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象