吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7978|回复: 31
收起左侧

[调试逆向] 逆向基础笔记十 汇编寻找C程序入口

  [复制链接]
lyl610abc 发表于 2021-3-2 13:47
本帖最后由 lyl610abc 于 2021-3-12 16:34 编辑

继续更新个人的学习笔记,
其它笔记传送门
逆向基础笔记一 进制篇
逆向基础笔记二 数据宽度和逻辑运算
逆向基础笔记三 通用寄存器和内存读写
逆向基础笔记四 堆栈篇
逆向基础笔记五 标志寄存器
逆向基础笔记六 汇编跳转和比较指令
逆向基础笔记七 堆栈图(重点)
逆向基础笔记八 反汇编分析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函数的入口

免费评分

参与人数 4吾爱币 +4 热心值 +3 收起 理由
冥界3大法王 + 1 这就是五子棋中后方制人的造点技巧,演绎为汇编则是特征码提取大法
AG6 + 1 + 1 我很赞同!
skyward + 1 + 1 有时间了得好好学下
ljj_1025 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

冥界3大法王 发表于 2021-3-2 23:17
        这就是五子棋中后方制人的造点技巧,演绎为汇编则是特征码提取大法
 楼主| lyl610abc 发表于 2021-3-2 14:28
wangqiuguo 发表于 2021-3-2 14:13
你试试VS2019的编译器就会不同了

原理是相同的,我笔记八中有解释为什么不使用Visual Studio
掂软心内 发表于 2021-3-2 14:08
deva 发表于 2021-3-2 14:10
学习学习!
wangqiuguo 发表于 2021-3-2 14:13
你试试VS2019的编译器就会不同了
ciker_li 发表于 2021-3-2 15:01
感谢分享
ljj_1025 发表于 2021-3-2 15:12
不错步错,楼主辛苦了!
lili2312280 发表于 2021-3-2 15:32
学习极度大佬,极度操作
WAY文 发表于 2021-3-2 16:29
感谢分享
zdychina 发表于 2021-3-2 16:39
感谢分享 学习了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-6 19:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表