`
java-mans
  • 浏览: 11466493 次
文章分类
社区版块
存档分类
最新评论

随想录(由自定义打印函数想到的)

 
阅读更多

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】


在编写代码的时候我们都会自己重新定义一个打印函数,为什么要这样呢?我想主要原因还是为了定义其中的打印开关。如果打开了开关,我们当然可以看到很多的打印信息,但是如果不需要这些信息,我们就可以关上这些开关,所以一般的打印函数函数都是可以自己定义的。

#include <stdio.h>
#include <windows.h>

void XiaoxingPrintf(char* str, ...)
{
	char buffer[64];
	va_list va;

	va_start(va, str);
	vsprintf(buffer, str, va);
	va_end(va);

	printf("%s\n", buffer);
}

int main(int argc, char* argv[])
{
	XiaoxingPrintf(" argc = %d, &argv = 0x%x\n", argc, &argc);
	return 1;
}
上面的这段代码其实也很平常,看上去没有什么特别之处,但是我们想知道,这个va究竟是个什么东西?不妨可以反汇编看一下,

7:    void XiaoxingPrintf(char* str, ...)
8:    {
0040D430   push        ebp
0040D431   mov         ebp,esp
0040D433   sub         esp,84h
0040D439   push        ebx
0040D43A   push        esi
0040D43B   push        edi
0040D43C   lea         edi,[ebp-84h]
0040D442   mov         ecx,21h
0040D447   mov         eax,0CCCCCCCCh
0040D44C   rep stos    dword ptr [edi]
9:        char buffer[64];
10:       va_list va;
11:
12:       va_start(va, str);
0040D44E   lea         eax,[ebp+0Ch]
0040D451   mov         dword ptr [ebp-44h],eax
13:       vsprintf(buffer, str, va);
0040D454   mov         ecx,dword ptr [ebp-44h]
0040D457   push        ecx
0040D458   mov         edx,dword ptr [ebp+8]
0040D45B   push        edx
0040D45C   lea         eax,[ebp-40h]
0040D45F   push        eax
0040D460   call        vsprintf (0040d750)
0040D465   add         esp,0Ch
14:       va_end(va);
0040D468   mov         dword ptr [ebp-44h],0
15:
16:       printf("%s\n", buffer);
0040D46F   lea         ecx,[ebp-40h]
0040D472   push        ecx
0040D473   push        offset string "%s\n" (0042201c)
0040D478   call        printf (0040d850)
0040D47D   add         esp,8
17:   }
0040D480   pop         edi
0040D481   pop         esi
0040D482   pop         ebx
0040D483   add         esp,84h
0040D489   cmp         ebp,esp
0040D48B   call        __chkesp (0040d710)
0040D490   mov         esp,ebp
0040D492   pop         ebp
0040D493   ret
上面的汇编代码很多,其实有用的信息并不多。首先我们看va_start这一句话,好像做的事情很简单,就是把ebp+0xc给了va,va本身占用了四个字节,也就是位于[ebp-44h]的位置。那么最后的va_end呢,也不复杂,也就是把[ebp-44h]的位置清零了一下。但是可虑到这里的va是一个临时变量,是否清零其实意义不大。那么,这里的ebp+0xc是什么意思呢?首先我们看到,ebp上面的数值是原来push ebp的数值,ebp+4是函数的返回地址,ebp+8是str的地址,那么很明显,ebp+0xc就是第二个参数的地址。所以,我们可以对这个打印函数稍微改造一下,

void XiaoxingPrintf(char* str, ...)
{
	char buffer[64];
	vsprintf(buffer, str, &str + 1);
	printf("%s\n", buffer);
}
同样一个函数,三句话就可以搞定了。不再需要记住那么多头文件和数据类型。当然,这背后需要你对函数堆栈、参数地址、ebp和esp理解得比较深刻和具体了。



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics