Archive for the ‘C/C++’ Category

指向数组的指针

// [2][3][4]
 char (*p)[3][4]  = (char (*)[3][4])malloc(2 * 3 * 4);
 memset(p, 'a', 2 * 3 * 4);
 p[1][2][0] = 'b';
 p[1][2][1] = 'b';
 p[1][2][2] = 'b';
 p[1][2][3] = 'b';
 for(int i = 0; i < 2; ++i)
 {
  for(int j = 0; j < 3; ++j)
  {
   for(int x = 0; x < 4; ++x)
    printf("%c", p[i][j][x]);
  }
 }

老忘记,记录一下。为什么指向char [2][3][4]数组的指针不是char (*)[2][3][4]呢?想想指向一维数组的指针为什么不是char (*)[4]你就知道了,因为当你使用类似p[x][][]时,第一个[]定位p的位置!

一个HelloWorld

VS2003 Debug配置编译了一个HelloWorld程序,主要代码如下:
int main()
{
 printf("Hello, world!\n");
 return 0;
}

用OD调试来学习一下它的运行机理。OD载入,程序停在如下位置:
00411339 > $ /E9 F2080000   jmp     mainCRTStartup

用LordPE查看下PE文件,发现镜像基址:00400000,入口点:00011339,00400000+00011339=00411339看来OD停在了入口点。

入口点jmp mainCRTStartup以及mainCRTStartup很明显是由编译器生成的。单步跟进mainCRTStartup或者在IDA中查看mainCRTStartup可以看到,在调用用户代码前后它都做了很多工作。
用户代码的调用点如下:
.text:00411D9B                 call    j__main

j_main也是由编译器生成的一小段存根代码,如下:
.text:004113BB ; int __cdecl j__main()
.text:004113BB j__main         proc near
.text:004113BB                 jmp     _main
.text:004113BB j__main         endp

为什么会这样?应该是库代码的框架是确定的,已经生成了obj?lib?or dll?但是用户代码的位置在链接前是不确定的(因为在不同的obj模块),通过这种存根的方法就能解决问题。

库代码和用户代码最终都被链接合并到了同一个exe文件中,如果是exe和dll之间的调用关系又该如何呢?
首先必须要知道要使用哪个dll的哪个函数,然后加载dll获取函数的地址,最后call或者jmp到函数中去。
PE的文件格式已经解决了如上问题。数据目录中的输入表目录指示了要使用哪个dll、该dll中有哪些导出函数以及在哪个位置保存这些导出函数的地址(IAT)。

HelloWorld.exe的输入表如图:

注意其FirstThunk的RVA:0002A17C,和数据目录的IAT RVA一致。打开IAT也可以发现其大小为4*62(kernel32.dll中有62个导出函数)用于保存函数地址。Windows Loader在加载dll的时候会负责正确地填充。

这样一来,可以说PE文件已经准备好了一切,调用dll变得简单call or jmp [IAT中对应函数的地址的偏移]。但这里依然有两种方式可选,一种是高效率的call [],另一种是低效率的存根方法(call 存根; 存根: jmp [];)。

IDA中可以发现类似如下代码:
.text:00411C79                 call    ds:__imp__GetVersionExA@4 ; GetVersionExA(x)
.text:004112D0 ; [00000005 BYTES: COLLAPSED FUNCTION GetVersionExA(x). PRESS KEYPAD "+" TO EXPAND]

它们分别对应着前面提到的两种选择,可见对一个导出函数的调用,编译器生成了两种可选方案,只是HelloWorld中最终没有发现有调用.text:004112D0处的代码。
虽然编译器生成了两种可选方案,但是可以对输入的dll导出函数加上__desclspec(dllexport)修饰符,就能在调用时使用高效率的方案。

main中的代码没有什么好解释的,提一下在main恢复堆栈后切换栈帧前,有如下代码:
.text:00411A60                 add     esp, 0C0h
.text:00411A66                 cmp     ebp, esp
.text:00411A68                 call    j___RTC_CheckEsp
.text:00411A6D                 mov     esp, ebp
.text:00411A6F                 pop     ebp
很明显j___RTC_CheckEsp堆栈平衡检查,但好像与securty cookie不同,并不会影响缓冲区溢出漏洞。

fopen系列函数引起的一个问题

今天凌晨无聊逛CSDN论坛的时候发现了这样一个帖子:

楼主使用fopen系列函数以“r+”的模式打开一个写有“abcdefg”字符串的文件,循环fgetc、printf,最后fclose,程序显示出“abcdefg”没有问题。但再次打开文件,先fputc('Z', fp),然后循环fgetc、printf,最后fclose,这就出问题了。

问题表现为两个方面:一是printf出来的字符串不是预期的“bcdefg”,而是很多“屯”的字样;二是原文件也并不是预期的“Zbcdefg”,而是“Z屯屯屯...”。

Read more

const

int _tmain(int argc, _TCHAR* argv[])
{
 const int e = 3;
 int *w = (int *)&e;
 *w = 6; // 不会报错,但是e的值不会改变。
 e = 6; // 报错
 int a = e;
 printf("%d,%d", e, a);
 return 0;
}

分析:编译器对const的检查是对常量名进行的,因此可以通过 int *w = (int *)&e;*w=6;改变e的值。但是为什么 int a =  e;printf("%d,%d", e, a);e和a的值都是3呢?这是因为由于在编译时e的值已知,并且是const常量,所以生成的代码并不是引用e,而且直接引用了立即数3。通过printf("%d-%d,%d-%d", &e, e, w, *w);会发现&e和w(即地址值)是一样的,但是e和*w(即值)是不一样的。

参考资料:CONST

修改const值

定义返回函数指针的函数

定义返回函数指针的函数:

return_type (*function(func_parameter_list))(parameter_list)

定义了一个函数function,该函数的参数列表是(function_patameter_list),返回类型是一个函数指针,这个函数指针的原型是return_type(*)(parameter_list)。

无觅相关文章插件,快速提升流量