认识启动函数,找到用户入口

Linux 0.13版本 1.5W行

< >和"" 影响搜索顺序
Linux 0.13版本 1.5W行

代码规范

华为C语言编程规范http://t.csdn.cn/h5OOZ


检查意识

build.bat脚本

del *.obj
del *.exe
cl /c /W3 /WX /P hello.c /P:常用于多个包含文件出了问题的时候或复杂宏的时候就可以 使用P 生成.i文件审查该文件
link hello.obj
pause #暂停看结果

关于C语言的输入输出重定向

1、一种是在程序中设置:使用freopen将输入输出重定向。

C语言的标准输入输出为stdin和stdout,这两个变量的类型为FILE*类型,也就是说,标准输入输出操作,其本质还是文件操作。
当需要重定向时,可以调用
:::info
#include <stdio.h>

freopen(“d:\data_in.txt”,“r”,stdin); 将输入定向为文件d盘下的文件data_in.txt文件

(linux下的路径表示有点差别:freopen(“/data_in.txt”,“r”,stdin);表示根目录下的data_in.txt文件。)

freopen(“d:\data_out.txt”,“w”,stdout); 将输出定向到d盘下的data_out.txt目录。
:::

当调用该函数时,需要引用头文件stdio.h,在使用freopen()之后的标准输出或输入会重新定向,而之前的不会变。

2、另一种是运行时重定向:

在命令行输入: myprog.exe > X:\data_out.txt (在myprog.exe所在的文件夹下)

可以将输出重定向到X盘下的data_out.txt文件中,这时程序中所有的输出都将重定向到该文件,除非在程序中使用了freopen()函数,如果是这样的话,程序中freopen()函数之前的输出重定向到data_out.txt文件,而freopen()之后的将定向到freopen()指定的文件中。
:::info
重定向输入的方式也类似:在命令行输入 myprog.exe < data_in.txt
:::
当程序中依次出现scanf()、getchar() 输入函数时,就会自动的依次从文件data_in.txt中读取对应长度的文本(以字节算)。需要注意的是文件的结束标记为EOF

例如要读取文件中的所有文本可以写如下代码:

while( getchar() != EOF)
{   
    putchar();    
 
}

可以重定向到文件、ftp(文件服务器)、局域网、打印机

重定向输出

>
    hello > c:/test.txt

重定向输入

<

stdout stdin stderr(输出到标准设备、显示器、用来做日志)

#include <stdio.h>//如果是标准库或者是官方库使用<>;自己编写的自定义库使用" "



//<>和"" 影响搜索顺序
//Linux 0.13版本 1.5W行
//代码规范
//检查意识


int main(int argc,char*argv[])//main函数的返回值就是进程的返回值
{
    //格式化输出到标准输出设备(stdout)
    
    int n = printf("hello\r\n"); //可变参数的函数 //返回值:成功输出的字节数,负数为失败
    printf("%d\r\n",n);
    // fprintf(stderr,"hello world!\n");

    return 0; //exit(0)->ExitProcess(0)


}

程序的真正入口

main或WinMain函数需要有一个调用者,在它们被调用前,编译器其实已经做了很多事情,所以main或WinMain是“语法规定的用户入口”,而不是“应用程序入口”。

入 口 代 码 其 实 并 不 是 main 或 WinMain 函 数 , 通 常 是 mainCRTStartup 、wmainCRTStartup 、 WinMainCRTStartup 或 wWinMainCRTStartup , 具 体 视 编 译 选 项 而 定 。 这才是应用程序的实际启动程序。它调用用户的main例程[w]main()或[w]WinMain执行C运行时库初始化。 其 中 mainCRTStartup 和 wmainCRTStartup 是控制台环境下多字节编码 和 Unicode 编 码 的 启 动 函 数 , 而 WinMainCRTStartup 和 wWinMainCRTStartup 则是Windows环境下多字节编码和 Unicode 编码的启动函数。在开发过程中,C++也允许程序员自己指定入口。

mainCRTStartup的详解

Get the full Win32 version 获取完整的Win32版本

initialize heap 初始化堆

GetCommandLineA函数:获取命令行参数信 息的首地址。

_crtGetEnvironmentStringsA函数:获取环境变 量信息的首地址。

_setargv函数:此函数根据GetCommandLineA 获取命令行参数信息的首地址并进行参数分析, 将分离出的参数的个数保存在全局变量_argc中, 将分析出的每个命令行参数的首地址存放在数组 中,并将这个字符指针数组的首地址保存在全局 变量_argv中。这样就得到了命令行参数的个数, 以及命令行参数信息。

_setenvp函数:该函数就根据 _crtGetEnvironmentStringsA 函数来 获取环境变量信息的首地址,然后进行分析,就会得到的每条环境变量字符串的首地址并存放在字符指针数组中,然后将这个数组的首地址存放在全局变量env中。

当调用main函数的时候,就会把_argc、_argv、env这 三个全局变量作为参数,以栈传参方式传递到 main函数中。
initialize multi-thread 初始化多线程

initialize lowio 初始化低级io

do C data initialize 做C数据初始化

call run time initializer 调用运行时初始化器


//预编译宏
#else/*_WINMAIN_*/
#ifdef WPRFLAG
//宽字符版控制台启动函数
void wmainCRTStartup(
#else/*WPRFLAG*/
//多字节版控制台启动函数
void mainCRTStartup(
#endif/*WPRFLAG*/
#endif/*_WINMAIN_*/
void{
//获取版本信息
_osver=GetVersion();
_winminor=(_osver>>8)&0x00FF;
_winmajor=_osver&0x00FF;
_winver=(_winmajor<<8+_winminor;
_osver=(_osver>>16)&0x00FFFF//堆空间初始化过程,在此函数中,指定了程序中堆空间的起始地址
//_MT是多线程标记
#ifdef_MT
if(!_heap_init(1))
#else/*_MT*/
if(!_heap_init(0))
#endif/*_MT*/
fast_error_exit(_RT_HEAPINIT);
//初始化多线程环境
#ifdef_MT
if(!_mtinit())
fast_error_exit(_RT_THREAD);
#endif/*_MT*/
__try{
//宽字符处理代码略
//多字节版获取命令行
_acmdln=char*)GetCommandLineA();
//多字节版获环境变量信息
_aenvptr=char*)
__crtGetEnvironmentStringsA();
//多字节版获取命令行信息
_setargv();
//多字节版获取环境变量信息
_setenvp();
#endif/*WPRFLAG*/
//初始化全局数据和浮点寄存器
_cinit();
//窗口程序处理代码略
//宽字符处理代码略
//获取环境变量信息
_initenv=_environ;
//调用main函数,传递命令行参数信息
mainret=main(_argc,_argv,_environ);
#endif/*WPRFLAG*/
#endif/*_WINMAIN_*/
//检查main函数返回值执行析构函数或atexit注册的函数指针,并结束程序
exit(mainret);
}
//退出结束代码略
}

main函数有如下 特征是:它有3个参数,分别为命令行参数个数、 命令行参数信息和环境变量信息,而且它是启动函数中唯一的具有3个参数的函数。

在VC++6.0中,main函数被调用前要先调用的 函数如下:

GetVersion()

_heap_init()

GetCommandLineA()

_crtGetEnvironmentStringsA()

_setargv()

_setenvp()

_cinit()

这些函数调用结束后就会调用main函数。 根 据main函数调用的特征,会将3个参数_argc、_argv、env压入栈内作为函数的参数。

extern "C" int mainCRTStartup()
{
return __scrt_common_main();
}
static __forceinline int __cdecl __scrt_common_main()
{
//初始化缓冲区溢出全局变量
__security_init_cookie();
return __scrt_common_main_seh();
}
static __declspec(noinline) int __cdecl
__scrt_common_main_seh()
{
//用于初始化C语法中的全局数据
if (_initterm_e(__xi_a, __xi_z) != 0)
return 255;
//用于初始化C++语法中的全局数据
_initterm(__xc_a, __xc_z);
//初始化线程局部存储变量
_tls_callback_type const* const tls_init_callback =
__scrt_get_dyn_tls_init_callback();
if (*tls_init_callback != nullptr &&
__scrt_is_nonwritable_in_current_image(tls_init_callback))
{
(*tls_init_callback)(nullptr, DLL_THREAD_ATTACH, nullptr);
}
//注册线程局部存储析构函数
_tls_callback_type const * const tls_dtor_callback =
__scrt_get_dyn_tls_dtor_callback();
if (*tls_dtor_callback != nullptr &&
__scrt_is_nonwritable_in_current_image(tls_dtor_callback))
{
_register_thread_local_exe_atexit_callback(*tls_dtor_callba
ck);
}
//初始化完成调用main()函数
int const main_result = invoke_main();
//main()函数返回执行析构函数或atexit注册的函数指针,并结束程序
if (!__scrt_is_managed_app())
exit(main_result);
}
static int __cdecl invoke_main()
{
//调用main函数,传递命令行参数信息
return main(__argc, __argv,
_get_initial_narrow_environment());
}

什么时候检查printf的返回值?(检查意识)

1、只要数据出内存了,(磁盘、局域网、)就要考虑重定向,做一步检查一步

使用winhex查看地址初始化的值以及地址的值被修改的数据

测试代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
/*
    规范 写一行,规范一行

    比如对于学生总数而言
    int nStudentCount = 0;
    float fStudentCount = 0.0f; or flStudentCount;
    double dblStudentCount =0.0;
    char cStudentCount = '\0';
    short int snStudentCount = 0;
    int *pnStudentCount = NULL;
    void *pv;
*/
    //0x0018ff44
    //未初始化的局部变量上回使:用的这个地址的代码留下来的值
    int n = 0;
    //从标准输入设备格式化输入到指定的内存地址上
    //小尾方式: 低数据位存放在低地址处,高数据位存在高地址处
    //大尾方式: 高数据位存放在低地址处,低数据位存在高地址处
    //计算机设计初期的体系结构决定
    //0xc0000005内存访问异常
    printf("%08x\r\n",&n);
    system("pause");

    scanf("%d",&n);
    printf("%d\r\n",n);
    system("pause");

    return 0;
}

首先先运行程序,注意要使程序不要主动退出,要加上 system(“pause”); 运行后如图所示

使用winhex打开RAM 选择运行程序的进程、全部内存

把刚才输出的地址输入进去,进行查找跳转

显示如下

继续运行程序输出值

返回winhex查看可知,地址的值已被修改

被修改为 E7030000

原因:

小尾方式: 低数据位存放在低地址处,高数据位存在高地址处
大尾方式: 高数据位存放在低地址处,低数据位存在高地址处
计算机设计初期的体系结构决定

本博文参考《C++反汇编与逆向分析技术揭秘》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑桃鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值