先撇开_ioinit()不谈,IO有点儿麻烦,待有空再去挖掘这些东西。
GetCommandLineA
该函数的汇编代码短得令人发指。
7C812FBD mov eax, dword ptr [7C8855F4]
7C812FC2 retn
可见7C8855F4是个特殊的内存地址,其中存放了一个指向命令行缓冲区的指针。通过该指针,可以访问整个exe的完整路径以及后面附加的参数。
另外,通过PEB
中的ProcessParameters
也能够访问到CommandLine,但是通过这种方式访问到得是一个Unicode
字符串,而GetCommandLineA 访问的则是ANSI字符串。
__crtGetEnvironmentStringsA
在A_ENV.c 文件中可以看到该函数的具体实现。在文件开头有如下描述:
写道
Internal support function. Since GetEnvironmentStrings returns in OEM and we want ANSI ( note that GetEnvironmentVariable returns ANSI! ) and SetFileApistoAnsi( ) does not affect it, we have no choice but to obtain the block in wide character and convert to ANSI.
最后返回的是一个指向当前进程的环境变量的指针,这里所谓的环境变量用一种类似于键值对的方式存储在内存中。
注意首先得到是wide character 版本的环境变量,随后需要将它们转化成ANSI 。
来看一下__crtGetEnvironmentStringsA 的实现:
LPVOID __crtGetEnvironmentStringsA( VOID )
{
static int f_use = 0;
wchar_t *wEnv = NULL;
wchar_t *wTmp;
char *aEnv = NULL;
char *aTmp;
int nSizeW;
int nSizeA;
/*
* Look for 'preferred' flavor. Otherwise use available flavor.
* Must actually call the function to ensure it's not a stub.
*/
if ( 0 == f_use )
{
if ( NULL != (wEnv = GetEnvironmentStringsW()) )
f_use = USE_W;
else if ( NULL != (aEnv = GetEnvironmentStringsA()) )
f_use = USE_A;
else
return NULL;
}
/* Use "W" version */
if (USE_W == f_use)
{
……
}
/* Use "A" version */
if ( USE_A == f_use )
{
……
}
return NULL;
}
可见实质上,__crtGetEnvironmentStringsA 函数依然是调用了win API,可能是:
GetEnvironmentStringsW 或 GetEnvironmentStringsA
有人也许奇怪,为什么__crtGetEnvironmentStringsA 不直接去调用GetEnvironmentStringsA 呢?原因在于,即使是用GetEnvironmentStringsA ,所获取的也并非是 ANSI 的环境变量。
GetEnvironmentStringsA uses the OEM codepage
.The "ANSI" version of GetEnvironmentStrings (GetEnvironmentStringsA) returns the strings in the OEM code page, not the ANSI code page
. If you need the returned strings in the ANSI code page call the Unicode version (GetEnvironmentStringsW) and translate the returned wide-string to an 8-bit string
using a conversion function such as WideCharToMultiByte. Or call GetEnvironmentVariableA, which correctly uses the ANSI code page.
这里很清楚地写到,GetEnvironmentStringsA 返回的其实是OEM 字符,并非 ANSI。如果最终需要获得 ANSI 的环境变量,可以使用 GetEnvironmentStringsW ,然后将所获得的结果从 wide-string 转化成 8-bit string 。
因此,__crtGetEnvironmentStringsA 中首先调用了 GetEnvironmentStringsW 之后,设置 f_use = USE_W , 再进入if (USE_W == f_use) 的 语句块,进入转化成ANSI的处理。
/*
这里将指针wTmp 移动到 environment block 的末尾处
特别要注意,wTmp 是一个 wchar_t 类型的指针,因此 ++ 是移动两个字节
例如 environment block 的末尾是:
45 00 64 00 69 00 74 00 00 00 00(移动到此处)
E d i t \0 00
*/
wTmp = wEnv;
while ( *wTmp != L'\0' ) {
if ( *++wTmp == L'\0' )
wTmp++;
}
/* 求出environment block 中wide char的个数 +1 */
nSizeW = wTmp - wEnv + 1;
/* 该API返回转化为byte字符的字节数,其实nSizeA 和 nSizeW 数值相同 */
nSizeA = WideCharToMultiByte( CP_ACP,0,wEnv,nSizeW,NULL,0,NULL,NULL );
/* 分配一块大小为nSizeA 的内存 */
if ( (nSizeA == 0) || ((aEnv = (char *)_malloc_crt(nSizeA)) == NULL) )
{
FreeEnvironmentStringsW( wEnv );
return NULL;
}
/* 这里将WideChar 转化为 byte ,并且存放到刚分配的内存区域中 */
if ( !WideCharToMultiByte( CP_ACP,0,wEnv,nSizeW,aEnv,nSizeA,NULL,NULL ) )
{
_free_crt( aEnv );
aEnv = NULL;
}
FreeEnvironmentStringsW( wEnv );
return aEnv;
这里的实现与我想象中有些差别,不太清楚为什么MS的coder 会两次调用WideCharToMultiByte 函数对environment block 进行转化。
_setargv
该函数负责为C 程序的"argc" 和 "argv" 参数作好准备,_setargv 的源代码位于 STDARGV.c 文件。它会读取 _acmdln(存放了之前GetCommandLineA返回的指针)来访问command line,然后最关键的步骤是通过调用parse_cmdline 函数来解析command line,并且创建argv 数组。
这里仅分析_setargv 中最为核心的代码行。
_TSCHAR *p;
_TSCHAR *cmdstart; /* start of command line to parse */
int numargs, numchars;
//MAX_PATH 是 260 ,很有趣 ,文件的完整路径最大255 + "." + 后缀(比如exe) + " \0"
static _TSCHAR _pgmname[ MAX_PATH ];
/*
* __initmbctable 只能被调用一次,因此会设置一个__mbctype_initialized标记。
* __initmbctable 内部会调用_setmbcp 函数去创建一个新的multibyte code page,
* 随后置__mbctype_initialized=1
*/
if ( __mbctype_initialized == 0 )
__initmbctable();
/* 将当前进程的exe完整路径复制到_pgmname数组中
* 注意GetModuleFileName 是拿不到 程序启动参数args的,它获得仅仅是程序的完整路径而已
*/
GetModuleFileName( NULL, _pgmname, sizeof( _pgmname ) / sizeof(_TSCHAR));
_pgmptr = _pgmname;
/* 如果之前解析出来的_acmdln为空,则采用_pgmptr */
cmdstart = (*_acmdln == NULCHAR) ? _pgmptr : _acmdln;
/* 计算出 numargs 和 numchars 的大小 */
parse_cmdline(cmdstart, NULL, NULL, &numargs, &numchars);
/*
* 为argv 分配所需的空间
* 先是numargs 个指针,前numargs-1 指向路径与参数,最后一个是NULL
* 紧接着是numchars 个字符,用来存放numargs-1 个指针所指的内容
*/
p = _malloc_crt(numargs * sizeof(_TSCHAR *) + numchars * sizeof(_TSCHAR));
if (p == NULL)
_amsg_exit(_RT_SPACEARG);
/* 为指针P所指向的内存空间里填充argv */
parse_cmdline(cmdstart, (char **)p, p + numargs * sizeof(char *), &numargs, &numchars);
/* 至此,argc 与 argv 已经全部现形 */
__argc = numargs - 1;
__argv = (char **)p;
numargs 和 numchars 可能比较难以理解。举例来说,现有test0.exe,运行时附加了三个参数 a b c
cmdstart 指向的内存地址是0x00141ee0:
这里一共占用了 33 个byte (包含末尾的 00)
最后指针 P 指向的内存地址是0x003812c0:
很显然,0x003812c0 开始是指针数组,其中包含了4个有效指针,1个空指针。接着指针数组后的是4个ANSI字符串(红色线框标出),每个字符串以"00"结尾。
所以,0x003812c0 的大小 = 5个指针 + 4个字符串。
numargs = 5,表示指针数组的大小 ; numchars = 31(0x1F),表示四个字符串一共占用的字节数 。
__argc = 5-1
__argv = 0x003812c0
注意,最后返回的时候,需要将numargs-1,毕竟最后1个是空指针。
分享到:
相关推荐
设置Entry Point的方法, 默认情况是main, 你可以再VC属性里面修改。
BookB_Entry_Point_Specification_v2_6
港股公司研究-西牛证券-家乡互动03798.HKAn attractive entry point of a cash cow
win10安装mysql报错The procedure entry point fesetround could not be located in the dynamic link library-附件资源
看说明后,修改配置,再双击批处理文件即可创建系统还原节点,还可以创建系统计划定期执行它来创建系统还原节点
慢慢的回味.entry_point–JVM Java栈桢的创建1
C#通过NCO组建,直接调用SAP BAPI接口。app.config中配置SAP连接信息,代码中加入调用函数。注意引用的DLL是针对64位操作系统的。
在Linux内核调试中,经常用到的打印函数调用堆栈的方法非常简单,只需在需要查看堆栈的函数中加入: dump_stack(); 或 __backtrace(); 即可 dump_stack()在~/kernel/ lib/Dump_stack.c中定义 void dump_stack(void)...
如qsort 等函数需要函数指针才能回调 用此函数库可以将成员函数指针转为普通函数指针 测试代码如下 #include #include #include #include #include #include using cmpfunc = int(__cdecl*)(const void*, ...
In the module entry point, create a linked list containing five struct birthday elements. Traverse the linked list and output its contents to the kernel log buffer. Invoke the dmesg command to ensure ...
Data Entry ActiveX控件既可以作为控件被别的程序调用,也可以当做一个完整的应用程序来用;支持布尔运算,而且在用户输入数据时能自动搜索数据库之前输入的数据进行匹配比较供用户选择,大大加快了数据输入的速度;...
比如用户想要在thread1里调用engOpen创建一个MatLab Engine,然后在thread2里利用创建的Engine来调用MatLab函数,这时会出现Engine函数调用无效,即使Engine指针仍然在thread2里正确存在着。 如下: Class ...
本文档以PDF格式,分类清晰,注解详细,从应用程序开发,到系统驱动开发,从进程,文件处理,到虚拟内存,即插即用,电源管理,注册表,NTFS文件格式,无所不包。总共分成21个大类,几千个没有公开的API函数的详细...
Android 报错:Entry name ‘AndroidManifest.xml’ collided前言解决方法1、直接退回3.5.32、排除打包文件3、使用旧打包工具4、最终解决方法完事 前言 这两天 Android studio 更新了 3.6 版本,天天在右下角提示 ...
Thumb指令集, 非常详细
Hankey addresses the kinetic theory of gases and the prediction of vehicle trajectories during re-entry, including a description of the Earth's atmosphere. He discusses the fundamentals of hypersonic ...
EFI entry point for Linux v2.13.6.