第30页10行:在正常情况下,每个系统调用支持6个参数,分别保存在EBX,ECX,EDX,ESI,EDI和EPB里。
英文原文是:Each syscall can have a maximum of six arguments, which are inserted into EBX, ECX, EDX, ESI, EDI, and EPB, respectively.
我查阅了80x86体系结构手册中关于寄存器的内容,所有80x86系列的寄存器里似乎并没有一个叫EPB的,所以在这里猜测,可能是把EBP误写成EPB了。我没有查阅关于Linux系统调用的fastcall参数传递的具体信息,所以这里不能妄下定论。不过MSDN中关于Windows的fastcall的具体信息倒是有案可查:The __fastcall calling convention specifies that arguments to functions are to be passed in registers, when possible. The following list shows the implementation of this calling convention.
Argument-passing order : The first two DWORD or smaller arguments are passed in ECX and EDX registers; all other arguments are passed right to left.
第50页第5段(这个结果虽然出乎我们的意料,……)
第2行:……,我们刚刚输入的字符串中的4个字符被当作“数字”传递给printf函数,……
英文原文是:……,so 4-byte segments from the string are being passed as the "numbers" to be substituted into the string.
按照我的理解,原文的意思应该是:我们刚刚输入的字符串每4个字符被当作1个“数字”传递给printf函数
也就是说,字符串被分成每4个字符一段,分别作为格式化参数传递给了printf函数。而译文给人的感觉是:我们刚刚输入的字符串中“只有”4个字符被当作“数字”传递给printf函数,给人产生了误解和歧义。
第62页第2段(注意标为粗体的那行。)
那里比较有意思,在错误的调用vsnprintf之后,程序的作者正确的调用了printf。
原文是:The interesting point is that there's a call to printf right after the vulnerable call to vsnprintf.
最后谈点小意见,这章的最后一句是:that they are worth understanding.
愚以为,对基本原理的理解,应该胜过简单的掌握和熟练现成的技巧。理解了原理,加上我们充满灵感的想象力,就可以搞出无数令人称奇的新技巧。软件每时每刻都在更新,不可能有一劳永逸的现成的技巧,所以,“理解(原理,规律)”,活的理解(ing),可能比“学”“习”(现成的技巧)更重要。
第72页第1段第1行
译文:本章主要介绍Linux上的堆溢出,使用的堆实现源自Doug Lee最初的malloc实现,……
原文:This chapter focuses on heap overflows on the Linux platform, which uses a malloc implementation originally written by Doug Lee,……
注:不好意思,第一行就找您的岔。原文中的which应该指代的是“堆溢出”(heap overflows),“使用的堆实现”似乎有些不合适,因为堆的实现在这里暂时还看不出和主题什么关系。也许改为:……,利用了源自Doug Lee最初的malloc实现,…… 较妥。不知arhat大哥意下如何。
第72页第2段第4行
译文:一个典型的Linux程序通常包括.bss段(为初始化的全局变量),用brk()或mmap()分配的、由malloc()使用的.data段(已初始化的全局变量)。
原文:Typically a Linux program has a .bss (global variables that are uninitialized) and a .data segment (global variables that are initialized) along with other segments used by malloc() and allocated with the brk() or mmap() system calls.
注:这个问题就比较严重了,必须要纠正,技术上是不能有这样的概念错误的:.data段当然不是用brk()或mmap()分配的、由malloc()使用的,.data段就只是用来存放已初始化的全局变量的。从原文中也可以看出“along with other segments used by……”。
愚以为,应翻译成:一个典型的Linux程序通常包括.bss段(为初始化的全局变量),.data段(已初始化的全局变量)和其他的用brk()或mmap()等系统调用分配的、由malloc()使用的一些段。总之概念上决不能有混淆,希望arhat大哥在这个基本概念的基础上对这句话做出语言上更准确地翻译。
第77页第1行
译文:……,并把buf2块头部的0xfffffff0改为0xffffffff。
原文:…… and changing the chunk header of buf2 to have a size of 0xfffffff0 and a previous size of 0xffffffff.
注:原文的意思是:修改buf2块的头部,使得buf2块的大小变成0xfffffff0,buf2块的前一块的大小变成0xffffffff。其实就是把buf2块头部的8个字节分别修改成0xffffffff,0xfffffff0。因为buf2块头部的8个字节分别存储的是buf2块的前一块的大小(32位)和buf2块的大小(32位)。从下面的这条命令本身也可以看出:
(gdb) run `python -c 'print
"A"*1024+"\xff\xff\xff\xff"+"\xf0\xff\xff\xff"'`
第77页第5段第3行
译文:那你应该注意,在大多数情况下,free()是infree的封装;……
原文:…… you should note that free() is really a wrapper to intfree in most cases.
注:笔误,将intfree写成了infree,少了一个n。
第78页第1,2段
译文:
在第一条指令(mov 0x8(%esi), %edx)里,……
在第二条指令(add %eax, %edi)里,……
原文:
In the first instruction (mov 0x8(%esi), %edx), ……
In the second instruction (add %eax, %edi), ……
注:arhat大哥的翻译和原文内容一致,可惜原文内容写错了。稍微阅读下这两段,就会发现,这两条指令本应该是这两段上面的那两条指令,可惜被误写为这两段下面的那五条指令的前两条,完全对不上号。
故,原文应为:
In the first instruction (mov 0xfffffff8(%edx),%eax), ……
In the second instruction (sub %eax,%esi), ……
译文也应相应作出修改。
第78页第4段第1行
译文:……,接着用前一个块的大小减去4改写buf2的块头部;……
原文:……, then overwrite the chunk header of buf2 with a previous size of -4. ……
注:arhat大哥理解错作者的意思了,其实作者是说用-4(补码0xfffffffc)填充buf2的块头部的前4个字节,也就是存储前一个块的大小的那32位。从下面的这条命令本身可以看出来:
(gdb) r `python -c 'print
"A"*(1024)+"\xfc\xff\xff\xff"+"\xf0\xff\xff\xff"+"AAAAABCDEFGH" '`
第79页最后一行
译文:我们正好可以把word1和栈上的地址作为word2。
原文:We can just use that as word1 and an address on the stack as word2.
注:根据上下文,我推断,作者这里的意思是说把exit函数指针(that所指代的)的地址作为word1(其实实际应该是word1+12),而栈上的某个地址(当然是某个shellcode的首地址啦)作为word2。因为word2会写入word1+12,所以改写了本来指向exit函数的函数指针,使得对exit函数的调用变成了对栈上word2地址处的跳转。不知arhat大哥意下如何。
第90页第3段第1行
译文:……,在1991年首次发行了Windows 3.1。……
原文:……, with its first release in 1991 as Windows NT 3.1 .……
注:Windows 3.1和Windows NT 3.1还是不同的,其实在XP以前,Windows系列和Windows NT一直是Microsoft的两条主线,两者有不同的内核,面向不同的对象。比如Windows 3.1不是一个独立的操作系统,必须在DOS下进行加载,很像现在Linux下的XWindow。而Windows NT 3.1从一开始就立足于全新的内核,是一个独立的操作系统。
Windows NT 3.1到Windows NT 4.x到Windows NT 5.0(Windows 2000)
Windows 3.x到Windows 9x到Windows Me
最后两个系列合并成了Windows XP,后来又有了Windows 2003及现在的Windows Vista
很小的时候就在DOS上接触了Windows 3.1(在命令行下输入win加回车,呵呵),作为Windows发展历史的见证人,关于这个问题我还是有发言权的,呵呵。
第95页第5段第2行
译文:……和Todd Sabin的DCE-RPC(http://razor.bindview.com/)。
原文:…… and Todd Sabin's DCE-RPC tools (available from http://razor.bindview.com/).
注:DCE-RPC当然不是Todd Sabin的啦,arhat大哥这里少翻译了一个“tools”,其实应该是:“Todd Sabin的DCE-RPC工具包”。或者直接就是“Todd Sabin的DCE-RPC tools”。
第100页第4段第1行
译文:……,大部分是由未公开的,……
原文:……, and many of these are undocumented ……
注:根据上下文,我认为译文中多打了一个“由”字,本来应该是:“大部分是未公开的”。而且如果不去掉这个“由”字,则译文有些文法不通。
第100页第4段第2行
译文:例如,加载LibraryA()时(把DLL载入内存),……
原文:For example, Load_LibraryA(), which loads a DLL into memory,……
注:这个API我用过,所以我有发言权,这个函数应该是LoadLibraryA(),在MSDN里面查不到这个函数的,顶多查到LoadLibrary(),其实LoadLibrary()是定义在windows.h里面的一个宏,其实大家也应该想到了,还有另一个函数,就是LoadLibraryW()。LoadLibraryA()和LoadLibraryW()的区别就在于接受dll路径及文件名作为参数时,一个把参数处理成ASC单字符,一个处理成宽字符。这些MSDN里面一点都没有提到。我之所以用到LoadLibraryA()这个函数(一般肯定就用LoadLibrary()了),是因为那时搞远程DLL注入,就是利用CreateRemoteThread()在远程进程内创建线程,线程的功能就是载入DLL,当然线程函数就要用LoadLibrary(),可是LoadLibrary()其实根本就是个宏,根本不可能在kernel32.dll中GetProcAddress(),所以必须用LoadLibraryA()或LoadLibraryW()。
不好意思说多了,其实原文也有误导,偏偏在一个完整的函数中间加一个“_”(注:要是在Windows里面真的调用Load_LibraryA(),是绝对调不成功的,而且Win32API是绝不可能在函数名里面有个“_”的),结果也误导了arhat大哥。其实文章后面很快就讲到了DLL注入类似的内容,而且也正确地书写了LoadLibraryA()(这次arhat大哥当然翻译得就没错啦)。
第101页第6段第1行
译文:WSASicjet(),调用WSASicjet()而不是……
原文:WSASocket() Calling WSASocket() instead of ……
注:明显的笔误,在译文里这个函数怎么看怎么不对劲怎么又似曾相识。WSASocket()这个函数我也用过,当时看来相比socket()的优势就是可以交叠处理提高效率。
第105页第6段第2行
译文:我们逐行分析heaoverflow.c,看它到底做了些什么!
原文:Let's take a line-by-line look at the shellcode, heapoverflow.c, and see how it works.
注:这个C文件名是:heapoverflow.c,译文少打了一个p。