进入x64时代用汇编写程序的越来越少了, 经常去的几个汇编论坛也越来越没人气. 也是,现在很少事情非得找到一个用汇编来写的理由了. 所以慢慢也就没落了.实际上32位汇编和x64汇编个人感觉差别也不大.为什么就没人用了呢..

     作为一个看<>步入编程领域的人, 闲来无事, 就把该书的随书例子都重新翻译成x64的. 总共18章加3个附录. 为了表示向罗云斌致敬, 本文题目就叫<>.

     x32上罗牛用的是MASM32的开发包, MASM32是一个好东西, 里面已经收集起来了开发windows程序所需要的编译器和头文件, 也都经过了较为严格的测试,总之之前我用MASM32基本没有遇到什么问题.

     x64上,目前还没有MASM32那样的牛人来给我们收集头文件, 编译器和其他东西, 一方面可能有一些版权方面的问题, 一方面可能现在x64也不火了.没人搞这方面的工作,所以导致x64上用汇编很麻烦..难倒了很多新人. 就我在将<>中的例子从32位修改到64位的过程中, 体会最深的一点就是并不是因为32位和64位寄存器大小的区别, 也不是堆栈传递参数的区别, 更不是其他的什么. 而是对齐问题. 对齐问题大部分又是因为头文件没有定义清楚. 所以我在修改的过程中. 一边写代码, 一边还要去修改头文件定义.. 很酸爽. 注意, 如果你在用x64汇编写程序的过程中遇到问题, 首先就想一想是不是对齐问题.. 现在堆栈都要按照16BYTE对齐, 不能乱搞了. 还有很多结构体也要按照16BYTE对齐.

     为了修改<>到x64上面, 我从vs2010里面copy出来了cl link rc等程序. 然后汇编器没有用vs2010的. ml64支持不了什么高级语法, 汇编想要写较大型程序必须要要有良好的结构体, 堆栈框架支持. 所以汇编器用的是开源的UASM

     http://www.terraspace.co.uk/uasm.html

      UASM是从之前的 JWasm 修改过来的, 这个汇编器还是开源的, 兼容MASM的语法. 另外头文件也是从JWasm那边搞过来的, 不过我加入了一些自己的私货. 比如C语言的头文件, 比如UNICODE支持. MASM32的时代我就已经找到了UNICODE支持中文办法, 不过一直没有推广出去. 如果你有幸看到这篇文章. 并且以后也会准备用汇编来写程序.. 一定不要再用老掉牙的ANSI编码, 而是要采用UNICODE来写.. 如果你用了附件我上传的开发包, 那么可以这样定义和使用UNICODE. 一种是在.const节里面定义字符串. 还有一种就向C语言一样定义字符串..

MASM支持中文UNICODE字符串定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; uasm -c -win64 Test.asm
; rc Test.rc
; Link /subsystem:windows Test.obj Test.res
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    option casemap:none
    option win64:2
UNICODE        equ    True   
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include        windows.inc
include     unicode.inc
includelib    user32.lib
includelib    kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        .const
szCaption    db    '恭喜', 0
szText        db    '当您看到这个信息的时候,您已经可以编译Win64汇编程序了!', 0
UszText        UString    ('当您看到这个信息的时候,',10,13,"您已经可以编译Win64汇编程序了", 21h, 0)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        .code
Jmain    Proc
    ; ANSI编码调用方式
    ;invoke    MessageBox, NULL, offset szText, offset szCaption, MB_OK
    ; UNICODE调用方式
    invoke    MessageBox, NULL, offset UszText, offset UString$( "恭喜" ), MB_OK
    invoke    ExitProcess, NULL
Jmain     Endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    end    Jmain

  中文的完美支持. 依靠的就是unicode.inc 如果你下载附件的开发包,里面就已经自带了. 作者是陈浪涛. 也是个汇编高手. 有兴趣可以看看. 显然MASM的M不是Microsoft而是 Macro.. 用MASM的宏可以实现很多完全突破想象的功能, 这里有一个哥们就用MASM实现了面向对象. 有兴趣可以了解下.

     http://objasm32.tripod.com/

     关于附件中的例子编译方法和罗牛书上是一样的, 只是他用的是masm32 SDK包, 现在换成了UASM. UASM64压缩包解压到一个目录下, 运行目录下的set_path.bat就可以到各个例子目录下运行nmake了. 另外把TAB设置成8个BYTE. 这样显示出来的代码格式才正常.

     下面是我修改例子程序的时候, 遇到的一些问题. 不过都已经被解决了..

     无法接受和发送WM_COPYDATA消息, 原因是对齐方面的问题. COPYDATASTRUCT结构体必须按照16BYTE对齐. 我已经修改了winuser.inc文件, 将COPYDATASTRUCT结构体设置成了16BYTE对齐

    C:\JASM64\luoyunbin\Chapter04\SendMessage-1

     这个例子在win7 x64上已经试验不出效果了, 应该是win7的gdi画窗口的方式换了,不过虽然没有效果,我还是修改成了x64的版本.

     C:\JASM64\luoyunbin\Chapter07\DcCopy

     无法打开通用对话框 已修复OPENFILENAME对齐必须是16BYTE的. 所以我修改了系统的commdlg.inc后面的人就不会遇到这个坑了.

     C:\JASM64\luoyunbin\Chapter09\Richedit

     C:\JASM64\luoyunbin\Chapter10\FormatText

     设置结构体对齐 pushcontext         alignment option fieldalign:16 popcontext          alignment

     这些无法打开通用对话框的问题都是由于没有设置对齐引起的. 这个问题折腾了好久好久. 他们说现在用汇编和C差不多, 相信用C肯定不会遇到这种问题.  不禁又让我想起了以前用汇编写驱动的时候, DriverEntry写出来死活崩溃. 偶尔又不崩溃. 最好找了好久的问题才发现原来DriverEntry里面不能动ebx. 如果动了ebx就会出错, 想用的话就要push保存起来. 这些都是一路走来的坑…

     工具栏有编译错误,解决

     C:\JASM64\luoyunbin\Chapter09\Toolbar

     工具栏有编译错误,已解决

     C:\JASM64\luoyunbin\Chapter09\Wordpad

     工具栏错误解决, 主要是因为CommCtrl.inc头文件定义的有问题, 看来WinInc208 jwasm的作者还是没有masm32作者那样做那么多的测试.只是s使用h2incx工具转换好了就不管了. 这个转换难免还是有不少问题的..

     屌爆了, C:\JASM64\luoyunbin\Chapter10\FindFile 里面使用了COM接口也被我修改成了x64修改了不少的接口定义. 还修改了系统头文件的几个定义, 系统的ShlObj.INC里面关于SHBrowseForFolder等几个函数的名称粉碎方式定义错了..

     这是只适用于32位的例子.关于内存申请方面测试的例子, 所以就不修改了

     C:\JASM64\luoyunbin\Chapter10\Fragment

     这个是在9x做进程隐藏的. 和x64也没有关系也就不修改了.

     C:\JASM64\luoyunbin\Chapter13\HideProcess9x

     因为之前罗牛也没有做PE文件的分析,所以这个Test程序在x64上要将PE文件的Relocation stripped勾选, 不然每次地址都不一样patch就无从谈起了, 不过例子程序里面的可以直接运行,如果自己编译的话, 记得勾选Relocation stripped就好了..

     C:\JASM64\luoyunbin\Chapter13\Patch1

     Patch2 里面的Test程序也是要勾选Relocation stripped.并且为了可以使upx加壳,我加了不少的nop

     C:\JASM64\luoyunbin\Chapter13\Patch2

     这个例子应该是罗牛书中最有技术含量的几个例子之一, 也是汇编相对C语言C语言还有一些做不到的事情之一,处理重定位问题和写_invoke宏花了不少时间.

     C:\JASM64\luoyunbin\Chapter13\RemoteThread

     异常始终不进去.经过调试发现,SetUnhandledExceptionFilter调用必须写在函数里面. 不能和32位一样,使用裸函数. 应该又是堆栈对齐方面的问题.

     C:\JASM64\luoyunbin\Chapter14\TopHandler

     SEH异常方面,x64完全变掉了, 现在变得更简单了.

     C:\JASM64\luoyunbin\Chapter14\SEH01

     C:\UASM64\luoyunbin\Chapter16\Chat-TCP

     第16章关于网络这块的定义都有问题, 主要问题是出在SOCKET. 之前UASM的头文件定义成了 SOCKET typedef u_int 实际上翻看windows的winsock.h头文件中的定义, typedef UINT_PTR        SOCKET;是一个64位的类型, 所以将头文件修改成 SOCKET typedef qword 就好了. 如果不修改所有和Socket有关的函数都会出问题. 另外fd_set结构体也要修改, 将对齐修改成8BYTE.

     C:\UASM64\luoyunbin\Chapter17\AddCode

     17章最复杂的几个例子之一, 但是代码层面的复杂度都被我搞定了, 程序也可以正常运行了, 不知道最后一步调用MessageBox为什么会出错.. 参数各方面都是正确的. 最后发现出错地址在MessageBox函数里面的movdqa指令上, 然后手册发现movdqa指令必须16BYTE对齐, 所以加入了以下指令来做对齐工作,

MOVDQA - 移动对齐的双四字

将双四字从源操作数(第二个操作数)移到目标操作数(第一个操作数)。此指令可以用于在 XMM 寄存器与 128 位内存位置之间移入/移出双四字,或是在两个 XMM 寄存器之间移动。源操作数或目标操作数是内存操作数时,操作数必须对齐 16 字节边界,否则将生成一般保护性异常 (#GP)。

在 16 位寻址模式中执行时,不允许 128 位数据访问的线性地址与 16 位段尾重叠,此时的行为定义成保留行为。在这种情形下,特定的处理器版本可能会生成 #GP 异常,也可能不会,并且,跨越段尾的地址可能会通过舍位变成段首地址,也可能不会。

另外Chapter17\AddCode总共有两种方式, 一种是在节表后面添加一个节,以为一个节的大小毕竟有限, 所以这种方式是目前的主流方式,另外一种方式是扫描代码段看看有没有空间可以插入代码,这种方式现在不流行了, 因为现在PE文件FileAlignment对齐一般都是0x200而随便写一点shell code都会超过0x200 BYTE.. 这个例子的shell code就有300个BYTE. 因为要放置GetProcAddress 和Get Kernel32 base的代码, 在x64情况下很容易就超了..所以要实验在代码节中添加代码的方式可以找VC6编译的程序, VC6的FileAlignment SectionAlignment都是0x1000比较容易找到空隙. 不然就自己编译代码在link的时候加上这两个选项.

#pragma comment(linker, “/ALIGN:0x1000”)

#pragma comment(linker, “/FILEALIGN:0x1000”)

一个是设置文件对齐大小, 一个是设置内存对齐大小的.

     C:\UASM64\luoyunbin\Chapter17\Reloc

     重定位显示的例子, 没有什么特别需要说的. 就是之前罗牛只处理了IMAGE_REL_BASED_HIGHLOW一种类型的重定位表, 这个在32位上也是没有错的, 但是x64上面的重定位表一般都是IMAGE_REL_BASED_DIR64, 我都给改过来了. 所以在看例子程序的时候需要注意一下.

     C:\UASM64\luoyunbin\Chapter18\OdbcSample

     这是一个使用odbc数据库的例子, 刚开始遇到问题就是没有找到odbc.inc. 后面发现使用的不是odbc.inc文件, 而是使用的sql.inc sqlext.inc两个头文件, 另外UASM自己的头文件也是有问题的, sql.inc里面的函数声明都是proto stdcall. 然后link的时候报错.  无法解析的外部符号 _SQLRowCount@16,一类的. 这个名字一看就是使用了stdcall的名称处理方式, 然后我在odbc32.lib里面里面看到的名称是没有做任何处理的, 所以翻看头文件发现函数的原型定义错了. 原版里面都有stdcall. 现在去掉. 默认使用x64的fastcall..

     另外x64上面access的连接字符串也要修改一下.

     32位: “Driver={Microsoft Access Driver (*.mdb)};dbq=path;”

     64位: “Driver={Microsoft Access Driver (*.mdb,*.accdb)};dbq=path;“)

     还有, 例子里面使用了ListView. 操作ListView需要一个结构体LVITEM. 这个结构体在x64上面也是16 BYTE对齐的. 所以如果你自己找的头文件需要注意这个问题. 当然我发出来的UASM已经被我修改好了.

     //===========================================================================

     最后, 所有的例子我修改完成以后都简单做了一下测试. 主要功能应该是没有什么问题的, 不过如果有什么隐藏的bug. 你可以报给我. [email protected] 当然也欢迎大家加群Q群127285697讨论技术问题.

     编译环境的下载地址因为看雪附件上传限制. 我上传到了百度云. 请猛戳下载, 附件是修改的18章+3个附录.

     http://pan.baidu.com/s/1boBPVXH

windows环境下64位汇编语言程序设计-密码1