StartAoA说的在理,但是大神们首现要有时间,工作后空闲时间应该是很少的;其次工作中的研究毕竟事关相应的产品,可能会涉及到将同组别人的研究成果泄漏,也算是对同事的一种不尊重;最后,没有最后,觉得更多的时候还是需要自己来一步步的走。恩,作为新手少说话,多学习吧。
第二章.C语言概述
1.主函数int main(void)
由于使用VC来编译学习代码,就不去遵循什么C99标准了,main依然是C语言的默认入口函数。不过使用VC下的一个编译选项#pragma comment(linker,"/entry:EntryFunc"),可以修改入口函数,但是还会产生一些问题,这个以后遇到了再说吧。
2.【疑问】main函数是执行时第一个函数么?
A1PASS当时写逆向三步走时,一开始就提出OD载入程序后,发现根本不是写的相关逻辑。这里自己也验证了一下,main函数调用前都做了什么。
大概就是这个过程,但是VC/VS各版本,包括Debug/Release,开启不同的优化编译选项出来的代码会有若干差异,这里忽略。
下列代码是从crt0.c拷贝来的,能够更好的理解mian函数执行前,都做了什么工作。
//ASCII控制台启动函数
void mainCRTStartup(void)
{
int mainret;
_TUCHAR *lpszCommandLine;
STARTUPINFO StartupInfo;
//获取版本信息
_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
if ( !_heap_init(0) )
#endif
fast_error_exit(_RT_HEAPINIT);
//初始化多线程环境
#ifdef _MT
if( !_mtinit() )
fast_error_exit(_RT_THREAD);
#endif
__try
{
//获取命令行
_acmdln = (char *)GetCommandLineA();
//获取环境变量信息
_aenvptr = (char *)__crtGetEnvironmentStringsA();
//获取命令行信息
_setargv();
//获取环境变量信息
_setenvp();
//初始化全局数据和浮点寄存器
_cinit();
__initenv = _environ;
//调用main函数
mainret = main(__argc, __argv, _environ);
//检查main函数返回值执行析构函数,或者atexit()注册的结束后运行函数
exit(mainret);
}
//异常处理
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
{
_exit( GetExceptionCode() );
}
}
3.C99标准允许把声明放在代码块中的任何位置,但是首次使用变量之前仍然要声明。
#include <windows.h>
#include <stdio.h>
typedef int (WINAPI * AddProc)(int a, int b);
int main()
{
HMODULE hInterfaceDLL;
AddProc add;
hInterfaceDLL = LoadLibrary("InterfaceDLL.dll");
add = (AddProc)GetProcAddress(hInterfaceDLL, "Add");
int i = 1;
int j = 3;
int s = add(i, j);
printf("%d\n", s);
return 0;
}
先不看这段代码的具体含义,编译的时候无法通过编译(前提,VC下建立源文件时将后缀名改成.c,使之为标准C编译),VC提示如下:
error C2143: syntax error : missing ';' before 'type'
error C2143: syntax error : missing ';' before 'type'
error C2143: syntax error : missing ';' before 'type'
error C2065: 's' : undeclared identifier
但是使用CPP后缀就能够成功通过编译,现在想来就是这个原因吧,C99之前的标准需要在一开始声明变量,查询相关错误号,在微软网站上找到一句话
In Microsoft C, compiler errors C2143 and C2144 are defined as follows:
You may receive this error message if your program places executable code before a data declaration, an acceptable practice in Kernighan-and-Ritchie C.
This practice has been outlawed in later versions of the ANSI drafts.
应该是使用了之前的K&R,C的标准,并不是ANSI定制的C99。
相对应改成如下就能够成功通过编译:
int main()
{
int i = 1;
int j = 3;
int s;
HMODULE hInterfaceDLL;
AddProc add;
hInterfaceDLL = LoadLibrary("InterfaceDLL.dll");
add = (AddProc)GetProcAddress(hInterfaceDLL, "Add");
s = add(i, j);
printf("%d\n", s);
return 0;
}
4.操作系统和库通常使用一个或者两个下划线开始的名字,自己带应用代码中避免出现这种命名。
_CRTIMP void * __cdecl malloc (
size_t nSize
)
{
void *res = _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);
return res;
}
这个是从C运行库中复制的,可以看出,使用了好多"_"为名称(宏名、调用约定名、函数名、结构名...)开始。
5.提高程序可读性的技巧,选择有意义的变量名,使用注释,使用缩进,使用空行分隔概念上的不同部分。
各个公司的代码风格应该是不相同的,但是有了自己的风格,还是更容易阅读代码。比如过evilor兄的
《QQ游戏大厅的SX保护》代码读起来就很舒服,所以还是要养成一个良好的代码编写风格。
2012/3/24 revfish