最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

Windows X汇编入门

来源:动视网 责编:小OO 时间:2025-09-28 20:44:14
文档

Windows X汇编入门

标题:【原创】WindowsX汇编入门(1)作者:tankaiha时间:2007-05-05,23:31链接:http://bbs.pediy.com/showthread.php?t=43967Windows X汇编入门(1)tankaiha    最近断断续续接触了些位汇编的知识,这里小结一下,一是阶段学习的回顾,二是希望对位汇编新手有所帮助。我也是刚接触这方面知识,文中肯定有错误之处,大家多指正。文章的标题包含了本文的四方面主要内容:(1)Windows:本文是在wind
推荐度:
导读标题:【原创】WindowsX汇编入门(1)作者:tankaiha时间:2007-05-05,23:31链接:http://bbs.pediy.com/showthread.php?t=43967Windows X汇编入门(1)tankaiha    最近断断续续接触了些位汇编的知识,这里小结一下,一是阶段学习的回顾,二是希望对位汇编新手有所帮助。我也是刚接触这方面知识,文中肯定有错误之处,大家多指正。文章的标题包含了本文的四方面主要内容:(1)Windows:本文是在wind
标 题: 【原创】Windows X汇编入门(1)

作 者: tankaiha

时 间: 2007-05-05,23:31

链 接: http://bbs.pediy.com/showthread.php?t=43967

Windows X汇编入门(1)

tankaiha

    最近断断续续接触了些位汇编的知识,这里小结一下,一是阶段学习的回顾,二是希望对位汇编新手有所帮助。我也是刚接触这方面知识,文中肯定有错误之处,大家多指正。

文章的标题包含了本文的四方面主要内容:

(1)Windows:本文是在windows环境下的汇编程序设计,调试环境为Windows Vista 位版,调用的均为windows API。

(2)X:本文讨论的是x汇编,这里的x表示AMD和Intel的EMT,而不包括IA。至于三者间的区别,可自行搜索。

(3)汇编:顾名思义,本文讨论的编程语言是汇编,其它高级语言的位编程均不属于讨论范畴。

(4)入门:既是入门,便不会很全。其一,文中有很多知识仅仅点到为止,更深入的学习留待日后努力。其二,便于类似我这样刚接触x汇编的新手入门。

  本文所有代码的调试环境:Windows Vista x,Intel Core 2 Duo。

1.  建立开发环境

1.1  编译器的选择

    对应于不同的x汇编工具,开发环境也有所不同。最普遍的要算微软的MASM,在x环境中,相应的编译器已经更名为ml.exe,随Visual Studio 2005一起发布。因此,如果你是微软的忠实fans,直接安装VS2005既可。运行时,只需打开相应的位命令行窗口(图1),便可以用ml进行编译了。

    第二个推荐的编译器是GoASM,共包含三个文件:GoASM编译器、GoLINK链接器和GoRC资源编译器,且自带了Include目录。它的最大好外是小,不用为了学习位汇编安装几个G 的VS。因此,本文的代码就在GoASM下编译。

    第三个Yasm,因为不熟,所以不再赘述,感兴趣的朋友自行测试吧。

不同的编译器,语法会有一定差别,这在下面再说。

1.2  IDE的选择

    搜遍了Internet也没有找到支持asm的IDE,甚至连个Editor都没有。因此,最简单的方法是自行修改EditPlus的masm语法文件,这也是我采用的方法,至少可以得到语法高亮。当然,如果你懒得动手,那就用notepad吧。

    没有IDE,每次编译时都要手动输入不少参数和选项,做个批处理就行了。

1.3  硬件与操作系统

    硬件要求就是位的CPU。操作系统也必须是位的,如果在位的CPU上安装了32位的操作系统,就算编译成功也无法运行程序。

2.  寄存器的改变

    汇编是直接与寄存器打交道的语言,因此硬件对语言影响很大。先来看看x与x32相比在硬件上多了什么,变了什么(图2)。

    X多了8个通用寄存器:R8、R9、R10、R11、R12、R13、R14、R15,当然,它们都是位的。另外还增加了8个128位XMM寄存器,不过通常用不着。

    X32中原有的寄存器在X中均为扩展为位,且名称的第一个字母从E改为R。不过我们还是可以在位程序中调用32位的寄存器,如RAX(位)、EAX(低32)、AX(低16位)、AL(低8位)、AH(8到15位),相应的有R8、R8D、R8W和R8B。不过不要在程序中使用如AH之类的寄存器,因为在AMD的CPU上这种用法会与某些指令产生冲突。

3.  第一个x汇编程序

    本节,我们开始编写自己的第一个x汇编程序。在这之前,先讲一下calling convention的改变。

3.1  API调用方式

    把Calling convention放在第一个讲,代表它的重要性。在32位汇编中,我们调用一个API时,采用的是stdcall,它有两个特点:一是所有参数入栈,通过椎栈传递;二是被调用的API负责栈指针(ESP)的恢复,我们在调用MessageBox后不用add esp,14h,因为MessageBox已经恢复过了。

而在x汇编中,两方面都发生了变化。一是前四个参数分析通过四个寄存器传递:RCX、RDX、R8、R9,如果还有更多的参数,才通过椎栈传递。二是调用者负责椎栈空间的分配与回收。

    下面给出一段代码,功能是显示一个简单的MessageBox,注意对RSP的操作:

代码:

;示例代码1.asm

;语法:GoASM

DATA SECTION

text     db 'Hello x!', 0

caption  db 'My First x Application', 0

CODE SECTION

START:

sub rsp,28h

xor r9d,r9d

lea r8, caption

lea rdx, text

xor rcx,rcx

call MessageBoxA

add rsp,28h

ret

    这段代码是在GoASM中编译,指令部分GoASM与ML差不多,关键是一些宏的定义有差别。比如masm中的.code,在这里就成了CODE SECTION。下面再说区别,先编译。GoASM中编译分两步:

(1)  编译:goasm /x 1.asm

(2)  链接:golink 1.obj user32.dll

    如果一些正常,命令行中应显示图3的内容。

    运行一下,我们的第一个位windows程序就运行了。

    GoASM还有一个特点是支持宏:ARG和INVOKE,使用这两个宏可以免除程序员自己对椎栈进行操作。不过初学吗,还是从基础掌握比较好。下面的一段代码相同的功能的MASM代码,注意看看区别。ML至今仍不支持宏,所以每一步工作都要自己做。

代码:

;示例代码2.asm

;语法:ML

extrn MessageBoxA: proc

.data

text     db 'Hello x!', 0

caption  db 'My First x Application', 0

.code

Main proc

sub rsp,28h

xor r9d,r9d

lea r8, caption

lea rdx, text

xor rcx,rcx

call MessageBoxA

add rsp,28h

ret

Main ENDP

end

    编译这段代码的命令行是:ml 2.asm /link /subsystem:windows /entry:Main user32.lib。如果正常,应该如图5显示那样。

    很有意思吧,在位系统下,我们仍然调用user32的API。可能是名称用习惯了,微软自己都懒得改了吧。

3.2  位的椎栈

    代码中还有一处值得注意,那就是sub rsp,28h和add rsp,28h。28h这个数值是怎么来的呢?

首先,x中椎栈被扩展为位;其次,我们在调用MessageBoxA时,要给四个参数外加一个返回地址留空间,因此8(位)*5=40=28h。

    另外一些小问题要注意,AMD不支持push 32bit寄存器的指令,最好的方法就是push和pop都用位寄存器。EMT如何?看了下Intel的开发手册,各个指令都分三种情况:纯32位、纯位和32与位混合。下面是手册的片段:

Opcode*      Instruction        -Bit Mode       Compat/Leg Mode      Description

FF /6         PUSH r/m16         Valid                 Valid            Push r/m16.

FF /6         PUSH r/m32         N.E.                  Valid            Push r/m32.

FF /6         PUSH r/m         Valid                  N.E.           Push r/m. 

Default operand size -bits.

    没别的好方法,使用中多注意,尽量在位程序中保用位寄存器。 

4.  一些参考资料

    写完了第一个hello world,本文就此打住。本还想写一些内容,但掌握不深,留待下回吧。感觉有些资料不得不在第一篇文章中放出来,因为它们是现有学习x汇编的最好教材了,文中很多代码和知识点也来自于这些资料。

(1)《Moving to Windows x》,出自:http://www.ntcore.com/Files/vista_x.htm

(2)GoASM的帮助文档,目前最好的位汇编教程。出自:www.jorgon.freeserve.co.uk

(3)《开始进行  位 Windows 系统编程之前需要了解的所有信息》,出自:http://www.microsoft.com/china/MSDN/library/Windev/bit/issuesx.mspx

(4)来自CodeGurus的两篇文章

《Assembler & Win》,

http://www.codegurus.be/codegurus/Programming/assembler&win_en.htm

《bout RIP relative addressing》

http://www.codegurus.be/codegurus/Programming/riprelativeaddressing_en.htm

(5)AMD开发手册

(6)Intel开发手册,注意是新的《ntel®  and IA-32 Architectures software Developer’s Manual》

标 题: 【原创】Windows X汇编入门(2)

作 者: tankaiha

时 间: 2007-05-07,22:51

链 接: http://bbs.pediy.com/showthread.php?t=44078

tankaiha

    五一长假就要结束了,总算有时间好好睡了几个懒觉。今天醒来后想到的第一件事就是,该写第二篇了。

    位技术现在还不成熟,没有好调试器,但是我们搞技术的总是对新东西充满了好奇和热情。这个理由就足够我们现在开始学习位汇编了!OK,Let’s go on。

1.  再说Calling convention

    关于API的调用方式,在入门(1)中说了一些,不过感觉有必要再讲两点。一是在调用API时椎栈的框架,也就是Stack Frame,二是利用反汇编位C/C++程序来研究calling convention。

    先说Stack Frame。图1是一个通用的椎栈框架。

    在一个使用STDCALL的32位程序中,stack frame的四项工作:

(1)  传入参数的调用;

(2)  在返回caller时,callee要负责平衡椎栈;

(3)  给局部变量提供空间;

(4)  保证ebx、esi、edi和ebp四个寄存器的值不变(这种寄存器被称为non-volatile)。

在位环境中,少了一个平衡椎栈的任务,因为平衡椎栈的工作由caller负责了,因此callee的stack frame只剩下三项工作:

(1)  将寄存器传入的参数和其它超过4个以上的参数在椎栈上保存(入栈);

(2)  给局部变量提供空间;

(3)  保证non-volatile寄存器的值不变,包括ebp、ebx、rdi、rsi、r12到r15,xmm6到xmm15。

    所以,在一个函数的开始往往有如下代码:

  MOV [RSP+8h],RCX

  MOV [RSP+10h],RDX

  MOV [RSP+18h],R8

  MOV [RSP+20h],R9

  PUSH RBP

  MOV RBP,RSP

    而在返回时会有如下代码:

  LEA RSP,[RBP]

  POP RBP

  RET

    图2摘自GoASM的帮助文档,上文描述的情况在图中一目了然。

    如果能在VC中编译位C/C++程序,再用IDA反汇编,不是挺好的吗?正确,这正是我们玩儿逆向工程的人喜欢的方法。Visual Studio 2005的位开发环境设置网上有,这里不多说了。以一个C/C++的代码为例:

代码:

// Message handler for about box.

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{

  UNREFERENCED_PARAMETER(lParam);

  switch (message)

  {

  case WM_INITDIALOG:

    return (INT_PTR)TRUE;

  case WM_COMMAND:

    if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)

    {

      EndDialog(hDlg, LOWORD(wParam));

      return (INT_PTR)TRUE;

    }

    break;

  }

  return (INT_PTR)FALSE;

}

    这段代码是一个地球人都知道的窗口消息处理代码,在编译为位程序后,用ida看一下它的反汇编。这样,熟悉而又有点陌生的位汇编代码就出来了,包括消息的判断,EndDialog的调用等,确实很方便。

2.  第二个汇编例子:SMC

    在入门(1)中我们写了第一个位的汇编程序,这里我们开始写第二个。当然,代码本身还是有点意思的,这就是Self Modify Code。让我们试一试SMC在位下进行的如何?这还牵涉到vista的特性。代码来自修改过的参考资料《About RIP relative addressing》。

DATA SECTION

testzero    db 'eax值为0', 0

testnonzero  db 'eax值不为0!', 0

testtitle db '测试or eax,eax指令', 0

oldprotect dd ?

CODE SECTION

Start:

  ;改变当前内存页的保护为可写

  sub rsp,28h

  lea r9, oldprotect        ; R9  = lpflOldProtect

  mov r8d, 40h              ; R8D = flNewProtect

  mov rdx, 1                ; RDX = dwSize

  lea rcx, modifyhere ; RCX = lpAddress

  call VirtualProtect

  add rsp,28h

  lea rax,modifyhere

  inc B[rax]

  xor eax,eax

  or eax,eax

  ;如果eax为0,则or指令会使jz跳转

  lea rax, testzero

modifyhere:

  jz >.skip  ;这个是GoASM的语法,>号表示后面代码中的label

  lea rax, testnonzero

.skip    ;GoASM中的label这样定义

  ; 显示结果

  sub rsp,28h

  mov r9d, 0         ; R9D = UINT uType

  lea r8,  testtitle ; R8  = LPCTSTR lpCaption

  mov rdx, rax       ; RDX = LPCSTR lpText

  mov rcx, 0         ; RCX = HWND hWnd

  call MessageBoxA

  mov ecx, eax       ; ECX = UINT uExitCode

  call ExitProcess

  add rsp,28h

  ret

    代码的执行流程如下:将eax赋0,然后进行or eax,eax,如果不修改代码,则jz处会跳转,结果会显示“eax值为0”。我们的任务就是把jz改为jnz。jz的十六进制编码为74,jnz为75。

编译一下:

GoASM /x “2.1.asm”

GoLink “2.1.obj” kernel32.dll user32.dll

    因为我们加入了下面两句代码:

  lea rax,modifyhere

  inc B[rax]

    所以jz为in为jnz了。结果显示如下图所示。注意,GoASM中byte ptr简写为B。当然,你可以把上面两句指令删除,那出来的就完全是另一个结果了。

3.  资源文件

    本文的最后一节来讲下带资源的程序编译。由于GoASM有自己的编译器GoRC,而visual studio中是rc,因此我们将分别用两种语法编写,看一下两个编译器中的相同与不同。

先按下面的代码建立MainDlg.rc,这个rc文件是两个例子通用的,代码来自RadASM的32位默认模板代码,其实就是一个对话框,没有添加任何控件:

代码:

#define IDD_DLG1 1000

IDD_DLG1 DIALOGEX 6,6,194,106

CAPTION "我的第一个DialogBox"

FONT 8,"MS Sans Serif"

STYLE 0x10CF0000

EXSTYLE 0x00000000

BEGIN

END

    来看一下GoASM语法的文件,其中用了很多GoASM的宏语法,不熟悉的可以看下帮助文件。我们把它保存为2.2.asm。

;##################################################################

; DIALOGAPP

;##################################################################

;暂时没有完整的include文件,我们把要用的自己添加进来

#Define WM_INITDIALOG               00110H

#Define WM_DESTROY                  00002H

#Define WM_COMMAND                  00111H

#Define WM_CLOSE                    00010H

#IFNDEF FALSE

  #Define FALSE               0

#ENDIF

#IFNDEF TRUE

  #Define TRUE                1

#ENDIF

CONST SECTION

  IDD_DLG1  equ    1000

DATA SECTION

  hInstance  DQ    ?

CODE SECTION

Start:

  ;invoke是GoASM调用API的宏,用不着我们自己进行stack frame了

  invoke GetModuleHandleA, 0

  mov [hInstance],rax

  invoke InitCommonControls

  invoke DialogBoxParamA,[hInstance],IDD_DLG1,0,ADDR DlgProc,0

  invoke ExitProcess,0

;FRAME是GoASM的宏

DlgProc FRAME hwnd,uMsg,wParam,lParam

  mov eax,[uMsg]

  cmp eax,WM_INITDIALOG

  jne >.WMCOMMAND

    jmp >.EXIT

  .WMCOMMAND

  cmp eax,WM_COMMAND

  jne >.WMCLOSE

    jmp >.EXIT

  .WMCLOSE

  cmp eax,WM_CLOSE

  jne >.DEFPROC

    INVOKE EndDialog,[hwnd],0

  .DEFPROC

    mov eax,FALSE

    ret

  .EXIT

  mov eax, TRUE

  ret

ENDF

    编译时有个很奇怪的问题,就是要把资源文件编译成.obj格式才能顺利链接。命令行如下:

GoRC /machine x /o maindlg.rc

GoASM /x 2.2.asm

GoLink “2.2.obj” maindlg.obj kernel32.dll user32.dll comctl32.dll

生成了2.2.exe后,运行,如下图所示:

    下面,看一下ml的编译过程。rc文件不变,把下面的代码保存为2.3.asm。

;##################################################################

; DIALOGAPP

;##################################################################

extrn GetModuleHandleA : proc

extrn MessageBoxA : proc

extrn InitCommonControls : proc

extrn DialogBoxParamA : proc

extrn DestroyWindow : proc

extrn ExitProcess : proc

extrn EndDialog : proc

.const

 WM_INITDIALOG       equ        00110H

 WM_DESTROY          equ        00002H

 WM_COMMAND          equ        00111H

 WM_CLOSE            equ        00010H

 FALSE               equ 0

 TRUE        equ 1

IDD_DLG1  equ    1000

.data?

  hInstance  qword    ?

.code

Main proc

  ;invode是GoASM调用API的宏,用不着我们自己进行stack frame了

  sub rsp,30h

  xor rcx,rcx

  call GetModuleHandleA

  mov [hInstance],rax

  call InitCommonControls

  mov rcx,[hInstance]

  mov rdx,IDD_DLG1

  xor r8,r8

  lea r9,DlgProc

  push 0

  call DialogBoxParamA

    

  xor rcx,rcx

  call ExitProcess

  add rsp,30h

  ret

Main endp

;DlgProc FRAME hwnd,uMsg,wParam,lParam

DlgProc:

  mov [rsp+8],rcx

  mov [rsp+10h],rdx

  mov [rsp+18h],r8

  mov [rsp+20h],r9

  push rbp

  mov rbp,rsp

  

  mov eax,edx

  cmp eax,WM_INITDIALOG

  jne WMCOMMAND

    jmp EXIT

  WMCOMMAND:

  cmp eax,WM_COMMAND

  jne WMCLOSE

    jmp EXIT

  WMCLOSE:

  cmp eax,WM_CLOSE

  jne DEFPROC

    

    sub rsp,18h

    xor rdx,rdx

    ;注意,这里第一个参数rcx未变。按理应该是由rbp定位

    mov rcx,[rbp+10h]

    call EndDialog

    add rsp,18h

  DEFPROC:

    pop rbp

    mov eax,FALSE

    ret

  EXIT:

  pop rbp

  mov eax, TRUE

  ret

end

    编译命令行为:

rc maindlg.rc

ml 2.3.asm /link /subsystem:windows /entry:Main kernel32.lib user32.lib comctl32.lib maindlg.res

    如果你编译正确了,应该和2.2.exe的运行结果一样。

    和GoASM的宏比起来,ml的语法显得更低级,也更基础。不过要注意的是,在2.3.asm中,很多语法我并没有写得很规范,要想知道最规范的代码,便是逆向高级语言或GoASM 生成的exe文件。

    OK,第二篇就到这里了。五一长假将在今晚结束,明天上班喽。

文档

Windows X汇编入门

标题:【原创】WindowsX汇编入门(1)作者:tankaiha时间:2007-05-05,23:31链接:http://bbs.pediy.com/showthread.php?t=43967Windows X汇编入门(1)tankaiha    最近断断续续接触了些位汇编的知识,这里小结一下,一是阶段学习的回顾,二是希望对位汇编新手有所帮助。我也是刚接触这方面知识,文中肯定有错误之处,大家多指正。文章的标题包含了本文的四方面主要内容:(1)Windows:本文是在wind
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top