首页
社区
课程
招聘
[原创]Win32Asm 驱动学习笔记 1-2 章
发表于: 2013-7-20 12:19 13923

[原创]Win32Asm 驱动学习笔记 1-2 章

2013-7-20 12:19
13923
Win32Asm 驱动学习笔记   目录

     版权声明

         本文所述内容很多来自互联网以及其他一些参考资料,并不是全部原创,属于学习笔记汇总,凡因此文引发的版权问题,作者本人不负任何责任。


自序

      虽我未学,下笔无文,然驱动入门之路何其曲折,何妨我将其倾注于笔墨文字,因其内容多引用经典,作者心得体会穿插其间,故不敢擅称教程,借以笔记之说,攒此文字,以谓后来者。
      另外,作者不才,内容错漏之处还请海涵指正,谢谢。

目录

第一章 驱动程序基础
     1.1 Windows NT内核结构概述
              1.1.1 主要组成部分
               1.2.2  内核模式和用户模式      
     1.2 设备驱动程序简介
              1.2.1 驱动程序的分类
              1.2.2 分层的和单层的设备驱动程序
     1.3  线程上下文   
     1.4 中断请求级别
      1.5 系统崩溃
      1.6  驱动程序的调试
      1.7    其他参考资料
第二章 驱动开发环境的搭建
      1.1 W32ASM简介
      1.2 关于编译器
      1.3关于DDK
      1.4. 关于IDE
第三章 调试基础
   3.1 应用层调试输出
   3.1.1 Debug宏简介
   3.1.2  Debug宏的使用
   3.2  驱动层调试输出
   3.3 本地内核调试基础

第四章 最简单的驱动程序
    4.1 关于NT式驱动
    4.2 驱动的头文件
    4.3 驱动入口
     4.4Hello world
     4.5 驱动程序的运行
     4.6 简单的例子
          4.6.1  Beeper源代码
          4.6.2 控制系统定时器

第五章 驱动程序的安装与卸载

5.1  Windows服务
5.2  服务控制管理器(SCM)
5.3  服务控制程序(SCP)
     5.3.1 建立到SCM的连接
     5.3.2 安装一个新的驱动
     5.3.3 打开已存在的驱动
     5.3.4  停止已存在的驱动
     5.3.5 启动驱动程序
     5.3.6 卸载驱动
5.4 通用模板
     5.4.1 模板的建立
     5.4.2 模板的导入

第六章驱动程序的通讯
      5.1 I/O管理器简介
      5.2 传输类型及缓冲策略
      5.3 读取BIOS的例子
  第七章 HOOK SSDT
        6.1  关于SSDT
        6.2  获取服务号
        6.3  编写一个简单的SSDT 框架
        6.4 保护进程的例子
    ......

Win32Asm 驱动学习笔记

     第一章 驱动程序基础
[LEFT] 本章目录:
1.1 Windows NT结构概述
    1.1.1Windows NT结构的主要组成部分
       1.1.2 内核模式和用户模式
1.2 Windows NT的设备驱动程序
       1.2.1 设备驱动程序的分类
       1.2.2 分层的和单层的设备驱动程序
1.3 线程上下文(Thread Context
1.4 中断请求级别
1.5 系统崩溃
1.6 驱动程序的调试
1.7 其他参考资料

正文开始:

        在学习驱动前,我们有必要对Windows NT的结构组成做一个基本的概述,罗云彬和刘松翻译的的KmdTut中文版里面的开篇就讲到了,讲的很详细,我摘录过来形成单独的一章,望各位看官悉之。

1.1 Windows NT结构概述

1.1.1Windows NT结构的主要组成部分

     根据地址空间、代码权限和职责的不同,Windows NT内部划分为两个截然不同的部分。
   ,整个32位系统的4GB内容被划分为两个相等的部分,用户模式(user-mode)的进程使用的地址空间被映射到低位的2GB上(地址范围00000000 - 7FFFFFFFh),而高位的2GB(地址范围80000000h - 0FFFFFFFFh)则供操作系统的组成部分来使用,如设备驱动程序、系统内存池、系统使用的数据结构等,在这部分中,内存共享的权限和职责等方面就要复杂一点了。

    下面就是用户模式进程的一些简单分类:
系统支持进程--如Logon进程(位于\%SystemRoot%\System32\Winlogon.exe
服务进程--如Spooler进程(位于\%SystemRoot%\System32\spoolsv.exe
用户应用程序--任何Win32、Windows 3.1、DOS、POSIX或者OS/2程序
子系统--Windows内置3个子系统:Win32(位于\%SystemRoot%\System32\Csrss.exe)、POSIX子系统(位于\%SystemRoot%\System32\Psxss.exe)和OS/2子系统(位于\%SystemRoot%\System32\Os2ss.exe),在Windows XP以及后续的操作系统中,POSIX和OS/2子系统已经被去掉了。

    而下面是内核模式的一些模块:
运行模块--内存管理、进程和线程的管理、安全机制等
内核--线程调度、中断、异常的分派等(运行模块和内核位于\%SystemRoot%\System32\Ntoskrnl.exe
设备驱动程序--硬件设备驱动程序、文件系统和网络驱动程序
硬件抽象层(Hardware Abstraction Layer, HAL)--将内核、设备驱动程序和运行模块和具体的硬件平台隔离开(位于\%SystemRoot%\System32\Hal.dll
窗口和图形系统--实现GUI函数,如处理窗口、用户界面的控制和绘画等(位于\%SystemRoot%\System32\Win32k.sys


图1.1 Windows NT结构简图

1.1.2 内核模式和用户模式

    Intel x86体系结构的处理器定义了4个级别的权限(称为Ring),Windows系统使用了Ring0(供特权模式使用)和Ring3(供用户模式使用),Windows系统只使用了2个级别的权限级别的原因是为了和其他一些硬件系统兼容,这些硬件系统只有2个级别的权限,如Compaq Alpha和Silicon Graphics MIPS等。
    每个用户模式的进程有其私有的地址空间,这些进程在最低的权限级别下运行(称为Ring3或者用户模式),它们不允许执行CPU的特权指令,对系统所属的数据、地址空间以及硬件等的访问也是被严格限制的,例如,如果某个用户程序访问4G地址空间中的高位2G,那么系统就会立即将其终止执行。要注意的是,进程调用系统功能的时候,可以切换到内核模式执行,但是调用结束后,就返回到用户模式了。
    用户模式的进程总是被认为是对操作系统稳定性的潜在威胁,所以它们的权限被严格地限制,任何触及这些限制的举动都将使进程被终止。
    而内核模式的组件则可以共享这些受保护的内核模式内存空间,在特权级别下运行(也称为Ring0),允许执行任何CPU指令,包括特权指令,可以无限制地访问系统数据、代码和硬件资源。
    内核模式代码运行在系统地址空间中,并总是被认为是可信任的,一旦被装载运行后,驱动程序就是系统的一部分,可以无限制地做任何事情。
    总的来说,用户模式程序被完全从操作系统隔离,这对操作系统的完整性来说是件好事情,但对某些种类的应用程序来说就太头痛了,比如Debug工 具。幸运地是,这些在用户模式几乎不可能完成的任务完全可以通过内核模式的驱动程序来完成,因为这些驱动程序的操作是不受限制的。因此,如果你打算从用户 模式存取操作系统内部的数据结构或者函数的话,唯一的方法就是将一个内核模式驱动程序装载到系统的地址空间中(并调用它),这是很简单的事情,操作系统完 全支持这样的操作。

1.2 Windows NT的设备驱动程序

1.2.1 设备驱动程序的分类

    Windows NT支持的设备驱动程序的范围很广,它们的分类如下:

    用户模式的驱动程序:
虚拟设备驱动程序(Virtual Device Drivers/VDD)--用户模式的组件,用于为16位的MS-DOS应用程序提供虚拟的执行环境,虽然和Windows 95/98里面的VxD从功能上看起来是差不多的,但实际上两者根本不同。
打印驱动程序--将与设备无关的图形转换到和打印机相关的指令

    内核模式驱动程序:
文件系统驱动程序--实现标准的文件系统模型
传统设备驱动程序--用于在没有其他驱动程序帮助的情况下控制硬件设备,它们是为老版本的Windows NT系统所写的,但是也可以不加修改地运行在Windows 2000/XP/2003系统上
视频驱动程序--不用多介绍了吧?
流驱动程序--支持多媒体设备,如声卡
◎ WDM驱动程序--即Windows Driver Model,WDM包括对Windows NT电源管理和即插即用的支持,WDM可以在Windows 2000、Windows 98和Windows ME下实现,所以在这些操作系统下,WDM驱动程序在源代码级别是兼容的,在有些情况下,在二进制代码级别上也是兼容的

    在不同的资料中,对驱动程序的分类方法可能完全不同,但这并不是问题。
    从名称理解,设备驱动程序是用于控制某个设备的,但这个"设备"并不一定指的是物理上存在的设备,它也可以是虚拟设备。
    从文件结构上讲,设备驱动程序就是一个普普通通的PE格式文件,就像其他EXE或者DLL文件一样。设备驱动程序是一个可装载的内核模式模块,一般以SYS为扩展名。他们之间的不同点在于两种的装载方法是完全不同的。实际上,我们可以把设备驱动程序理解成一个内核模式的DLL,用于完成在用户模式下所不能完成的功能,本质上的不同就在于我们无法直接存取设备驱动程序的代码和数据(注:DLL的代码和数据是可以被直接存取的,这方面的资料可以参考《Windows环境下32位汇编语言程序设计一书》中的DLL一章),唯一的存取方式是通过I/O管理器,它提供了简单的驱动程序管理环境。
    刚开始学习内核开发的时候,你可能感觉自己根本就是一个菜虫(旁白:就是比菜鸟还低级,呵呵~~~),因为你以前用Windows API开发程序的经验在这里根本帮不上忙,即使你以前写过n多个(n趋向无穷大……)用户模式下的应用系统也没用。内核提供了完全不同的函数和数据结构,以至于你要从头开始了解,而且资料奇缺无比,一般情况下,可供参考的只有头文件。

1.2.2 分层的和单层的设备驱动程序

    大部分控制硬件设备的驱动程序是分层的驱动程序,分层驱动的概念就是当用户模式发出一个请求时,每个请求从高层次的驱动程序逐层处理并流传到低层次的驱动程序中,一个I/O请求的处理可能分步在多个驱动程序中,例如,如果一个应用程序发出读盘请求,处理请求会在多个驱动程序中流过,在其中你也可以再加入n多个过滤驱动程序(比如插入一个加解密的模块)。
    单层的驱动程序是最简单的一类驱动程序,这一类驱动程序通常并不依赖于其他已装载的驱动程序,他们的接口仅仅针对用户模式的应用程序,开发和调试这一类驱动程序是非常简单的,我们即将开始讨论的就是这类程序,其他类型的驱动程序将在以后讨论。

1.3 线程上下文(Thread Context

    在大多数情况下,我们的系统中只安装了一个CPU,所以,对于所有这些运行中的程序来说,操作系统对每个进程中的线程所使用的CPU时间进行调度,循环为每个线程分配时间片,这就造成了多个程序同时执行的假象。如果系统中安装了多个CPU,那么操作系统的调度算法将复杂得多,因为它要将各CPU上的线程进行平衡。如果Windows检测到一个新线程要开始运行了,它将进行一次上下文切换(context switch)(注:上下文(Content)实际上就是线程运行的环境,也就是运行时各寄存器和其他东东的状态,更自然的理解就是"线程状态")。所谓上下文切换就是保存线程运行时的机器状态,然后将另一个线程的状态恢复并重新开始执行。如果重新开始执行的线程属于另一个进程,那么该进程的地址空间也将被同时切换过来(通过在CR3寄存器中装入页表)。
    每个用户进程都有私有的地址空间,所以他们的页表都是不同的,CPU通过切换页表来将虚拟地址映射到物理地址,设备驱动程序并不需要直接做这些工作。上下文切换比较耗CPU时间,所以驱动程序一般不创建它们自己的线程,它们一般在下列环境中的一个中运行:

1. 在发起I/O请求的用户线程中运行
2. 在内核模式下的系统线程中运行
3. 作为中断运行(并不处于哪个特定的进程或线程中,因为它们都被暂时挂起了)

    在处理I/O请求包(IRPs)时,我们总是运行在和用户模式的调用者相同的进程上下文中运行,这样我们就能对用户程序的地址空间进行寻址。但是当驱动程序被加载或者卸载的时候,我们将在系统进程中运行,这时存取的只能是系统的地址空间。

1.4 中断请求级别

    中断是任何操作系统都少不了的组成部分,中断使处理器打断正常的程序流程来首先处理它们,中断分硬件中断和软件中断两种,中断是分优先级的,一个高优先级的中断可以打断低优先级的中断的执行。
    Windows中把中断优先级称为IRQLs(interrupt request levels),在系统中表示为从0(被动)到31(高级)的整数,其中大的数值对应高优先级的中断。注意IRQL值的含义和线程调度优先级的含义是完全两码事情。
    严格来说,IRQL=0的中断并不是中断,因为它无法打断任何其他代码的执行(因为没有比0更低级的代码了),所有的用户模式线程在这个级别上运行,该级别也称为被动级别(passive level)。我们后面要讨论的驱动程序代码也在这个级别上运行,注意这并不意味着其他的驱动程序也在被动级别下运行。
    因此这里还有两个重要的结论:
    首先:当驱动程序运行于用户模式程序的线程中时,代码的执行可能被高IRQL级别的代码打断,一些函数可以用来获取当前的IRQL值,并可以对其进行提升或者降低。
    第二:被动模式IRQL下的代码可以调用任何的内核函数(DDK指明了每个函数允许调用的IRQL级别),可以对已分页的或未分页的内存进行寻址(注:即已映射过的虚拟地址还是物理内存地址)。反过来,当在一个比较高的IRQL级别下对分页内存进行寻址时(指等于或高于DISPATCH_LEVEL),系统将崩溃,因为这时内存管理器的IRQL级别反而比较低,以至于无法处理页错误了。

1.5 系统崩溃

    我想每个人都见过著名的蓝屏死机画面,即"Blue Screen Of Death",简称为BSOD,也许根本不需要解释它是怎么出现或者在什么时候出现的,因为在后面的KMD开发过程中,你会很频繁地遇到它们。
    在内核模式下,Windows不对任何系统内存进行保护,由于内核模式的驱动程序可以对系统内存和操作系统的地址空间进行任意存取,所以你必须对你开发的驱动程序进行严格的测试,以防它危及到系统的稳定。
    你可以把这个作为最基本的原则,另外,如果没有线程上下文、中断优先级、内核模式和用户模式等方面的概念,开发内核模式驱动程序将是不可能的事

1.6 驱动程序的调试

    调试内核模式的代码需要合适的调试器,Compuware的SoftIce是个不错的选择(见 http://www.compuware.com/products/numega/index.htm),当然你也可以使用Microsoft Kernel Debugger,它需要两台计算机:主机和目标机器,目标机器是被调试的机器,主机是运行调试软件的机器。Mark Russinovich ( http://www.sysinternals.com/ ) 也写了一个工具,叫做LiveKd,它允许在单台机器上运行Microsoft Kernel Debugger,而不再需要两台机器了。

1.7 其他参考资料

1. David Solomon, Mark Russinovich, "Inside Microsoft Windows 2000. Third Edition", Microsoft Press, 2000
2. Though there is no source code in this book at all, it's the number one book for the device driver programmers.
3. Sven B. Schreiber, "Undocumented Windows 2000 Secrets. A Programming Cookbook", Addison-Wesley
4. The especially practical book, it has many Windows 2000 secrets revealed.
5. Walter Oney, "Programming the Microsoft Driver Model", Microsoft Press, 1999
6. Walter Oney, "Programming the Microsoft Windows Driver Model. 2nd edition", Microsoft Press, 2003(这是一本很好的书,它强调的是即插即用驱动程序的开发,但这并不降低了它的重要性,因为驱动开发方面的基本理论都是通用的)
7. Art Baker, Jerry Lozano, "The Windows 2000 Device Driver Book, A Guide for Programmers, Second Edition", Prentice Hall, 2000,这也是一本好书,选题范围和本教程类似
8. Rajeev Nagar, "Windows NT File System Internals. A Developer's Guide", O'Reilly
9. Prasad Dabak, Sandeep Phadke, and Milind Borate, "Undocumented Windows NT", M&T Books, 1999 ,这本书里面包含了n多公开的文档中找不到的东西。
10. Gary Nebbett, " Windows NT-2000 Native API Reference", MacMillan Technical Publishing, 2000,这里也有一堆公开的文档中找不到的函数和数据结构的说明
11. Jeffrey Richter, "Programming Applications for Microsoft Windows. Fourth Edition", Microsoft Press, 1999,这本书和开发设备驱动程序没什么关系,但是也是一本很有趣的书。

    这里列出的并不是全部,但这些似乎都有点儿看看的必要性。

注:本章的内容均来自于上文提到的KmdTut中文版,它是由由罗云彬和刘松翻译的。




第二章  驱动开发环境的搭建

[LEFT]2.1 Win32Asm简介

[/LEFT]
     WIN32程序运行在保护模式下的,所有的 WIN32 的应用程序运行在一个连续、平坦、巨大的 4GB 的空间中。
这同时也意味着您无须和段寄存器打交道,您可以用任意的段寄存器寻址任意的地址空间,这对于程序员来说是非常方便的,这也使得用32位汇编语言和用C语言一样方便。
2.2 Win32ASM编译器

     Win32ASM的编译器最常用的有两种:Borland公司的Tasm和Microsoft的Masm,两种编译器各有自己的优缺点,看来使用哪一种编译器还是比较难选择的,但CCDebuger给了我们一个答案,他为RadAsm(下面有介绍)整合了供MASM使用的很全的SDK、DDK库,这些库、include文件、学习资料和其他工具还有Masm9.0版本整合形成了一个非常方便的版本:RadAsm 汉化整合版。这样一来,我们用汇编编程就象用VC一样方便了。
    因为有了RadAsm 汉化整合版,所以就我个人而言,我推荐使用Masm作为Win32ASM的编译工具,但Masm和Tasm的宏语法有很多的不同,我的这个是以Masm格式写的。

2.3关于DDK

   开发驱动程序一个必备的东西就是DDK(设备驱动开发包),它跟我们在应用层常听到的SDK差不多,只不过一个是应用层的,一个是内核层的。
     RadAsm 汉化整合版里整合的实际上是被称为KmdKit的DDK
KmdKit的全称是Kernel Mode Driver development Kit for assembly language programmers,即内核模式驱动程序汇编开发包,KmdKit中包括了用宏汇编编译器开发驱动程序需要用到的所有东西。     

     你可以从http://www.wasm.ru/或者http://www.freewebs.com/four-f/下载到最新版本的KmdKit
     网上流传了一份KmdKit的使用教程KmdTut中文版,它是由由罗云彬和刘松翻译的,大家可以百度一下,我以后的文章中也可能会多次引用里面的内容,这里说明一下。
   
2.4 关于IDE

         Win32ASM常用的IDE有Radasm,MASMPLUS,WINASM,EASYCode等,前面我们已经说了,RadAsm 汉化整合版整合了我们驱动开发所需要的所有东西,非常方便,这里我们就使用它来进行代码编写工作,其它的IDE读者可以自行搜索了解。
RadAsm是一个非常好用的IDE,它不仅支持代码高亮,输入提示,还支持编译其它语言,如C/C++等,使用也非常简单,全中文的,基本摸索下就会使用了,它像使用VC一样方便,我这里就不啰嗦了,可以百度一下或者在这里下载:
                http://www.onlinedown.net/soft/26097.htm
    注意: RadAsm汉化增强版适合于未安装Masm32及Viusual C++的用户,安装路径必须是分区根目录,最好不要在系统盘,RadAsm的使用可以参考这篇文章:Win32ASM可视化编程

本章小结

    搭建W32ASM的驱动开发环境,只需要一个步骤:下载安装RadAsm汉化增强版

  
   
[/LEFT]

[课程]Linux pwn 探索篇!

上传的附件:
收藏
免费 5
支持
分享
最新回复 (21)
雪    币: 304
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
前排沙发,果断站位!!!
2013-7-20 12:44
0
雪    币: 93908
活跃值: (200199)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
3
+1
2013-7-20 13:16
0
雪    币: 750
活跃值: (228)
能力值: ( LV9,RANK:780 )
在线值:
发帖
回帖
粉丝
4
占位编辑
2013-7-20 14:33
0
雪    币: 3343
活跃值: (1243)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
5
好东西,学习了,刚刚收藏
2013-7-20 15:00
0
雪    币: 1042
活跃值: (470)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
前排学习nohacks好文
2013-7-20 16:20
0
雪    币: 6366
活跃值: (4336)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
7
感谢分享,来学习了
2013-7-20 16:41
0
雪    币: 259
活跃值: (279)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
太感动了。。。终于有了
2013-7-20 19:01
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
期待楼主精彩文章 支持
2013-7-20 19:58
0
雪    币: 680
活跃值: (68)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
10
期待撸主后续内容
2013-7-20 19:59
0
雪    币: 224
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
支持楼主,楼主你一定要坚持下去啊
2013-7-21 00:54
0
雪    币: 209
活跃值: (778)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
用C不比用Win32Asm直观简单明了么?
稍微大型的程序用Win32Asm有多大实际意义?
在驱动中只在必要的时候内联汇编就行了!
2013-7-21 02:02
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
为了更加知其所以然
2013-7-21 09:56
0
雪    币: 108
活跃值: (44)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
14
太感谢了!!
2013-7-21 10:36
0
雪    币: 209
活跃值: (778)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
这样只会更加不知其所以然!
2013-7-21 18:59
0
雪    币: 750
活跃值: (228)
能力值: ( LV9,RANK:780 )
在线值:
发帖
回帖
粉丝
16
抱歉,我没有学过C,但能看懂C的大概吧

WinAsm32 不像以前的8086汇编,使用宏,语法和C很接近

除了不能直接赋值,其他基本都可以用宏实现,已经很方便了,借助RadAsm这个IDE,和用VC写C没两样了,我感觉用来写后台代码还是很理想的
2013-7-21 21:45
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
好吧,为了看调试器的反汇编代码能像看C/C++代码一样流畅
2013-7-21 23:15
0
雪    币: 209
活跃值: (778)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
一般的反汇编代码中能有几句伪指令?
2013-7-22 10:41
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
既然用asm干嘛用那么多伪指令,

我向来用最接近所见即所得的方式写,

基本也就写一些小程序,

用太多的伪指令那还不如用C/C++呢
2013-7-22 11:09
0
雪    币: 209
活跃值: (778)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
看来你没弄明白我的意思...
我的意思就是说Win32Asm有太多的伪指令了,还不如用C,内联汇编当然全部都是纯粹的汇编指令!
2013-7-22 14:06
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
我明白您的意思,您的意思就是asm不适用于开发实战,这个我没否定的意思,

而我的意思是说,在有了一定的C/C++基础以后,也不能忽略了asm代码的练习,这样无论是对逆向,反汇

编的阅读都很有帮助,比单纯的写程序看反汇编更有效,哪种经常阅读反汇编,就能提高逆向功力的说法,

我持极大怀疑态度!
2013-7-22 15:01
0
雪    币: 9
活跃值: (165)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
22
学习了,谢谢分享
2018-1-17 11:32
0
游客
登录 | 注册 方可回帖
返回
//