Win32汇编教程(基础)
汇编语言基础win32一、引言Win32应用程序一般使用C语言编程,但是在某些需要进行深层编程的情况下,例如Win32应用程序执行机制分析、病毒清除、加密解密等深层编程,或者对于某些速度要求较高的程序,需要使用汇编语言(甚至机器语言)直接编写Win32应用程序。
Win32应用程序虽然和其他32位应用程序(例如32位保护模式DOS 程序)一样可以使用386汇编语言和保护模式编程,但是Win32应用程序的执行机制与其他32位应用程序有一定的差别,例如消息循环、动态链接等,Win32汇编语言也有其特殊的编程方式。
目前国内极少看到有关Win32汇编语言的资料,市面上的汇编语言书籍一般只介绍DOS实模式汇编语言和386保护模式汇编语言,金山公司的《深入Windows编程》一书虽然介绍了使用汇编语言写Windows应用程序的方法,可惜该书只介绍了Win16汇编语言。
为了使大家能对Win32汇编语言的基本编程方法有一定的了解,近日得闲,笔者编写了本教程,旨在抛砖引玉,如果本教程能够带领你走进神秘的Win32汇编语言世界,笔者心愿足矣。
使用本教程,要求读者具有C语言编写Win32应用程序(Win32SDK编程)的基础。
二、进行Win32汇编语言编程的基本软件进行Win32汇编语言编程,应该准备下列基本软件:1、MASM 6.11以上版本的汇编器MASM是Microsoft公司的汇编器,这是最基本的软件,必需MASM 6.11以上版本才能够汇编Win32汇编语言源程序。
不过进行Win32汇编语言编程不必要全套的MASM 6.11,只要一个ML.EXE文件就可以了,Windows 95 DDK中带有MASM 6.11c的ML.EXE文件,Windows 98 DDK中带有MASM 6.11d的ML.EXE文件,都可以使用。
Turbo MASM 5.0(TASM)是Borland公司的汇编器,也可以用来汇编Win32汇编语言源程序,但是TASM的部分语法与MASM不同,用于MASM的Win32汇编语言源程序可能需要修改后才能用TASM汇编。
本教程中的所有Win32汇编语言源程序都基于MASM。
2、Win32SDK进行Win32汇编语言编程需要用到Win32SDK中的资源编译器(RC.EXE)和连接器(LINK.EXE),还需要用到Win32SDK中的引入库文件(KERNEL32.LIB、USER32.LIB、GDI32.LIB等)。
如果没有Win32SDK,Platform SDK也可以,还可以安装Visual C++ 2.0以上版本的Visual C++,笔者使用的是Visual C++ 6.0。
Borland C++ 4.0以上版本的Borland C++也可以使用,只是资源编译器和连接器的文件名不同,分别是BRC.EXE(BRC32.EXE)和TLINK.EXE(TLINK32.EXE),选项也不尽相同,另外Borland C++不支持COFF格式的OBJ文件,汇编时不能使用/coff选项。
3、汇编语言编辑器一个普通的文本编辑器,用于编辑Win32汇编语言源程序。
EDIT、PWB等都可以,Visual C++等编程语言中的编辑器也可以,甚至WORD、WPS 97等可以编辑文本文件的字处理软件都可以,不过笔者推荐使用ASMEDIT,这是一个专用的汇编语言编辑器,效果非常好。
Win32汇编语言一般使用命令行方式汇编连接,经过一定的设置也可以在某些集成环境(PWB、Visual C++、ASMEDIT等)下汇编连接,还可以使用NMAKE工具,不过本教程中只使用命令行方式汇编连接,也不使用NMAKE工具。
三、ANSI字符集API与UNICODE字符集APIWin32 API中凡是与字符有关的API都有两种不同的类型:ANSI字符集API和UNICODE字符集API,分别对应ANSI字符和UNICODE字符,Windows NT支持两种类型的API,Windows 95/98只支持ANSI字符集API。
在WINDOWS.H 头文件和其他Win32 API定义头文件中,凡是与字符有关的API都有两种不同的定义,ANSI字符集API以API 名称加字符“A”表示,UNICODE字符集API以API名称加字符“W”表示,并使用条件编译和宏定义实现自动根据当前字符集使用对应的API定义,例如GetModuleHandle函数的定义(包括在WINBASE.H头文件中):WINBASEAPIHMODULEWINAPIGetModuleHandleA(LPCSTR lpModuleName);WINBASEAPIHMODULEWINAPIGetModuleHandleW(LPCWSTR lpModuleName);#ifdef UNICODE#def ine GetModuleHandle GetModuleHandleW#else#def ine GetModuleHandle GetModuleHandleA#endif // !UNICODE与字符有关的数据结构也有类似的定义。
本教程考虑到汇编语言使用条件汇编会导致不太直观,全部使用ANSI字符集API,这样也可以保证在Windows 95/98和Windows NT环境下的兼容性,所以本教程中许多API名称和数据结构的名称都加有“A”字符,读者可以方便地改用UNICODE字符集API。
四、一个简单的Win32汇编语言程序读者可能一听到“汇编语言”四个字就觉得十分头疼!汇编语言给人的第一印象就是一大堆难以看懂又不直观的指令,而且不结构化,大量的标号、无条件跳转指令(JMP)和条件跳转指令让你难以看懂程序;过程(或者函数)的调用参数传递又不直观,要么直接使用寄存器传递参数,不符合结构化程序设计原则;要么使用堆栈传递参数,又不能有效地检验参数类型……想必Win32汇编语言更麻烦吧!还好,MASM 6.0以上版本的汇编器提供了很多结构化汇编语言伪指令,可以方便地实现汇编语言结构化程序设计,当你看完本教程以后,你可能会感觉到:Win32汇编语言并不比C语言麻烦多少。
(如果读者看不懂本教程中的汇编语言源程序也不要紧,可以对照MASM 6.11的帮助看)和C语言Win32编程需要WINDOWS.H头文件和其他Win32 API定义头文件定义常量、数据结构和API 一样,Win32汇编语言也需要包含文件(INC文件)定义常量、数据结构和API。
不过笔者找了很长时间也没有找到一个完整的可用于Win32汇编语言的WINDOWS.INC文件或者WIN32.INC文件(倒是找到了用于Win16汇编语言的WINDOWS.INC文件),Turbo MASM 5.0中提供的WIN32.INC文件也不完整,只能用于自带的WAP32例子程序,而且与MASM 6.11不太兼容(听说NASM中有完整的WIN32.INC文件,可惜没有找到,也不知道与MASM 6.11是否兼容)。
笔者只好自己定义常量、数据结构和API(根据WINDOWS.H头文件和其他Win32 API定义头文件定义),不过这倒带来了不少好处——可以更好地了解Win32汇编语言的编程方法和原理。
笔者编写了一个简单的Win32汇编语言程序,该程序的功能很简单:在屏幕上显示一个消息框。
本程序只调用了两个API函数:MessageBox函数和ExitProcess函数,程序如下:包含文件(MSGBOX.INC):UINT TYPEDEF DWORDLPSTR TYPEDEF PTR BYTELPCSTR TYPEDEF LPSTRPVOID TYPEDEF PTRHANDLE TYPEDEF PVOIDHWND TYPEDEF HANDLEMB_ICONINFORMATION = 00000040hMB_OK = 00000000hMessageBoxA PROTO stdcall, :HWND,:LPCSTR,:LPCSTR,:UINTExitProcess PROTO stdcall, :UINT源程序(MSGBOX.ASM):.386p.MODEL flat,stdcallINCLUDE MSGBOX.INC.STACK 4096.DATAWindowTitle BYTE 'MsgBox',0Message1 BYTE 'This is a simple MessageBoxWin32 application.',0.CODE_start:INVOKE MessageBoxA,0,ADDR Message1,ADDR WindowTitle,MB_ICONINFORMATION or MB_OKINVOKE ExitProcess,0PUBLIC _startEND汇编连接本程序的命令如下:ml /c /coff /Cp msgbox.asmlink /subsystem:windows /entry:_start msgbox.obj kernel32.lib user32.lib汇编命令中的/c选项表示只汇编,不自动连接;/coff选项表示生成COFF格式的OBJ文件(如果使用Borland的连接器不能使用/coff参数);/Cp选项表示标识符区分大小写。
连接命令中/subsystem:windows选项表示连接器生成普通Windows可执行文件;/entry:_start选项表示程序入口点是_start标识符。
连接时连接KERNEL32.LIB和USER32.LIB引入库。
运行汇编连接后生成的MSGBOX.EXE文件,屏幕上将显示出一个消息框,消息框的标题是“MsgBox”,消息框中的字符串是“This is a simple MessageBox Win32 application.”。
Win32汇编语言源程序应该由.386p伪指令和.MODEL flat,stdcall伪指令开始,指示汇编器汇编386保护模式指令,并使用平坦内存模式(Win32内存模式)和stdcall 函数调用方式(Win32标准函数调用方式)。
PROTO伪指令定义函数原型(与C 语言中函数原型的定义相似),可以定义函数名、调用方式和参数,INVOKE伪指令调用由PROTO伪指令定义的函数,可以方便地传递参数和检查参数类型。
MSGBOX.INC文件中使用PROTO伪指令定义API函数,MSGBOX.ASM文件中使用INVOKE伪指令调用API函数,可见MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令大大简化了Win32汇编语言编程(本程序一条汇编语言指令也没有用到)。
本程序调用了MessageBox函数显示消息框以后,调用了ExitProcess函数终止程序的执行,ExitProcess函数的作用是终止当前进程。
Win32汇编语言-体系结构1.
2018/9/15 7
IA-32CPU工作模式
实地址模式(Real-address Mode)
实现8086处理器的程序设计环境 所有的Intel处理器都是从实地址模式引导的
保护模式(Protected Mode)
处理器的主要状态
虚拟8086模式
(Virtual-8086 Mode)
EDX数据寄存器:乘除运算、I/O指令中特指端口 地址
14
2018/9/15
变址寄存器
变址寄存器常用于存储器寻址时提供地址
ESI源变址寄存器 EDI目的变址寄存器
变址寄存器不可分割成8位寄存器。作为通 用寄存器,也可存储算术逻辑运算的操作 数和运算结果。 串操作类指令中,ESI和EDI具有特定功能, SI和DI分别与DS和ES联用
2018/9/15
5
Intel微处理器发展
IA-32
IA-64(安腾) 奔腾4 奔腾III
奔腾II 奔腾 80486 80386 80286 8086
摩尔定律(Moore's Law) 摩尔定律是指IC上可容纳的晶体 管数目,约每隔18个月将会增加 一倍,性能也将提升一倍!
6
4004
2018/9/15
基本单位是字节B。
更大的容量单位有
KB(千字节)、MB(兆字节)、GB(千兆字节) TB(兆兆字节)
2018/9/15
4
微处理器(CPU)
微处理器是微机的硬件核心 主要包含指令执行的运算和控制部件,还 有多种寄存器 对汇编程序员来说,微处理器抽象为以名 称存取的寄存器 课程以Intel 32位微处理器为主体学习32位 汇编语言程序设计
win32汇编指导--部分
3. 互斥锁
互斥锁是一种特殊的信号量。信号量中有一个 计数器,信号量有2个操作:“获得”、“释 放”。为“获得”信号量,如果信号量的当前 值为 0,那么线程就阻塞,直到计数器值大于 0为止。之后,该计数器值减 1。“释放”信 号量时,该计数器值加 1。 互斥锁是那些值只能为 0 或 1 的信号量。一个 互斥锁可以作为一个“令牌”,每次只允许一 个线程访问共享的全局变量。互斥锁用于线程 之间的互相排斥:在任何一个时刻,只有一个 线程可以“拥有”一个互斥锁。
-创建线程的属性
lpThreadAttributes:线程的安全属性,置该值为NULL 时,使用缺省的安全属性。 dwStackSize:线程的堆栈大小。如果为0,使用和进 程相同大小的堆栈。 lpStartAddress:线程函数的起始地址。 lpParameter:传递给线程的上下文。可以是一个整数, 或者一个指向某个结构的指针,通过lpParameter将参 数传递给线程。 dwCreationFlags:=0表示创建线程后立即启动; =CREATE_SUSPENDED时,需要调用ResumeThread() 函数启动该线程。 lpThreadId:指向一个双字,用于保存线程ID。
5. 任务现场的保存、恢复
程序在临时任务和演示任务之间进行了多次任务切换。临时 任务在进入时,清除屏幕内容,将屏幕左上角(0,0)处的字符 设为’0’。 演示任务执行一次,显示EDI的当前值。第1次执行时,EDI 的值等于(80*4+50)*2,即000002E4h,在屏幕上位置(4,50) 处显示“EDI=000002E4”,之后,EDI增加160,执行下一行。 在切换到临时任务时,EDI的值被保存到DemoTSSSeg中。 下一次从临时任务切换到演示任务时,EDI从DemoTSSSeg 中恢复,所以在屏幕上位置(5,50)处显示。 从临时任务切换到演示任务时,临时任务的ECX值也被保存 到TempTSSSeg中,切换回临时任务时,从TempTSSSeg恢 复ECX值,所以,循环一共执行5次,每次将屏幕左上角(0,0) 处的字符加1。执行结束时,该字符变为’5’。
汇编教程Win32调试API(1)
在本教程中,我们将学习Win32提供给开发者的用于调试的原语. 在教程的结尾,我们将学习如何调试一个进程.理论:Win32有一些供程序员使用的API,它们提供相当于调试器的功能. 他们被称作Win32调试API(或原语).利用这些API,我们可以:加载一个程序或捆绑到一个正在运行的程序上以供调试获得被调试的程序的低层信息,例如进程ID,进入地址,映像基址等.当发生与调试有关的事件时被通知,例如进程/线程的开始/结束, DLL的加载/释放等.修改被调试的进程或线程简而言之,我们可以用这些API写一个简单的调试器.由于这个题目有些过大,我把它分为几部分,而本教程就是它的第一部分.在本教程中,我将讲解一些基本概念及Win32调试AP I的大致框架.使用Win32调试API的步骤如下:创建一个进程或捆绑到一个运行中的进程上. 这是使用Win32调试API的第一步.由于我们的程序要扮演调试器的角色,我们要找一个供调试的程序.一个被调试的程序被称为de buggee.可以通过以下两种方式获得debuggee:通过CreateProcess创建debuggee进程.为了创建被调试的进程,必须指定DEBUG_PROC ESS标志.这一标志告诉Windows我们要调试该进程. 当debuggee中发生重要的与调试有关的事件(调试事件)时,Windows 会向我们的程序发送通知.debuggee会立即挂起以等待我们的程序准备好.如果debuggee还创建了子进程,Windows还会为每个子进程中的调试事件向我们的程序发送通知.这一特性通常是不必要的.我们可以通过指定DEBUG_ONLY_THIS_PROC ESS与 DEBUG_PROCESS的组合标志来禁止它.我们也可以用 DebugActiveProcess标志捆绑到一个运行中的进程上.等待调试事件. 在获得了一个debuggee进程后,debuggee的主线程被挂起,这种状况将持续到我们的程序调用WaitForDebugEvent为止.这个函数和其他的WaitForXXX函数相似,比如说,它阻塞调用线程直到等待的事件发生.对这个函数来说, 它等待由Windows发送的调试事件.下面是它的定义:WaitForDebugEvent proto lpDebugEvent:DWORD, dwMilliseconds:DWORDlpDebugEvent is the address of a DEBUG_EVENT这个结构将被填入关于debuggee中发生的调试事件的信息.dwMilliseconds 该函数等待调试事件的时间,以毫秒为单位.如果这段时间没有调试事件发生, WaitForDebugEvent返回调用者.另一方面,如果将该参数指定为 INFINITE 常数,函数将一直等待直到调试事件发生.现在我们看一下DEBUG_EVENT 结构.DEBUG_EVENT STRUCTdwDebugEventCode dd ?dwProcessId dd ?dwThreadId dd ?u DEBUGSTRUCT <>DEBUG_EVENT ENDSdwDebugEventCode 该值指定了等待发生的调试事件的类型.因为有很多种类型的事件发生,我们的程序要检查该值,知道要发生事件的类型并做出响应. 该值可能的取值如下:取值含义CREATE_PROCESS_DEBUG_EVENT 进程被创建.当debuggee进程刚被创建(还未运行) 或我们的程序刚以DebugActiveProcess被捆绑到一个运行中的进程时事件发生. 这是我们的程序应该获得的第一个事件.EXIT_PROCESS_DEBUG_EVENT 进程退出.CREATE_THEAD_DEBUG_EVENT 当一个新线程在deuggee进程中创建或我们的程序首次捆绑到运行中的进程时事件发生.要注意的是当debugge的主线程被创建时不会收到该通知.EXIT_THREAD_DEBUG_EVENT debuggee中的线程退出时事件发生.debugee的主线程退出时不会收到该通知.我们可以认为debuggee的主线程与debugge进程是同义词. 因此, 当我们的程序看到CREATE_PROCESS_DEBUG_EVENT标志时,对主线程来说,就是CREATE_THREAD_DE BUG_EVENT标志.LOAD_DLL_DEBUG_EVENT debuggee装入一个DLL.当PE装载器第一次分解指向DLL的链接时,我们将收到这一事件. (当调用CreateProcess装入 debuggee时)并且当debuggee调用LoadLibrary时也会发生.UNLOAD_DLL_DEBUG_EVENT 一个DLL从debuggee中卸载时事件发生.EXCEPTION_DEBUG_EVENT 在debuggee中发生异常时事件发生. 注意: 该事件仅在debu ggee开始它的第一条指令之前发生一次.异常实际上是一个调试中断(int 3h).如果想恢复debuggee事,以 DBG_CONTINUE 标志调用ContinueDebugEvent 函数. 不要使用DBG_EXCEPT ION_NOT_HANDLED 标志否则debuggee会在NT下拒绝运行(Win98下运行得很好).OUTPUT_DEBUG_STRING_EVENT 当debuggee调用DebugOutputString函数向我们的程序发送消息字符串时该事件发生.RIP_EVENT 系统调试发生错误dwProcessId 和dwThreadId发生调试事件的进程和线程Id.我们可以用这些值作为我们感兴趣的进程或线程的标志符.记住如果我们使用CreateProcess来装载debuggee,我们仍可在PROCESS_INFO结构中获得debuggee的进程和线程.我们可以用这些值来区别调试事件是发生在debuggee中还是它的子进程中(当没有指定 DEBUG_ONLY_THIS_PROCESS 标志时).u 是一个联合,包含了调试事件的更多信息.根据上面dwDebugEventCode的不同,它可以是以下结构:dwDebugEventCode u的解释CREATE_PROCESS_DEBUG_EVENT 名为CreateProcessInfo的CREATE_PROCESS_DEBUG_INF O结构EXIT_PROCESS_DEBUG_EVENT 名为ExitProcess的EXIT_PROCESS_DEBUG_INFO结构CREATE_THREAD_DEBUG_EVENT 名为CreateThread的CREATE_THREAD_DEBUG_INFO结构EXIT_THREAD_DEBUG_EVENT 名为ExitThread的EXIT_THREAD_DEBUG_EVENT 结构LOAD_DLL_DEBUG_EVENT 名为LoadDll的LOAD_DLL_DEBUG_INFO 结构UNLOAD_DLL_DEBUG_EVENT 名为UnloadDll的UNLOAD_DLL_DEBUG_INFO结构EXCEPTION_DEBUG_EVENT 名为Exception的EXCEPTION_DEBUG_INFO结构OUTPUT_DEBUG_STRING_EVENT 名为DebugString的OUTPUT_DEBUG_STRING_INFO 结构RIP_EVENT 名为RipInfo的RIP_INFO 结构我不会在这一个教程里讲所有这些结构的细节,这里只详细讲一下CREATE_PROCESS_DE BUG_INFO 结构.假设我们的程序调用了WaitForDebugEvent函数并返回,我们要做的第一件事就是检查dwDebugEventCode中的值来看debuggee进程中发生了那种类型的调试事件.比如说,如果d wDebugEventCode的值为 CREATE_PROCESS_DEBUG_EVENT,就可认为u的成员为CreateProce ssInfo 并用u.CreateProcessInfo来访问.在我们的程序中做对调试事件的响应. 当WaitForDebugEvent 返回时,这意味着在deb uggee进程中发生了调试事件或者发生了超时.所以我们的程序要检查dwDebugEventCode 来作出适当的反应.这里有些象处理Windows消息:由用户来选择和忽略消息.继续运行debuggee. 当调试事件发生时, Windows挂起了debuggee,所以当我们处理完调试事件,还要让debuggee继续运行.调用ContinueDebugEvent 函数来完成这一过程.ContinueDebugEvent proto dwProcessId:DWORD, dwThreadId:DWORD, dwContinueSta tus:DWORD该函数恢复由于调试事件而挂起的线程.dwProcessId和dwThreadId是要恢复的线程的进程ID和线程ID,通常这两个值从 DEB UG_EVENT结构的dwProcessId 和dwThreadId成员获得.dwContinueStatus显示了如何继续报告调试事件的线程.可能的取值有两个: DBG_CON TINUE 和DBG_EXCEPTION_NOT_HANDLED. 对大多数调试事件,这两个值都一样:恢复线程.唯一的例外是EXCEPTION_DEBUG_EVENT,如果线程报告发生了一个异常调试事件,这意味着在d ebuggee的线程中发生了一个异常.如果指定了DBG_CONTINUE,线程将忽略它自己的异常处理部分并继续执行.在这种情况下,我们的程序必须在以DBG_CONTINUE恢复线程之前检查并处理异常,否则异常将生生不息地不断发生....如果我们指定了 DBG_EXCEPTION_NOT_HANDL ED值,就是告诉Windows我们的程序并不处理异常:Windows将使用debuggee的默认异常处理函数来处理异常.总而言之,如果我们的程序没有考虑异常,而调试事件又指向debuggee进程中的一个异常的话,就应调用含DBG_CONTINUE标志的ContinueDebugEvent函数.否则,我们的程序就必须以DBG_EXCEPTION_NOT_HANDLED调用 ContinueDebugEvent.但在下面这种情况下必须使用DBG_CONTINUE标志:第一个在ExceptionCode成员中有值EXCEPTION_BREAKPOINT的 EXC EPTION_DEBUG_EVENT事件.当debuggee开始执行它的第一条指令时,我们的函数将接受到异常调试事件.它事实上是一个调试中断(int 3h).如果我们以DBG_EXCEPTION_NOT_HANDLED 调用ContinueDebugEvent 来响应调试事件, Windows NT会拒绝执行debuggee(因为它没有异常处理).所以在这种情况下,要用DBG_CONTINUE标志告诉Windows我们希望该线程继续执行.继续上面的步骤循环直到debuggee进程退出. 我们的程序必须在一个很象消息循环的无限循环中直到debuggee结束.该循环大体如下:.while TRUEinvoke WaitForDebugEvent, addr DebugEvent, INFINITE.break .if DebugEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT<调试事件处理>invoke ContinueDebugEvent, DebugEvent.dwProcessId, DebugEvent.dwThreadId, DBG_E XCEPTION_NOT_HANDLED.endw就是说,当开始调试程序时,我们的程序不能和debuggee分开直到它结束.我们再来总结一下这些步骤:创建一个进程或捆绑我们的程序到运行中的进程上.等待调试事件响应调试事件.继续执行debuggee.继续这一无尽循环直到debuggee进程结束例子:这个例子调试一个win32程序并显示诸如进程句柄,进程Id,映象基址等..386.model flat,stdcalloption casemap:noneinclude \masm32\include\windows.incinclude \masm32\include\kernel32.incinclude \masm32\include\comdlg32.incinclude \masm32\include\user32.incincludelib \masm32\lib\kernel32.libincludelib \masm32\lib\comdlg32.libincludelib \masm32\lib\user32.lib.dataAppName db "Win32 Debug Example no.1",0ofn OPENFILENAME <>FilterString db "Executable Files",0,"*.exe",0db "All Files",0,"*.*",0,0ExitProc db "The debuggee exits",0NewThread db "A new thread is created",0EndThread db "A thread is destroyed",0ProcessInfo db "File Handle: %lx ",0dh,0Ahdb "Process Handle: %lx",0Dh,0Ahdb "Thread Handle: %lx",0Dh,0Ahdb "Image Base: %lx",0Dh,0Ahdb "Start Address: %lx",0.data?buffer db 512 dup(?)startinfo STARTUPINFO <>pi PROCESS_INFORMATION <>DBEvent DEBUG_EVENT <>.codestart:mov ofn.lStructSize,sizeof ofnmov ofn.lpstrFilter, offset FilterStringmov ofn.lpstrFile, offset buffermov ofn.nMaxFile,512mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_E XPLORER or OFN_HIDEREADONLYinvoke GetOpenFileName, ADDR ofn.if eax==TRUEinvoke GetStartupInfo,addr startinfoinvoke CreateProcess, addr buffer, NULL, NULL, NULL, FALSE, DEBUG_PROCESS+ DEBU G_ONLY_THIS_PROCESS, NULL, NULL, addr startinfo, addr pi.while TRUEinvoke WaitForDebugEvent, addr DBEvent, INFINITE.if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENTinvoke MessageBox, 0, addr ExitProc, addr AppName, MB_OK+MB_ICONINFORMATION.break.elseif DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENTinvoke wsprintf, addr buffer, addr ProcessInfo, DBEvent.u.CreateProcessInfo.hFi le, DBEvent.u.CreateProcessInfo.hProcess, DBEvent.u.CreateProcessInfo.hThread, DBEvent.u.CreateProcessInfo.lpBaseOfImage, DBEvent.u.CreateProcessInfo.lpStartA ddressinvoke MessageBox,0, addr buffer, addr AppName, MB_OK+MB_ICONINFORMATION.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINU E.continue.endif.elseif DBEvent.dwDebugEventCode==CREATE_THREAD_DEBUG_EVENTinvoke MessageBox,0, addr NewThread, addr AppName, MB_OK+MB_ICONINFORMATION.elseif DBEvent.dwDebugEventCode==EXIT_THREAD_DEBUG_EVENTinvoke MessageBox,0, addr EndThread, addr AppName, MB_OK+MB_ICONINFORMATION.endifinvoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTI ON_NOT_HANDLED.endwinvoke CloseHandle,pi.hProcessinvoke CloseHandle,pi.hThread.endifinvoke ExitProcess, 0end start分析:程序首先填充OPENFILENAME结构,调用GetOpenFileName让用户选择要调试的程序. invoke GetStartupInfo,addr startinfoinvoke CreateProcess, addr buffer, NULL, NULL, NULL, FALSE, DEBUG_PROCESS+ DEBU G_ONLY_THIS_PROCESS, NULL, NULL, addr startinfo, addr pi当接收用户选择后,调用CreateProcess装载程序.并调用GetStartupInfo以默认值填充STARTUPINFO结构.注意我们将DEBUG_PROCESS标志与DEBUG_ONLY_THIS_PROCESS标志组合来仅调试这个程序,不包括子进程..while TRUEinvoke WaitForDebugEvent, addr DBEvent, INFINITE在debuggee被装入后,我们调用WaitForDebugEvent进入无尽的调试循环,WaitForDeb ugEvent在debuggee中发生调试事件时返回,因为我们指定了INFINITE作为第二个参数.当调试事件发生时, WaitForDebugEvent 返回并填充DBEvent结构.本文来自编程入门网---w .if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENTinvoke MessageBox, 0, addr ExitProc, addr AppName, MB_OK+MB_ICONINFORMATION.break我们要先检查dwDebugEventCode的值, 如果是EXIT_PROCESS_DEBUG_EVENT,用一个消息框显示"The debuggee exits" 并退出调试循环..elseif DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENTinvoke wsprintf, addr buffer, addr ProcessInfo, DBEvent.u.CreateProcessInfo.hFi le, DBEvent.u.CreateProcessInfo.hProcess, DBEvent.u.CreateProcessInfo.hThread, DBEvent.u.CreateProcessInfo.lpBaseOfImage, DBEvent.u.CreateProcessInfo.lpStartA ddressinvoke MessageBox,0, addr buffer, addr AppName, MB_OK+MB_ICONINFORMATION 如果dwDebugEventCode 的值为CREATE_PROCESS_DEBUG_EVENT,我们就在消息框中显示一些感兴趣的底层信息.这些信息从u.CreateProcessInfo获得. CreateProcessInfo是一个CREATE_PROCESS_DEBUG_INFO类型的结构体.你可以查阅Win32 API获得它的更多信息e..elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINU E.continue.endif如果dwDebugEventCode 的值为EXCEPTION_DEBUG_EVENT,我们就要更进一步检查异常类型.它是一大堆的结构嵌套,但我们可以从ExceptionCode成员获得异常类型.如果ExceptionCode的值为 EXCEPTION_BREAKPOINT并且是第一次发生(或者我们已知道deuggee中没有int 3h指令),我们可以安全地假定在debuggee要执行第一条指令时发生这一异常.在我们完成这些处理后,就可以用 DBG_CONTINUE调用ContinueDebugEvent来继续执行debuggee.接着我们继续等待下一个调试事件的发生..elseif DBEvent.dwDebugEventCode==CREATE_THREAD_DEBUG_EVENTinvoke MessageBox,0, addr NewThread, addr AppName, MB_OK+MB_ICONINFORMATION.elseif DBEvent.dwDebugEventCode==EXIT_THREAD_DEBUG_EVENTinvoke MessageBox,0, addr EndThread, addr AppName, MB_OK+MB_ICONINFORMATION.endif如果dwDebugEventCode 的值为CREATE_THREAD_DEBUG_EVENT或EXIT_THREAD_DEBUG_E VENT, 我们的程序显示一个消息框.invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTI ON_NOT_HANDLED.endw除了上面讨论过的 EXCEPTION_DEBUG_EVENT,用DBG_EXCEPTION_NOT_HANDLED标志调用ContinueDebugEvent函数恢复debuggee的执行.invoke CloseHandle,pi.hProcessinvoke CloseHandle,pi.hThread当debuggee结束时,我们就跳出了调试循环,这时要关闭 debuggee的线程和进程句柄.关闭这些句柄并不意味着要关闭这些进程和线程.只是说不再用这些句柄罢了.。
32位汇编语言学习笔记3leal和算术运算指令
32位汇编语言学习笔记3leal和算术运算指令32位汇编语言学习笔记在学习汇编语言的过程中,我们已经了解了一些基本指令和寄存器的用法。
本文将继续介绍两个重要的指令:leal指令和算术运算指令。
通过深入了解和学习这两个指令,我们将更好地理解和掌握汇编语言编程的技巧和方法。
一、leal指令leal指令用于将一个有效地址(Effective Address,EA)加载到目标操作数中。
它的一般格式为:leal Source, Destination。
在这个指令中,Source表示源操作数,可以是寄存器、内存或立即数。
Destination表示目标操作数,只能是寄存器。
leal指令在源操作数的基础上进行计算,将计算结果存储到目标操作数中。
下面是一些leal指令的示例:1. leal (%eax,%ebx,4), %edx这条指令将地址(%eax + %ebx * 4)的结果存储到%edx寄存器中。
其中,%eax是个基址寄存器,%ebx是个变址寄存器,4表示缩放因子。
2. leal -8(%ebp), %ecx这条指令将地址(%ebp - 8)的结果存储到%ecx寄存器中。
其中,%ebp是个基址寄存器,-8是个偏移量。
需要注意的是,leal指令只能进行地址计算,并将结果存储到目标操作数中,不能进行实际的加载操作。
二、算术运算指令在汇编语言中,算术运算指令主要用于进行数值的计算和操作。
常见的算术运算指令包括add、sub、mul、div等。
这些指令可以对数据寄存器和内存中的数据进行四则运算。
下面是一些常用的算术运算指令及其示例:1. add指令:用于将两个操作数相加,并存储结果到目标操作数中。
一般格式为:add Source, Destination。
示例:add %eax, %ebx这条指令将%eax和%ebx寄存器中的值相加,结果存储到%ebx中。
2. sub指令:用于将第一个操作数减去第二个操作数,并将结果存储到目标操作数中。
(最新整理)Win32汇编语言-分支与循环.-完整资料
2020/6/12
10
例题:求绝对值
;计算EAX的绝对值
Good
cmp eax,0
jge nonneg
neg eax
nonneg: mov result,eax
;分支条件:AX≥0 ;条件不满足,求补 ;条件满足
;计算AX的绝对值
Bad
cmp eax,0
jl yesneg
jmp nonneg
yesneg: neg eax
2020/6/12
15
循环构造
again:
mov mov
mov xor mov loop
ecx, 35Ah dl, 5Ah
al, byte_40619C[ecx] al, dl byte_40619C[ecx], al again
构造循环
xor mov loc_40659E: mov xor mov inc cmp jb
分支与循环
宋军 计算机学院信息安全系
songjun@
2020/6/12
1
教学重点
分支结构
条件表达式 跳转指令:JMP、JCC
循环结构
LOOP指令 跳转指令构造
分支和循环结构的实现
2020/6/12
2
分支结构
在高级语句中,分支结构一般用IF语句来 实现,在汇编语言中,调用无条件转移指 令或条件转移指令实现分支结构。
2020/6/12
9
比较有符号数大小
有符号数的大(Greater)小(Less)需要 组合OF、SF标志,并利用ZF标志确定相等 (Equal)
两数的大小分成4种关系:
⑴ 小于(不大于等于):JL(JNGE) ⑵ 不小于(大于等于):JNL(JGE) ⑶ 小于等于(不大于):JLE(JNG) ⑷ 不小于等于(大于):JNLE(JG )
Win32汇编--使用MASM
Win32汇编--使⽤MASM经过上⼀讲的准备⼯作,相信⼤家已经搭建好了 Win32 汇编的⼯作环境,并已经知道编译、链接⼀个程序的过程和原理了。
现在,我们让例⼦回归到经典:include <stdio.h>int main(void){Printf(“Hello, world\n”);} // 事实上想想,这不正是初⽣的婴⼉?!⿇雀虽⼩,五脏俱全。
刚刚那个C语⾔的”Hello, world”程序包含了C语⾔中的最基本的格式。
在C语⾔的源程序中,我们不需要为堆栈段、数据段和代码段的定义⽽烦恼,编译器会⾃⼰解决。
回顾⼀下,在DOS 下的汇编这段代码会变成什么样? Follow me!;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 堆栈段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>stack segmentdb 100 dup (?) ;定义100个字节的内存存储单元空间,默认值为?stack ends ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 数据段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>data segmentszHello db ‘Hello, world’,0dh,0ah,’$’data ends ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 代码段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>code segmentassume cs:code,ds:data,ss:stackstart:mov ax,datamov ds,axmov ah,9mov dx,offset szHelloint 21hmov ah,4chint 21hcode endsend start在例⼦中我们看到,stack、data、code都找到了⾃⼰的⼩窝。
学Win32汇编[10]:TYPE、$、LENGTHOF、SIZEOF、TYPEDEF
学Win32汇编[10]:TYPE、$、LENGTHOF、SIZEOF、TYPEDEF TYPE: 获取类型⼤⼩, 对数组是获取每个元素的⼤⼩; Test10_1.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data?v1 db ?v2 dw ?v3 dd ?v4 df ?v5 dq ?v6 dt ?v7 db 1024 dup(?).codemain procPrintDec (type v1) ;1PrintDec (type v2) ;2PrintDec (type v3) ;4PrintDec (type v4) ;6PrintDec (type v5) ;8PrintDec (type v6) ;10PrintDec (type v7) ;1retmain endpend main$: 当前程序语句的地址; Test10_2.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.dataV1 db 11,22,33,44V1Size = ($ - V1) ;这是变量 V1 的⼤⼩V2 dw 11,22,33,44V2Size = $ - V2 ;这是变量 V2 的⼤⼩V3 dd 10 dup(0)V3Size = $ - V3 ;这是变量 V3 的⼤⼩V3Len = ($-V3) / 4 ;这是变量 V3(dword 类型数组)的元素数⽬V4 db 'abcdefghijklmnopqrstuvwxyz', 0V4Size = $ - V4 ;这是变量 V4 的⼤⼩.codemain procPrintHex $ ;00401001, 这是当前语句的地址 PrintDec V1Size ;4PrintDec V2Size ;8PrintDec V3Size ;40PrintDec V3Len ;10PrintDec V4Size ;27PrintHex $ ;0040135A, 这是当前语句的地址 retmain endpend mainLENGTHOF: 数组元素个数; SIZEOF: 总字节数; Test10_3.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.datav1 db 'abc', 0v2 dd 11, 22, 33, 44, 55v3 dd 10 dup(0).codemain procPrintDec (lengthof v1) ;4PrintDec (lengthof v2) ;5PrintDec (lengthof v3) ;10PrintDec (sizeof v1) ;4PrintDec (sizeof v2) ;20PrintDec (sizeof v3) ;40retmain endpend mainTYPEDEF: 创建⾃定义类型; Test10_4.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib;⾃定义⼀个等同于 DWORD 的类型: Cardinal Cardinal typedef ptr dword.dataval Cardinal 100 ;使⽤ Cardinal 定义变量.codemain procPrintDec val ;100retmain endpend main;TYPEDEF 还⽤于定义函数原型, 如:;MyType typedef proto :DWORD, :DWORD, :DWORD。
win32汇编程序设计
win32汇编程序设计Win32汇编程序设计Win32汇编是一种基于x86架构的汇编语言,用于开发Windows操作系统的应用程序。
它的设计目标是实现高效、快速和可靠的程序,利用底层硬件资源来实现更高级别的功能。
本文将介绍Win32汇编的概念、特点和应用,以及如何进行Win32汇编程序设计。
一、Win32汇编的概念和特点Win32汇编是一种面向Windows平台的汇编语言,它是基于Intel x86架构的指令集。
与高级语言相比,汇编语言更接近底层硬件,可以直接控制计算机的寄存器、内存和外设等资源。
Win32汇编程序可以通过调用Windows API函数来实现各种功能,如图形界面、文件操作、网络通信等。
Win32汇编具有以下特点:1. 直接访问硬件资源:Win32汇编允许程序员直接访问寄存器、内存和外设等底层硬件资源,从而实现更高级别的功能。
这种直接访问的特性使得Win32汇编在一些对效率要求较高的应用场景中具有优势。
2. 高效、快速:由于汇编语言直接操作底层硬件,因此具有更高的执行效率和更快的运行速度。
这使得Win32汇编在一些对性能要求较高的应用中被广泛使用,如游戏开发、图像处理等。
3. 灵活性:Win32汇编语言具有很高的灵活性,可以根据具体需求进行定制化开发。
程序员可以直接控制底层资源,实现特定的功能,而不受高级语言的限制。
二、Win32汇编的应用领域Win32汇编广泛应用于Windows操作系统的应用程序开发中。
以下是一些典型的应用领域:1. 系统工具开发:Win32汇编可以用于开发各种系统工具,如磁盘清理工具、注册表编辑工具等。
通过直接访问底层资源,可以实现更高级别的功能。
2. 游戏开发:由于Win32汇编具有高效、快速的特点,因此在游戏开发中被广泛应用。
通过直接操作硬件资源,可以实现更复杂的游戏逻辑和更出色的性能。
3. 图像处理:Win32汇编可以用于图像处理领域,如图像压缩、图像滤波、图像识别等。
WIN32汇编-第2课
WIN32汇编-第2课2012年12月5日星期三15:36pdb文件.pdb文件:符号描述文件,记录代码行与汇编一一对应的表.使用winhex查看pdb文件格式,如图1几种编译选项:/od (Optimization Disable ) 禁用优化/zi 产生符号文件OllyDbg的使用使用看雪官网的版本,仅仅是做了汉化处理的.便于学习. UDD文件夹od目录下面的的UDD文件夹,如图1图1UDD文件夹中存放调试过程中的存档信息,譬如: 断点信息,注释信息等.Lib文件夹lib文件夹:存放一些一些动态链接库的导入库.lib库作用:主程序可以通过函数名称查询函数编号.而OD则通过lib文件夹中的lib库的函数编号反查其函数名称,提高程序的可读性.如图2图2OMS文件夹OMS文件夹:用于存放第3方的一些调试脚本,例如可以简化手工操作,我们不必关心.可以通过文件夹里面的OM帮助文档来学习一下脚本的使用.如图3图3 Plugin文件夹Plugin文件夹:用于存放插件的目录如图4图4Plugin_bak文件夹Plugin_bak文件夹:用于存放插件的备份.如图5图5Script文件夹Script文件夹:同样是用来存放第三方脚本的,我们不用关系.如图6图6Test文件夹Test文件夹:用来测试程序的.如图7图7 Tools文件夹Tools文件夹:同样是用来测试程序用的.如图8图8将OD设置为即时调试器如图9图9然后就可以设置了即时调试了,如图10图10将OD设置菜单关联同上面的操作,如图11图11这样OD将加入资源管理菜单项了,右键附加程序就比较方便了.如图12图12OD的命令行插件ntsd的那些调试命令又可以使用了,如图13d 地址 : 查看该地址的内存单元如图14图14右边还有命令提示.d 地址 -- 查看该地址的内存数据如图15图15 dump 地址 -- 同d一样db 地址 -- 以字节方式显示如图16图16 dw 地址 -- 以2个字节方式显示如图17图17 dd 地址 -- 以4字节方式显示如图18图18dc 地址 -- 以ASCII方式显示如图19图19 du 地址 -- 以UNICODE方式显示如图20图20 da 地址 -- 以反汇编方式显示如图21图21调试功能:单步步入F7:首先在程序断在call处,如图22:图22 F7就能跟进去,如图23图23单步步过F8同样上例,F8不会跟进Call中,如图24图24重新载入程序CTRL+F2(重新载入程序,默认断在程序入口点处) OEP:(Program Entry Point) 应用程序入口点如果不小心跟丢了,就可以重新载入程序,重新载入程序快捷键:CTRL+F2如图25程序已经退出了,CTRL+F2就重新载入了图25修改数据CTRL+E如图26图26光标现在在48h处,直接CTRL+E即可修改数据,直接在ASCII码栏填字符串即可,如图27图27回车之后,字符串被成功修改,如图28图28修改寄存器的值光标定位到寄存器值上面然后双击即可修改寄存器的值,如图29图29填入新值,0000ffff,观察寄存器情况,如图30图30注意:双击不能修改EIP的值,如果想要修改,直接使用jmp 地址来修改EIP的值.段寄存器的值同样是双击修改,但是最好不要随便修改,除非自己比较了解程序的执行情况.修改汇编代码修改汇编直接双击即可,如图31图31单击汇编按钮以后,就修改成功了,如图32图32如果想要保存修改结果,可以选择上修改过的所有行,然后右键选择复制到可执行文件中的选择部分,也可以选择所有改动,如图33图33单击选择部分以后,如图34图34右键单击备份数据到文件即可,如图35图35如果想要保存数据的修改,选择上修改的数据,如图36图36然后右键选择复制到可执行文件,如图37图37然后右键选择备份保存数据到文件即可,如图38图38然后载入修改之后的程序,观察是否修改成功了,如图39图39查看执行模块想要查看执行模块,选择主菜单中的查看选项卡,然后选择执行模块,如图40然后可执行模块就出来了,如图41图41运行程序运行程序:F9程序重新载入,被挂起,如图42F9之后就可以运行起来程序了,如图43图43记录如图44图44如图45图45执行模块如图46图46如图47图47内存如图48图48内存的前面0xFFFF是系统保留的,分配内存以块为单位.块以页为单位,一个分页是0x1000,如图49图49进程如图50图50句柄程序中申请了哪些句柄,如图51图51断点下断点的几种方法:1.F22.在机器码窗口中双击删除断点的方法同下断点方法如图52图52地址栏变红了,表示下了断点.查看一下断点窗口,可以看到下了的两个断点,如图53图53想删除断点的话,同样是F2或者双击即可,如图54图54调用堆栈(同VC6.0的调用堆栈)程序暂停之后,可以像VC中一样查看调用堆栈,栈回溯功能,如图55图55VC中双击即可栈回溯,如图56图56OD的中调用堆栈回溯,选择想要定位的地址,右键跟随堆栈地址,如图57图57然后就会马上定位到栈窗口中相应的地址处,如图58然后右键选择在反汇编窗口跟随就可以定位到相应的汇编代码处了,如图59单击之后,反汇编窗口就马上定位过去了,如图60图60执行到光标处如果光标定位到某行汇编代码,F4就可以直接执行到这行代码,如图61光标在ExitProcess处,F4以后,程序就运行到这里了.如图62图62如果查看代码的时候移到了别的地方,想回去怎么办?如图63图63可以直接双击EIP即可,如图64图64修改注释选择希望修改注释的那行汇编代码,单击;键即可,或者双击注释栏也行,如图65图65编辑好注释以后,单击确定,注释就修改好了,如图66图66。
Win32汇编语言-概述.
汇编语言程序设计
最古老的程序设计语言
深入理解计算机体系结构和操作系统
高级汇编语法
2018/9/15
,MASM,TASM接近MASM
6
什么是汇编语言
汇编语言是一种面向机器的低级程序设计语言 汇编语言以助记符形式表示每一条计算机指令 助记符:易记忆、描述指令功能和指令操作数 汇编指令:用助记符表示的指令 汇编语言:汇编格式指令、编写规则 汇编程序:称汇编语言源程序(.ASM)
使用助记符的语句 汇编指令与机器指令
语言,纯粹的数字形式 特定的处理器才能理解
一一对应
2018/9/15
11
汇编语言与高级语言
高级语言与汇编语言及机器语言是一对多 的关系
一条简单的C语言会被扩展成多条汇编语言或
机器语言指令
x = ( y + 4 )*3 mov add mov imul mov
2018/9/15
7
编译和调试环境
编辑器、编译器、链接器和调试器
编辑器创建源文件,如文本或代码编辑器 编译器把汇编源程序转换为二进制目标文件 链接器是把编译器创建的多个单独的文件组合 链 接成可执行文件 调试器查看程序运行中寄存器和内存状态
.ASM .OBJ .EXE
编辑器
编译器
链接器
虚拟机VM1(L1)程序在虚拟机VM0(L0) 执行,例如Java虚拟机(JVM)
24
2018/9/15
虚拟机层次
高级语言 5th 汇编语言 4th 操作系统 3th
指令集体系结构 2nd
C++,C#,Java等,翻译为第4层 翻译为第2层;中断在第3层
