大神论坛

找回密码
快速注册
查看: 300 | 回复: 0

[语言编程类] 大神论坛 逆向脱壳分析基础学习笔记十 汇编寻找C程序入口

主题

帖子

5

积分

初入江湖

UID
20
积分
5
精华
威望
10 点
违规
大神币
68 枚
注册时间
2021-03-14 10:40
发表于 2021-03-14 17:10
本帖最后由 kay2kay 于 2021-03-14 17:10 编辑

本文为本人的滴水逆向破解脱壳学习笔记之一,为本人对以往所学的回顾和总结,可能会有谬误之处,欢迎大家指出。
陆续将不断有笔记放出,希望能对想要入门的萌新有所帮助,一起进步


所有笔记链接:

大神论坛 逆向脱壳分析基础学习笔记一 进制篇
大神论坛 逆向脱壳分析基础学习笔记二 数据宽度和逻辑运算
大神论坛 逆向脱壳分析基础学习笔记三 通用寄存器和内存读写
大神论坛 逆向脱壳分析基础学习笔记四 堆栈篇
大神论坛 逆向脱壳分析基础学习笔记五 标志寄存器 
大神论坛 逆向脱壳分析基础学习笔记六 汇编跳转和比较指令
大神论坛 逆向脱壳分析基础学习笔记七 堆栈图(重点)(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记八 反汇编分析C语言
大神论坛 逆向脱壳分析基础学习笔记九 C语言内联汇编和调用协定
大神论坛 逆向脱壳分析基础学习笔记十 汇编寻找C程序入口(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记十一 汇编C语言基本类型
大神论坛 逆向脱壳分析基础学习笔记十二 汇编 全局和局部 变量(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记十三 汇编C语言类型转换(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记十四 汇编嵌套if else(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记十五 汇编比较三种循环(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记十六 汇编一维数组(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记十七 汇编二维数组 位移 乘法(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记十八 汇编 结构体和内存对齐(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记十九 汇编switch比较if else(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记二十 汇编 指针(一)(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记二十一 汇编 指针(二)(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记二十二 汇编 指针(三)(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记二十三 汇编 指针(四)(需登录才能访问)
大神论坛 逆向脱壳分析基础学习笔记二十四 汇编 指针(五) 系列完结(需登录才能访问)

更多逆向脱壳资源,请访问  大神论坛

C语言程序入口

首先明确一点,这里所指的C语言程序入口为C语言的控制台程序的入口,和WIN32等其它类型的程序入口并不相同,主要为学习如何寻找程序入口

首先我们都知道C语言的控制台程序中,我们都将代码写在了main函数里,但是这个main函数不过是我们编写代码的入口,而不是真正程序的入口,如何找寻真正的程序入口?

在main函数的头部下断点,然后观察其堆栈的调用情况,通过前面的学习,我们知道在调用子函数即CALL XXXX后,会将要返回的地址压入堆栈中,我们可以通过这个返回地址来一层一层往上找到真正的程序入口

寻找程序入口

首先写一个之前调用空函数的例子,然后设置断点,断下以后,打开Call Stack(调用堆栈)窗口,可以看到如下画面

image-20210302123412524

我们可以通过这个知道函数的调用顺序为:

KERNEL32!7c817077→mainCRTStartup→main

通常情况下,在调试程序的时候,比如通过OD或者x32dbg等调试工具来调试时,我们都希望程序能够在main函数这里断下,但实际上,它断在了mainCRTStartup这里


修改入口程序

为了进一步理解程序入口,我们可以手动修改程序入口,来观察其与main函数的不同

我们随便新建一个空函数

void Test(){

}

然后工程→设置(也可以使用快捷键ALT+F7)

image-20210302124424202

然后在弹出来的新窗口中选中连接(Link)选项卡

image-20210302124811520

接着再将分类里的常规改成输出

image-20210302124906878

最后在入口点里填上我们前面新建的空函数的函数名即可

image-20210302125037488


修改完入口程序以后,我们在Test函数那下个断点,观察其堆栈调用情况

image-20210302125426636

我们可以发现入口点确实被修改为了Test并且mainCRTStartup也没了

但是实际上mainCRTStartup极其重要,我们不能没有它,因为它做了很多初始化的工作,关系到我们后续代码的正常运转

关于mainCRTStartup

  • mainCRTStartup 和  wmainCRTStartup 是控制台环境下多字节编码和Unicode 编码的启动函数
  • 而WinMainCRTStartup  和wWinMainCRTStartup 是windows 环境下多字节编码和Unicode 编码的启动函数

mainCRTStartup做了哪些事:

函数功能
GetVersion()获取操作系统版本
_heap_init()初始化堆空间,和malloc相关
GetCommandLineA()获取命令行参数
_crtGetEnvironmentStringsA()获取环境变量
_setargv()设置argv
_setenvp()设置envp
_cinit()c初始化

寻找main函数入口

理论

前面我们已经知道了mainCRTStartup也就是程序入口,那么如何通过mainCRTStartup来找到main函数入口?

根据函数的参数来进行判断

乍看之下,main函数貌似只有两个参数,但实际上main函数一共有三个参数,只不过一般第三个参数我们并没有用到,于是在使用main函数时并没有加上,完整的main函数原型如下:

int main(int argc,char *argv[],char *envp[]){}

这里的argv和envp对应mainCRTStartup里_setargv()和_setenvp()

main函数的三个参数:

参数含义
argc用于存放命令行参数的个数
argv是个字符指针的数组,每个元素都是一个字符指针,指向一个字符串,即命令行中的每一个参数
envp也是一个字符指针的数组,这个数组的每一个元素是指向一个环境变量的字符指针

我们这里无需关注这三个参数的具体细节,只用记住main函数具有三个参数的这个特征即可,然后通过这个特征我们来找main函数

实践

载入程序

我们拿OD随便拉一个简单的控制台程序来寻找其main函数

image-20210302131645780

程序停在了入口处,也就是mainCRTStartup处,我们从这里出发开始寻找main函数

如果OD并没有停在mainCRTStartup这里,可以进行如下设置,然后重新加载程序即可

image-20210302132109490

image-20210302132019052


准备工作完成后,就开始从这里向下寻找main函数

寻找main函数
004011B0 >/$  55            push ebp
004011B1 |. 8BEC mov ebp,esp
004011B3 |. 6A FF push -0x1
004011B5 |. 68 10F14100 push CallingC.0041F110
004011BA |. 68 A42C4000 push CallingC.00402CA4 ; SE 处理程序安装
004011BF |. 64:A1 0000000>mov eax,dword ptr fs:[0]
004011C5 |. 50 push eax
004011C6 |. 64:8925 00000>mov dword ptr fs:[0],esp
004011CD |. 83C4 F0 add esp,-0x10
004011D0 |. 53 push ebx
004011D1 |. 56 push esi
004011D2 |. 57 push edi
004011D3 |. 8965 E8 mov [local.6],esp
004011D6 |. FF15 3C414200 call dword ptr ds:[<&KERNEL32.GetVersion>; kernel32.GetVersion
004011DC |. A3 E0254200 mov dword ptr ds:[0x4225E0],eax
004011E1 |. A1 E0254200 mov eax,dword ptr ds:[0x4225E0]
004011E6 |. C1E8 08 shr eax,0x8
004011E9 |. 25 FF000000 and eax,0xFF
004011EE |. A3 EC254200 mov dword ptr ds:[0x4225EC],eax
004011F3 |. 8B0D E0254200 mov ecx,dword ptr ds:[0x4225E0]
004011F9 |. 81E1 FF000000 and ecx,0xFF
004011FF |. 890D E8254200 mov dword ptr ds:[0x4225E8],ecx
00401205 |. 8B15 E8254200 mov edx,dword ptr ds:[0x4225E8]
0040120B |. C1E2 08 shl edx,0x8
0040120E |. 0315 EC254200 add edx,dword ptr ds:[0x4225EC]
00401214 |. 8915 E4254200 mov dword ptr ds:[0x4225E4],edx ; ntdll.KiFastSystemCallRet
0040121A |. A1 E0254200 mov eax,dword ptr ds:[0x4225E0]
0040121F |. C1E8 10 shr eax,0x10
00401222 |. 25 FFFF0000 and eax,0xFFFF
00401227 |. A3 E0254200 mov dword ptr ds:[0x4225E0],eax
0040122C |. 6A 00 push 0x0
0040122E |. E8 8D180000 call CallingC.00402AC0
00401233 |. 83C4 04 add esp,0x4
00401236 |. 85C0 test eax,eax
00401238 |. 75 0A jnz short CallingC.00401244
0040123A |. 6A 1C push 0x1C
0040123C |. E8 CF000000 call CallingC.00401310
00401241 |. 83C4 04 add esp,0x4
00401244 |> C745 FC 00000>mov [local.1],0x0
0040124B |. E8 00150000 call CallingC.00402750
00401250 |. FF15 38414200 call dword ptr ds:[<&KERNEL32.GetCommand>; [GetCommandLineA
00401256 |. A3 2C3F4200 mov dword ptr ds:[0x423F2C],eax
0040125B |. E8 D0120000 call CallingC.00402530
00401260 |. A3 C4254200 mov dword ptr ds:[0x4225C4],eax
00401265 |. E8 B60D0000 call CallingC.00402020
0040126A |. E8 610C0000 call CallingC.00401ED0
0040126F |. E8 7C080000 call CallingC.00401AF0
00401274 |. 8B0D FC254200 mov ecx,dword ptr ds:[0x4225FC]
0040127A |. 890D 00264200 mov dword ptr ds:[0x422600],ecx
00401280 |. 8B15 FC254200 mov edx,dword ptr ds:[0x4225FC]
00401286 |. 52 push edx ; ntdll.KiFastSystemCallRet
00401287 |. A1 F4254200 mov eax,dword ptr ds:[0x4225F4]
0040128C |. 50 push eax
0040128D |. 8B0D F0254200 mov ecx,dword ptr ds:[0x4225F0]
00401293 |. 51 push ecx
00401294 |. E8 7BFDFFFF call CallingC.00401014
00401299 |. 83C4 0C add esp,0xC
0040129C |. 8945 E4 mov [local.7],eax
0040129F |. 8B55 E4 mov edx,[local.7]
004012A2 |. 52 push edx ; ntdll.KiFastSystemCallRet
004012A3 |. E8 88080000 call CallingC.00401B30
004012A8 |. 8B45 EC mov eax,[local.5]
004012AB |. 8B08 mov ecx,dword ptr ds:[eax]
004012AD |. 8B11 mov edx,dword ptr ds:[ecx] ; ntdll.7C92DC9C
004012AF |. 8955 E0 mov [local.8],edx ; ntdll.KiFastSystemCallRet
004012B2 |. 8B45 EC mov eax,[local.5]
004012B5 |. 50 push eax
004012B6 |. 8B4D E0 mov ecx,[local.8]
004012B9 |. 51 push ecx
004012BA |. E8 010A0000 call CallingC.00401CC0
004012BF |. 83C4 08 add esp,0x8
004012C2 \. C3 retn

我们从OD的断点处一路向下寻找具有三个参数的函数,最终找到了这里

00401286  |.  52            push edx                                 ;  ntdll.KiFastSystemCallRet
00401287 |. A1 F4254200 mov eax,dword ptr ds:[0x4225F4]
0040128C |. 50 push eax
0040128D |. 8B0D F0254200 mov ecx,dword ptr ds:[0x4225F0]
00401293 |. 51 push ecx
00401294 |. E8 7BFDFFFF call CallingC.00401014
00401299 |. 83C4 0C add esp,0xC

寻找技巧:虽然说push的个数并不一定代表就是参数个数,但是通过push确实可以过滤不少

我们找到的这里这个call下面有个add esp,0xC,非常典型的一个堆栈外平衡,由此我们可以推断它采用的调用协定是cdecl,再观察其压入的参数的数据宽度为4字节(压入的是三个32位通用寄存器),所以我们可以计算出参数的个数为C/4=3个参数

所以我们可以认为这里便是main函数的入口

返回顶部