课后解惑

Q:我在Windows 2000 SP2版本下,查找到LoadLibraryA函数的地址是0x77E6A254,system函数的地址是0x78019B4A,都是正确的,但为什么我把ShellCode对应的地方改成“\x77\xE6\xA2\x54”和“\x78\x01\x9B\x4A”后,不能弹出DOS窗口呢?

A:你把字节的顺序写反了,应该是“\x54\xA2\xE6\x77”和“\x4A\x9B\x01\x78”。

Q:哦!改动后的确能正确弹出DOS对话框了,但为什么顺序要是这样呢?好别扭啊!

A:在Windows系统下,多字节数存放的规则是:数的高位放在内存高址,数的低位放在内存低址。对0x77E6A25478来说,0x77是最高位,所以要放在内存的高地址,而在字符串中,是按照内存从低到高排列的,所以要把0x77放在字符串中数的最后。在后面的章节中还会讲到。

Q:能不能给出一些系统下LoadLibraryA函数和system函数的地址值呢?

A:好的。LoadLibraryA和system函数的地址在Win2000 SP0下,分别是0x77E78023和0x7801AAAD;在SP2下分别是0x77E6A254和0x78019B4A;在SP3下分别是0x77E69F64和0x7801AFC3;在XP SP0下分别是0x77E605D8和0x77BF8044。

要注意的是,覆盖的跳转地址也要符合相应版本,最好使用通用地址。

Q:为什么LoadLibrary函数在系统里面有LoadLibraryA和LoadLibraryW两种实现?而system只有一个实现,没有systemA或systemW呢?

A:问得好!这是因为在Windows下,存在几种编程接口。

一种是Windows API函数。这类函数是和Windows系统相关的,使用的也是Windows下才特有的数据类型(比如CHAR)。API函数就存在A和W这两种实现,而LoadLibrary是API函数。

另一种是C运行链接库,是按照C语言的标准来实现的,所以只有小写字母,而且只有一种实现,比如system函数。

Q:那怎么区分API函数和C运行库函数呢?

A:从函数的命名可以看出来。API函数遵循的是Windows自己定义的命名规范,是大?濨氷? 小写混写的函数,如LoadLibrary;而在C语言标准中,规定函数名称都是小写,所以C运行库函数也遵循全是小写的规范,如system。

Q:为什么Windows会有两种命名规范呢?

A:因为微软想遵循一种更科学的命名规范,所以规定了API函数命名法则;但为了保持和标准C语言的兼容,C运行库函数还是遵循了C语言标准的规定;所以存在了两种命名规范。

Q: 那我们自己编程时用那种命名规则呢?

A:哈哈!命名规则应和所用的操作系统或开发工具的风格保持一致。例如,Windows应用程序的标识符通常用“大小写”混排的方式,如AddChild;而Unix应用程序的标识符用“小写加下划线”的方式,如add_child。别把这两类风格混在一起用。

我们自己开发时,在Windows下建议使用“匈牙利”命名规则,类名和函数名用大写字母开头的单词组合而成,变量和参数用小写字母开头的单词组合而成,常量全用大写,全局变量加前缀“g”,类的数据成员加前缀“m_”。

Q: 我们如何验证提取出的ShellCode是否正确呢?太容易抄错了。

A:有两种方法。一种方法是在实际溢出程序中,使用提取出的ShellCode测试,看能否达到效果;另一种方法就是把ShellCode数组当成一个函数来执行,其实现办法会在下一章的“验证ShellCode功能方法”中讲到。

Q: 好像ShellCode里面不能有0x00,为什么呢?怎么避免呢?

A:因为0x00是字符串的结束符,如果ShellCode中存在,就会被截断;我们会在ShellCode变形大法一章中详细的讲解如何避免0x00的方法。