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语言的标准输入输出为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()之后的标准输出或输入会重新定向,而之前的不会变。
在命令行输入: 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++也允许程序员自己指定入口。
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个参数的函数。
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());
}
1、只要数据出内存了,(磁盘、局域网、)就要考虑重定向,做一步检查一步
测试代码:
#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++反汇编与逆向分析技术揭秘》