欢迎来到淘文阁 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
淘文阁 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    Windows 汇编语言编程教程.doc

    • 资源ID:79333837       资源大小:55.50KB        全文页数:21页
    • 资源格式: DOC        下载积分:15金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要15金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    Windows 汇编语言编程教程.doc

    Windows 汇编语言编程教程Version 1.02Copyright2005,Jeff Huang.All rights reservedTranslator:fqh 2005.7.10JEFF HUANG 作,fqh译目录介 绍 2为什么选用汇编语言 2为什么选择Windows系统2开始学习之旅 .3编译器 3编辑器 3第一个程序 4控制台程序 4窗体程序 6ADDR 与 OFFSET 6汇编基础7cpu寄存器 7指令集基础 8 Push 和 Pop8 Invoke 9程序例子 9IV. 窗体程序基础10预备知识10 宏10 过程10变量10一个简单的窗体程序11IV. 深入汇编和系统13字符串操作13文件管理13 存储14 程序例子14控制15附加资源 16 互联网16 书籍16 MASM3216 MSDN16 新闻组16 IRC 16介 绍“This is for all you folks out there,who want to learn the magic art of Assembly programming”-MAD介 绍我最近才开始学习windows系统汇编语言编程,这个教程是我在学习汇编语言的过程中写下来的。我阅读大量的在线教程、书本,以及通过新闻组以及IRC通讯工具请问他人,本人就是通过这些方式学习汇编语言的。互联网上有很多的汇编编程的教程,但这些教程只是侧重于X86汇编。因为这些教材都假定读者已经掌握了高级编程语言以及基本的计算机系统知识。为什么选用汇编语言?汇编语言具有若干的特色,使得在某此情况下,汇编语言是一种很好的选择。1 快速 汇编语言程序运行的速度比高级语言程序要快。通常,要求运行效率高的子程序是用汇编语言编写的。2 强大 运用汇编语言,你能得到不受限制的权力。相对的,高级语言则有种种限制,在实现某些特定的要求时变得困难。3 体积小 汇编语言程序通常比其他语言程序要小得多。这种特性在空间有限的情况下是非常有用的。为什么选择Windows系统?在任何操作系统和处理器模式下,都可以编写相应的汇编语言程序的。但是当前,多数人在使用基于x86处理器的Windows系统,所以从编写运行于此种环境下的程序开始我们的教程。一旦一种汇编语言的基础知识掌握了,我们就会很容易写出在其他运行环境下汇编程序。第一章开始学习之旅编写汇编程序,我们必须具备一些工具,它们是编译器以及编辑器。我们选择了一些能胜任这些工作的运行于Windows系统的工具如下。编译器编译器能把写下的汇编程序代码转换成机器码。通常,它附带有一个连接器。连接器用来连接可编译文件并从中生成可执行文件。Windows系统的可执行文件是以.exe为后缀的。下面给出一些流行的编译器:1 MASM 这个编译器是本教程所选用的,在学习本教程的过程中,你可以使用它。它原先由微软公司开发,现在被包括在MASM32v8程序包内了。MASM32v8程序包还包括了其他的工具。你可以从这个网址得到它:注意:教程中有一些指令和宏指令,只有在MASM编译器才是有效的,所以强烈建议您从开始学习时选用MASM。2. TASM 这是另一个受欢迎的编译器。由Borland公司开发,现在依然是个商业软件,所以你不能免费地获取到它。3. NASM 一个免费开放源码的编译器,它也能在其他系统平台上使用。它可以从这个网址获取到 记住编辑器编辑器是在编译前编写程序代码的软件。编辑器可以个人自由选择。现在在很多种编辑器,你可以试用一下它们并选择一种你喜欢的。1 Notepad 记事本,Windows系统自带的。虽然它缺少很多功能,但它使用简便。2 Visual Studio 它不是免费的编辑器,但它出色的语法高亮显示功能能让你的代码更易于阅读。3. 其他 还有很多其他的编辑器,在些不一一列出它们的名字。其中一些很受欢迎:a. Ultraedit (我个人最喜欢的e) b. Textpad c. VIM http:/www.vim.org/d. Emacs http:/www.gnu.org/software/emacs/emacs.htmle. jEdit http:/www.jedit.org/第二章第一个程序现在我们有了自己的工具,打开你的文本编辑器,跟着下面的介绍,开始学习编程吧。这是世上最普通的程序,“Hello World”程序。控制台程序控制台程序是运行在系统控制台的(也就大家所知的命令行)。为创建这个程序,首先粘贴下面的代码到你的文本编辑器上,并保存为文件“hello.asm”。.386.model flat, stdcalloption casemap :noneinclude masm32includewindows.incinclude masm32includekernel32.incinclude masm32includemasm32.incincludelib masm32libkernel32.libincludelib masm32libmasm32.lib.dataHelloWorld db "Hello World!", 0.codestart:invoke StdOut, addr HelloWorldinvoke ExitProcess, 0end start现在,通过开始菜单,点“运行”选项,输入“cmd”(没有引号)并回车,就能进入到命令行。接着在cmd下转到保存有”hello.asm”的目录,输入"masm32binml /c /Zd /coff hello.asm"。期望编译器不会提示错误,你的程序能被正确编译!然后,我们还得连接它,所以接着输入"masm32binLink /SUBSYSTEM:CONSOLE hello.obj"。祝贺你!你已经成功编译了第一个汇编语言程序。在文件夹里出现了一个中Hello.exe的文件。在命令行下打"hello"来运行你的程序。它会输出"Hello World!"。可见,为了显示"Hello World!",我们只要编写很少的代码就可以了。这些代码都起了什么作用呢?让我们一行一行地看下去。.386这条指令的作用是告知编译器使用.386指令集。当前,几乎没有处理器使用比.386更老的指明令集了。我们还可以选择使用.486.或586,但是.386是兼容性最好的指令集。.model flat, stdcall.MODEL 是一条指定你程序的内存模式的汇编指令。Flat是一种方便的系统程序模式,因为在这种模式下不再区分远指针(far)和近指针(far)。Stdcall 是一种系统函数传递参数的方法,它意味着你得以从右到左的顺序传递你的参数。option casemap :none强制你的程序代码大小写敏感,这意味着Hello和hello被看做不同的。很多高级编程语言同样是大小写敏感的,所以这是个编程的良好习惯。include masm32includewindows.incinclude masm32includekernel32.incinclude masm32includemasm32.inc这是系统程序必需的包含文件。windows.inc通常必须包含的,因为它包含了Win32 API常量和定义的声明。kernel32.inc包含了我们所使用的ExitProcess函数。masm32.inc包含有StdOut函数。StdOut函数不是Win32函数,它是MASM32v8新增进去的。includelib masm32libkernel32.libincludelib masm32libmasm32.lib函数依赖库,基于这个目的,这些库得包含进去。.data程序中所有初始化的数据必须放在这条指令下面。此外,还有别的指令比如.data?和.const。它们分别位于未初始化数据和常量的前面,但是,在我们的”Hello World”程序中并没有用到它们。HelloWorld db "Hello World!", 0db代表“字节”,并声明HelloWorld为一个字符串。"Hello World!"后面跟着一个”NULL”字母,这是因为ANSI字符串必须以NULL结尾。.code这代表程序代码段的开始。start:你程序的代码位于这个标号的后面,但位于” end start”前面。invoke StdOut, addr HelloWorldInvoke调用一个函数及其参数,addr HelloWorld位于它后面。这一行所做的是传递"Hello World!"的地址和调用StdOut。注意StdOut函数只是在MASM32中有效的,它是一个调用其它函数来输出文件的宏。在别的编译器里,你得使用更多的代码并要用到win32函数WriteConsole.。invoke ExitProcess, 0显而易见,它传递参数0到ExitProcess函数,从而退出进程。窗体程序我们也可以编写一个有窗体版本的“Hello World”程序。粘贴下面文本到你的文件编辑器里并保存为文件"hellow.asm".386.model flat, stdcalloption casemap :noneinclude masm32includewindows.incinclude masm32includekernel32.incinclude masm32includeuser32.incincludelib masm32libkernel32.libincludelib masm32libuser32.lib.dataHelloWorld db "Hello World!", 0.codestart:invoke MessageBox, NULL, addr HelloWorld, addr HelloWorld, MB_OKinvoke ExitProcess, 0end start现在,再打开命令行并转到"hellow.asm"所在目录。输入"masm32binml /c /Zd /coff hellow.asm"回车,接着输入"masm32binLink /SUBSYSTEM:WINDOWS hellow.obj"并回车。注意,subsystem是WINDOWS不再是CONSOLE。这个程序弹出一个显示"Hello World!"的信息框。与控制台版相比,窗体版本的代码只有3行是不同的。其中有两行把masm32包含文件和库文件更换为user32包含文件和库文件,这是因为我们现在是使用MessageBox函数,而不是使用StdOut了。第3个不同的行是用MessageBox函数代替了StdOut函数。不同之处就这么多而已!ADDR 与 OFFSET在我们“Hello World!”程序例子中,我们使用了'addr' 来获取字符串"Hello World!"的地址。还有另外一个类似的指令'offset',虽然两者的目的都是在程序执行中获取变是变量的地址。它们主要的区别是'offset' 只能获取全局变量的地址,然而addr能获取全局变以及局部变量的地址。然而我们不讨论局部变量,所以不用担心这种区别。但是我们还是要记住这种区别的。 第三章汇编基础cpu寄存器现在我们已经能够编写并运行一个简单的程序了。让我们转到本教程的核心内容-汇编基本语法吧。你要写出自己的汇编程序,这些基本的知识是要掌握的。 32位通用寄存器有8个。它们其中前面四个也就是eax,ebx,ecx,edx,也能用它们16位或8位的名字形式进行存取。比如,ax存取eax的低16位,al存取低8位,还有ah存取的是9到16位。其余的寄存器也能以类似的方式进行存取。就如大家想象的那样,这些通用寄存器虽然大多有专用的用途,但它们有通用的地方。地址名称描述EAX*累加寄存器进行计算操作和保存数据结果EBX基址寄存器指向数据寄存器中的数据ECX*计数寄存器字符串以及循环的计数EDX*数据寄存器输入/输出的指针ESI源变址寄存器字符串操作中的源指针EDI目的变址寄存器字符串操作中的目的指针ESP堆栈指针寄存器堆栈指针,不能人为使用它EBP堆栈基址寄存器指向堆栈中的数据注意:虽然它们被称为通用寄存器,但是只有那些标有*号的才能在窗体程序编程中使用。下面是6个16位的段寄存器。它们定义在存储器的段。地址名称描述CS代码段寄存器保存要运行的指令DS,ES,FS,GS数据段寄存器数据段SS堆栈段寄存器当前程序的堆栈最后,还有2个32位的没有归类的寄存器地址名称描述EFLAGE标志寄存器状态,控制,系统标志EIP指令指针寄存器下一个要执行的指针的偏移指令集基础x86指令集非常宏大,但是我们通常并没全都使用到了它们。下面介绍一些我们应该掌握的指令。指令描述ADD* reg/memory, reg/memory/constant把两个操作数相加并把结果保存进第一个操作数。如果有进位的话,它会设置CF标志位SUB* reg/memory, reg/memory/constant第一个操作数减去第二个操作数,并把结果保存到第一个操作数里AND* reg/memory, reg/memory/constant两个操作数逻辑与,并把结果存到第一个操作数里OR* reg/memory, reg/memory/constant两个操作数逻辑或,并把结果存到第一个操作数里XOR* reg/memory, reg/memory/constant两者异或,并把结果存到第一个操作数里。注意你不能对两个存储器操作数进行异或操作MUL reg/memory操作数与累加器寄存器相乘,而后把结果存进累加器寄存器DIV reg/memory累加器寄存器被操作数除并把结果存到累加器INC reg/memory操作数的值增1并把结果存进操作数DEC reg/memory操作数的值减1并把结果存进操作数NEG reg/memory操作数的值取补并把结果存进操作数NOT reg/memory操作数的值取反并把结果存进操作数PUSH reg/memory/constant把操作数压进堆栈顶端POP reg/memory弹出堆栈顶端的值并保存到操作数MOV* reg/memory, reg/memory/constant把第二个操作数的值保存到第一个操作数里面CMP* reg/memory, reg/memory/constant第一个操作数减第二个操作数,并设置相应当的标志位。通常与JMP,REP等指令一起使用JMP* label跳转到标号处LEA reg, memory取第二个操作数的地址偏移,并把结果保存进第一个操作数CALL subroutine调用另一个过程直到程序返回RET程序返回到调用者INT constant调用操作数指定的中断*指令不能有两个存储器操作数*这个指令可以结合条件来使用。比如,JNB(不小于),是只有在CF=0这一条件下才会跳转。最新的全部指令集参考可以从下面这个网址得到Push 和 PopPush和pop是操作堆栈的指令。Push获取一个数据并把它压进堆栈的顶端。Pop获取堆栈顶端的数据,弹出并保存它。因此,堆栈是使用一个先进后出的存取方式(LIFO)。堆栈是计算机中一个常见的数据结构,所以如果你在编程过程中对堆栈操作感到不顺手的话我建议你先掌握这一知识。InvokeInvoke是MASM特有的一个伪指令。它使得在调用函数前不必先传递参数。这让我们省略了很多的代码。举个例子说明如下invoke SendMessage, hWnd, WM_CLOSE, 0, 0等效于:push 0push 0push WM_CLOSEpush hWndcall SendMessage程序例子下面是一个完整的程序。它说明了如何去使用指令和寄存器。看看是否全部弄懂了它。.386.model flat, stdcalloption casemap :noneinclude masm32includewindows.incinclude masm32includekernel32.incinclude masm32includemasm32.incincludelib masm32libkernel32.libincludelib masm32libmasm32.lib.dataProgramText db "Hello World!", 0BadText db "Error: Sum is incorrect value", 0GoodText db "Excellent! Sum is 6", 0Sum sdword 0.codestart:; eaxmov ecx, 6 ; set the counter to 6 ?xor eax, eax ; set eax to 0 0_label: add eax, ecx ; add the numbers ?dec ecx ; from 0 to 6 ?jnz _label ; 21mov edx, 7 ; 21mul edx ; multiply by 7 147push eax ; pushes eax into the stackpop Sum ; pops eax and places it in Sumcmp Sum, 147 ; compares Sum to 147jz _good ; if they are equal, go to _good_bad: invoke StdOut, addr BadTextjmp _quit_good: invoke StdOut, addr GoodText_quit: invoke ExitProcess, 0end start注意:“;”符号表示注释。所有跟在它后面的字符都不会被编译。把提示和注意点放在注释中是个好主意,它能让你的代码易读。第四章IV. 窗体程序基础窗体程序通常由一个或几个窗体组成。因此,做为windows程序员至少要懂得怎么创建一个简单的窗体。很不幸,它不是那么容易的事,但是本教程会指导你怎么去做。预备知识在编写窗体程序前我们还要讨论几个主题。让我们花点时间复习一下预备知识。宏MASM有几个让汇编编程变得非常容易的宏。我们已经接触到invoke,它简单地调用一个函数。下面列出其他几种,之前在你用高级语言编程时它们的用法是很明显的。o .if, .else, .endifo .while, .break, .endw过程与高级语言类似,MASM让你定义各种过程使得你的代码易于阅读。它们的格式如下所示:<name> proc <var1>:<var1 type>, <var2>:<var2 type>, .<function code>ret<name> endp返回值保存在eax寄存器里,这个过程用下面格式来调用invoke <name>, param1, param2, .返回值可以用下面指令来获取mov RetVal, eax变量变量被分配在内存中的,并用来存储你的数据。在没有足够的寄存器可用的情况下,变量是非常有用的。变量有两种类型全局变量和局部变量。如果全局变量已被初始化,它们被置于.data块;如果它们没被初始化的话,就被置于.data?块。还有,如果全局变量被初始化并且不会被改变的话,它们就被置于.const块。声明全局变量的格式如下:<name> <type> <value, or ? if uninitialized>局部变量是被放于过程内部的,暂时保存,供过程内部使用。它们在创建时不能被初始化。格式如下:local <name>:<type>有几种变量类型以后将会遇到。其中有几种常见,比如byte,word (4 bytes),dword(8 bytes)。还有更多,但是它们常常与这三种类型中的一种是相同的,只不过名称不同。一个简单的窗体程序窗体程序有两个主要的部分。第一部分是WinMain,它创建窗体还包含叫实现消息循环的代码。“消息循环”监视消息并分派消息。第二部分是过程返回WndProc,它是接收消息的,这部分处理你的鼠标事件及刷新窗口等。.386.model flat, stdcalloption casemap :noneinclude masm32includewindows.incinclude masm32includeuser32.incinclude masm32includekernel32.incincludelib masm32libuser32.libincludelib masm32libkernel32.lib以上是我们通常必需的WinMain proto :DWORD, :DWORD, :DWORD, :DWORDThis is a function prototype. It let's us call the WinMain function later in the program.It can be compared to a C/C+ function declaration.这是函数原型。在稍后的程序里我们称它为WinMain函数。.dataClassName db "WinClass", 0AppName db "Simple Window", 0我们声明我们的字符变量.data?hInstance HINSTANCE ?变量hInstance保存模块实例的句柄,以便与窗体相关联。稍后我们将把它传递到CreateWindow函数中。.codestart:invoke GetModuleHandle, NULLmov hInstance, eaxinvoke WinMain, hInstance, NULL, NULL, 0invoke ExitProcess, eax获取模块句柄并把它保存到变量hInstance中。接着调用WinMain函,而已退出。WinMain是这个程序的核心,所以我们将深入研究它。注意:从这点来看,我们假设你能够从MSDN中查找windows函数。它有函数参数,返回值,还有其它你必须了解的信息。你可以在附加资源这个章节里获取关于MSDN的信息。WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR,CmdShow:DWORDlocal wc:WNDCLASSEXlocal msg:MSGlocal hwnd:HWND这是WinMain函数的开头部分。我们声明了三个局部变量:wc,msg,,还有hwnd。Wc保存我们创建的窗口类,窗体类是一个创建窗体的模板。Msg保存消息循环中返的消息。Hwnd保存我们窗本的句柄。mov wc.cbSize, SIZEOF WNDCLASSEXmov wc.style, CS_HREDRAW or CS_VREDRAWmov wc.lpfnWndProc, offset WndProcmov wc.cbClsExtra, NULLmov wc.cbWndExtra, NULL注意:在窗体程序中,or操作运算符常常用来联合参数中的标志。push hInstancepop wc.hInstancemov wc.hbrBackground, COLOR_WINDOW+1mov wc.lpszMenuName, NULLmov wc.lpszClassName, offset ClassNameinvoke LoadIcon, NULL, IDI_APPLICATIONmov wc.hIcon, eaxmov wc.hIconSm, eaxinvoke LoadCursor, NULL, IDC_ARROWmov wc.hCursor, eaxinvoke RegisterClassEx, addr wc这些是填充我们先前声明的wc结构。然后以wc为参数调用RegisterClassEx。至于更多关于wc的每个成员的信息,请在MSDN中查找WNDCLASSEX结构的资料。 invoke CreateWindowEx, 0, addr ClassName, addr AppName, WS_OVERLAPPEDWINDOWor WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,NULL, hInst, NULLmov hwnd, eax调用CreateWindowEx函数创建窗体。其中有很多参数被传递进去来表明怎么创建窗体。窗体的句柄会返回并保存到变量hwnd中。.while TRUEinvoke GetMessage, addr msg, NULL, 0, 0.break .if (!eax)invoke TranslateMessage, addr msginvoke DispatchMessage, addr msg.endw这个while循环也就是先前提到的消息循环。当一个输入事件发生,系统会传递这个事件到一个消息里,并把消息放进程序的消息队列中去。GetMessage取回这些消息并保存进变量msg里。TranslateMessage把键盘消息转换成字符消息。最后,DispatchMessage把这些消息发送到WndProc函数里。在WndProc函数中,这些消息将会被处理。mov eax, msg.wParamretWinMain endp返回值保存进msg.wParam,WinMain函数结束。WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM.if uMsg = WM_DESTROYinvoke PostQuitMessage, 0.elseinvoke DefWindowProc, hWnd, uMsg, wParam, lParamret.endifxor eax, eaxretWndProc endpWndProc函数是处理消息的地方。唯一一个一定要处理的消息是WM_DESTROY,它通过调用PostQuitMessage来退出程序。如果有其它你要处理的事件,你可以在这里把它们加进来。普通要处理的消息是WM_CREATE(当创建窗体时),WM_PAINT (当窗体必须重画时), 还有WM_CLOSE (关闭窗体时)。其它没有处理的消息被传递给DefWindowProc函数对消息进行默认处理end start就这些了。你已经了解了怎么去创建一个窗体!第五章IV. 深入汇编和系统下面有一些资料来扩展你关于汇编以及系统编程方面的知识:字符串操作,文件处理,还有系统窗体的控制。字符串操作字符串,数组都是程序中的基本部分。如果你想显示文本或者请求使用者输入,它们就用得了。它们使用到了如下的寄存器:esi,edi,ecx,eflag中的方向控制标志。方向控制标志是指定移动字符串时的方向。一些常见的字符串操作指令是movsb, cmpsb, stasb, and stosb.为了操作字符串,你可以在字符串控制指令中使用某些rep?的形式。下面是在串操作指令中可以使用到rep?前缀前缀串操作指令描述repmovsb复制字符串repecmpsb比较字符串repnescasb扫描字符串中一个字符repstosb保存一个字符到字符串中下面给个复制字符串的例子cld ; sets the direction flag to forwardmov esi, source ; move the source address in to esimov edi, dest ; move the destination address in to edimov ecx, length ; move the length to copy in to ecxrep movsb ; copy length bytes from esi to edi文件管理在旧的DOS系统世界时,文件是通过使用中断进行操作的。在windows系统里,我们通过使用系统函数访问文件。其中可供我们使用的4种函数是:CreateFile 创建或打开一个文件,并返回它的句柄ReadFile 从文件中读取数据WriteFile 写数据到文件里CloseHandle 关闭你用CreateFile函数得到的句柄存储为了读取文件内容,我们必须分配一些内存来存储数据。内存可以如你所愿地被分配,锁定,但是最后记得解锁和释放。做这些工作的函数是GlobalAlloc, GlobalLock, GlobalUnlock, 还有GlobalFree。相当容易,呵呵!程序例子这个程序读取"c:test.txt"的内

    注意事项

    本文(Windows 汇编语言编程教程.doc)为本站会员(asd****56)主动上传,淘文阁 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知淘文阁 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于淘文阁 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号 © 2020-2023 www.taowenge.com 淘文阁 

    收起
    展开