本文旨在自己动手实现一个类似于“按键精灵”的桌面软件。第一部分介绍了简单的模拟方式,但是有些软件能够屏蔽掉这种简单模拟带来的效果,因此第二部分将介绍如何从驱动级层面进行模拟。

“游戏外挂一般分为三个级别:初级是鼠标、键盘模拟,中级是Call游戏内部函数,读写内存,高级是抓包,封包的“脱机挂”(完全模拟客户端网络数据,不用运行游戏)。用C#写外挂的不是很多,大部分是C++,主要原因是MS的C#目前不支持内联汇编功能。因此用C++写底层库,然后用C#调用成为DONET爱好者开发外挂的首选。”——某开发者言

简单的模拟方式

模拟鼠标操作

Windows API介绍

.NET没有提供改变鼠标指针位置、模拟点击操作的函数,但是可以通过调用Windows API函数实现。

[DllImport("user32.dll")]
static extern bool SetCursorPos(int X,int Y);

该函数用于设置鼠标的位置,其中X和Y是相对于屏幕左上角的绝对位置.

[DllImport("user32.dll")]
static extern void mouse_event(MouseEventFlag flags,int dx,int dy,uint data,UIntPtr extraInfo);

该函数不仅可以设置鼠标指针绝对位置,而且可以以相对坐标来设置位置.

其中flags标志位集,指定点击按钮和鼠标动作的多种情况.dx指鼠标沿x轴绝对位置或上次鼠标事件位置产生以来移动的数量.dy指沿y轴的绝对位置或从上次鼠标事件以来移动的数量.data如果flags为MOUSE_WHEEL则该值指鼠标轮移动的数量(否则为0),正值向前转动.extraInfo指定与鼠标事件相关的附加32位值.

[DllImport("user32.dll")]
static extern IntPtr FindWindow(string strClass, string strWindow);

该函数根据类名和窗口名来得到窗口句柄,但是这个函数不能查找子窗口,也不区分大小写.如果要从一个窗口的子窗口查找需要使用FIndWindowEX函数.

[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string strClass, string strWindow);

该函数获取一个窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配,该函数查找子窗口时从排在给定的子窗口后面的下一个子窗口开始。其中参数hwnParent为要查找子窗口的父窗口句柄,若该值为NULL则函数以桌面窗口为父窗口,查找桌面窗口的所有子窗口。 hwndChildAfter子窗口句柄,查找从在Z序中的下一个子窗口开始,子窗口必须为hwnParent直接子窗口而非后代窗口,若hwnChildAfter为NULL,查找从父窗口的第一个子窗口开始。 strClass指向一个指定类名的空结束字符串或一个标识类名字符串的成员的指针。 strWindow指向一个指定窗口名(窗口标题)的空结束字符串.若为NULL则所有窗体全匹配。返回值:如果函数成功,返回值为具有指定类名和窗口名的窗口句柄,如果函数失败,返回值为NULL。

实现步骤

1-引入命名空间using System.Runtime.InteropServices; 因为要使用user32.dll中的接口。
2-创建一个新类MouseFlag
3-声明函数 
- 此处的位置都是屏幕绝对位置 
- 设置鼠标位置:public static extern int SetCursorPos(int x, int y); 
- 鼠标事件:static extern void mouse_event(MouseEventFlag flags, int dx, int dy, uint data, UIntPtr extraInfo);虽然这个已经弃用了,但是上手简单。这里需要一个类型:MouseEventFlag
4-创建MouseEventFlag :enum MouseEventFlag : uint

5-用类包起来

实现实例

(一个控制台程序)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;namespace Mouse
{class Program{public class MouseFlag{enum MouseEventFlag : uint{Move = 0x0001,LeftDown = 0x0002,LeftUp = 0x0004,RightDown = 0x0008,RightUp = 0x0010,MiddleDown = 0x0020,MiddleUp = 0x0040,XDown = 0x0080,XUp = 0x0100,Wheel = 0x0800,VirtualDesk = 0x4000,Absolute = 0x8000}[DllImport("user32.dll")]static extern void mouse_event(MouseEventFlag flags, int dx, int dy, uint data, UIntPtr extraInfo);[DllImport("user32.dll")]public static extern int SetCursorPos(int x, int y);public static void MouseLeftClickEvent(int dx, int dy, uint data){SetCursorPos(dx, dy);System.Threading.Thread.Sleep(2 * 1000);mouse_event(MouseEventFlag.LeftDown, dx, dy, data, UIntPtr.Zero);mouse_event(MouseEventFlag.LeftUp, dx, dy, data, UIntPtr.Zero);}public static void MouseRightClickEvent(int dx, int dy, uint data){SetCursorPos(dx, dy);System.Threading.Thread.Sleep(2 * 1000);mouse_event(MouseEventFlag.RightDown, dx, dy, data, UIntPtr.Zero);mouse_event(MouseEventFlag.RightUp, dx, dy, data, UIntPtr.Zero);}}static void Main(string[] args){            System.Threading.Thread.Sleep(6 * 1000);MouseFlag.MouseLeftClickEvent(10, 1000, 0);}}
}

模拟键盘操作

Windows API介绍

        /// <param name="bVk" >按键的虚拟键值</param>/// <param name= "bScan" >扫描码,一般不用设置,用0代替就行</param>/// <param name= "dwFlags" >选项标志:0:表示按下,2:表示松开</param>/// <param name= "dwExtraInfo">一般设置为0</param>[DllImport("user32.dll")]public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);

实现步骤

可类比“模拟鼠标操作”的实现步骤,直接学习以下实例吧。

实现实例

(一个控制台程序)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;namespace VKB
{class Program{public class KeyBoard{public const byte vKeyLButton = 0x1;    // 鼠标左键public const byte vKeyRButton = 0x2;    // 鼠标右键public const byte vKeyCancel = 0x3;     // CANCEL 键public const byte vKeyMButton = 0x4;    // 鼠标中键public const byte vKeyBack = 0x8;       // BACKSPACE 键public const byte vKeyTab = 0x9;        // TAB 键public const byte vKeyClear = 0xC;      // CLEAR 键public const byte vKeyReturn = 0xD;     // ENTER 键public const byte vKeyShift = 0x10;     // SHIFT 键public const byte vKeyControl = 0x11;   // CTRL 键public const byte vKeyAlt = 18;         // Alt 键  (键码18)public const byte vKeyMenu = 0x12;      // MENU 键public const byte vKeyPause = 0x13;     // PAUSE 键public const byte vKeyCapital = 0x14;   // CAPS LOCK 键public const byte vKeyEscape = 0x1B;    // ESC 键public const byte vKeySpace = 0x20;     // SPACEBAR 键public const byte vKeyPageUp = 0x21;    // PAGE UP 键public const byte vKeyEnd = 0x23;       // End 键public const byte vKeyHome = 0x24;      // HOME 键public const byte vKeyLeft = 0x25;      // LEFT ARROW 键public const byte vKeyUp = 0x26;        // UP ARROW 键public const byte vKeyRight = 0x27;     // RIGHT ARROW 键public const byte vKeyDown = 0x28;      // DOWN ARROW 键public const byte vKeySelect = 0x29;    // Select 键public const byte vKeyPrint = 0x2A;     // PRINT SCREEN 键public const byte vKeyExecute = 0x2B;   // EXECUTE 键public const byte vKeySnapshot = 0x2C;  // SNAPSHOT 键public const byte vKeyDelete = 0x2E;    // Delete 键public const byte vKeyHelp = 0x2F;      // HELP 键public const byte vKeyNumlock = 0x90;   // NUM LOCK 键//常用键 字母键A到Zpublic const byte vKeyA = 65;public const byte vKeyB = 66;public const byte vKeyC = 67;public const byte vKeyD = 68;public const byte vKeyE = 69;public const byte vKeyF = 70;public const byte vKeyG = 71;public const byte vKeyH = 72;public const byte vKeyI = 73;public const byte vKeyJ = 74;public const byte vKeyK = 75;public const byte vKeyL = 76;public const byte vKeyM = 77;public const byte vKeyN = 78;public const byte vKeyO = 79;public const byte vKeyP = 80;public const byte vKeyQ = 81;public const byte vKeyR = 82;public const byte vKeyS = 83;public const byte vKeyT = 84;public const byte vKeyU = 85;public const byte vKeyV = 86;public const byte vKeyW = 87;public const byte vKeyX = 88;public const byte vKeyY = 89;public const byte vKeyZ = 90;//数字键盘0到9public const byte vKey0 = 48;    // 0 键public const byte vKey1 = 49;    // 1 键public const byte vKey2 = 50;    // 2 键public const byte vKey3 = 51;    // 3 键public const byte vKey4 = 52;    // 4 键public const byte vKey5 = 53;    // 5 键public const byte vKey6 = 54;    // 6 键public const byte vKey7 = 55;    // 7 键public const byte vKey8 = 56;    // 8 键public const byte vKey9 = 57;    // 9 键public const byte vKeyNumpad0 = 0x60;    //0 键public const byte vKeyNumpad1 = 0x61;    //1 键public const byte vKeyNumpad2 = 0x62;    //2 键public const byte vKeyNumpad3 = 0x63;    //3 键public const byte vKeyNumpad4 = 0x64;    //4 键public const byte vKeyNumpad5 = 0x65;    //5 键public const byte vKeyNumpad6 = 0x66;    //6 键public const byte vKeyNumpad7 = 0x67;    //7 键public const byte vKeyNumpad8 = 0x68;    //8 键public const byte vKeyNumpad9 = 0x69;    //9 键public const byte vKeyMultiply = 0x6A;   // MULTIPLICATIONSIGN(*)键public const byte vKeyAdd = 0x6B;        // PLUS SIGN(+) 键public const byte vKeySeparator = 0x6C;  // ENTER 键public const byte vKeySubtract = 0x6D;   // MINUS SIGN(-) 键public const byte vKeyDecimal = 0x6E;    // DECIMAL POINT(.) 键public const byte vKeyDivide = 0x6F;     // DIVISION SIGN(/) 键//F1到F12按键public const byte vKeyF1 = 0x70;   //F1 键public const byte vKeyF2 = 0x71;   //F2 键public const byte vKeyF3 = 0x72;   //F3 键public const byte vKeyF4 = 0x73;   //F4 键public const byte vKeyF5 = 0x74;   //F5 键public const byte vKeyF6 = 0x75;   //F6 键public const byte vKeyF7 = 0x76;   //F7 键public const byte vKeyF8 = 0x77;   //F8 键public const byte vKeyF9 = 0x78;   //F9 键public const byte vKeyF10 = 0x79;  //F10 键public const byte vKeyF11 = 0x7A;  //F11 键public const byte vKeyF12 = 0x7B;  //F12 键[DllImport("user32.dll")]public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);public static void keyPress(byte keyName){KeyBoard.keybd_event(keyName, 0, 0, 0);KeyBoard.keybd_event(keyName, 0, 2, 0);}}static void Main(string[] args){            System.Threading.Thread.Sleep(6 * 1000);///MouseFlag.MouseLeftClickEvent(350, 70, 0);///System.Threading.Thread.Sleep(5*1000);///MouseFlag.MouseLeftClickEvent(20, 30, 0);///System.Threading.Thread.Sleep(5 * 1000);KeyBoard.keyPress(KeyBoard.vKeyBack);System.Threading.Thread.Sleep(5 * 1000);}}
}

驱动级模拟

介绍

有时候,按照上面说的方式进行模拟会失败,桌面软件尤其是一些游戏,对于消息命令一点也不“服从”,我们有这样的疑问:它是如何识别真实键盘的按键的呢?难道是程序中有判断吗?在DirectX编程中有个叫DirectInput的API,就是它绕过了Windows的消息机制,它的目的是为了让软件(游戏)的实时性控制更好、更快。Windows消息是队列形式的,在传递过程中会有延时,比如即时对战类游戏对实时性控制要求是非常高的,Window消息机制不能满足这个需求。而DirectInput直接和键盘驱动程序打交道,效率当然要高出一大截。

那么怎么解决这个问题呢,有人发现“按键精灵”使用了WINIO驱动的原理。于是我们也可以通过WINIO实现这样的模拟。

注意要使用winio.dll这个动态库,是需要一些条件的:

1- WinIO函数库只允许被具有管理者权限的应用程序调用。如果使用者不是以管理者的身份进入的,则WinIO.DLL不能够被安装,也不能激活WinIO驱动器。通过在管理者权限下安装驱动器软件就可以克服这种限制。然而,在这种情况下,ShutdownWinIo函数不能在应用程序结束之前被调用,因为该函数将WinIO驱动程序从系统注册表中删除。

2- 64位操作系统下需要将WinIo.sys驱动文件添加数字签名。

3- 注意WinIo.sys和WinIo.dll这两个文件的存放路径,如果路径不对,会导致初始化失败。

可能遇到各种麻烦,请耐心解决。

实现实例

(本实例摘自:点击打开链接)

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace lizheAionWG
{public class WinIo{public const int KBC_KEY_CMD = 0x64;public const int KBC_KEY_DATA = 0x60;[DllImport("winio.dll")]public static extern bool InitializeWinIo();[DllImport("winio.dll")]public static extern bool GetPortVal(IntPtr wPortAddr, out int pdwPortVal, byte bSize);[DllImport("winio.dll")]public static extern bool SetPortVal(uint wPortAddr, IntPtr dwPortVal, byte bSize);[DllImport("winio.dll")]public static extern byte MapPhysToLin(byte pbPhysAddr, uint dwPhysSize, IntPtr PhysicalMemoryHandle);[DllImport("winio.dll")]public static extern bool UnmapPhysicalMemory(IntPtr PhysicalMemoryHandle, byte pbLinAddr);[DllImport("winio.dll")]public static extern bool GetPhysLong(IntPtr pbPhysAddr, byte pdwPhysVal);[DllImport("winio.dll")]public static extern bool SetPhysLong(IntPtr pbPhysAddr, byte dwPhysVal);[DllImport("winio.dll")]public static extern void ShutdownWinIo();[DllImport("user32.dll")]public static extern int MapVirtualKey(uint Ucode, uint uMapType);public void sendwinio(){if (InitializeWinIo()){KBCWait4IBE();}}///Wait for Buffer gets emptyprivate void KBCWait4IBE(){int dwVal = 0;do{bool flag = GetPortVal((IntPtr)0x64, out dwVal, 1);}while ((dwVal & 0x2) > 0);}/// key downpublic void MykeyDown(int vKeyCoad){int btScancode = 0;btScancode = MapVirtualKey((byte)vKeyCoad, 0);KBCWait4IBE();SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)0xe2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)btScancode, 1);}/// Key uppublic void MykeyUp(int vKeyCoad){int btScancode = 0;btScancode = MapVirtualKey((byte)vKeyCoad, 0);KBCWait4IBE();SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)0xe0, 1);KBCWait4IBE();SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)btScancode, 1);}/// Simulate mouse downpublic void MyMouseDown(int vKeyCoad){int btScancode = 0;btScancode = MapVirtualKey((byte)vKeyCoad, 0);KBCWait4IBE(); // 'wait for buffer gets emptySetPortVal(KBC_KEY_CMD, (IntPtr)0xD3, 1);// 'send write commandKBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);// 'write in io}/// Simulate mouse uppublic void MyMouseUp(int vKeyCoad){int btScancode = 0;btScancode = MapVirtualKey((byte)vKeyCoad, 0);KBCWait4IBE(); // 'wait for buffer gets emptySetPortVal(KBC_KEY_CMD, (IntPtr)0xD3, 1); //'send write commandKBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);// 'write in io}//----------------------------------------------------------------------------------//VK codes//----------------------------------------------------------------------------------public enum Key{// mouse movementsmove = 0x0001,leftdown = 0x0002,leftup = 0x0004,rightdown = 0x0008,rightup = 0x0010,middledown = 0x0020,//keyboard stuffVK_LBUTTON = 1,VK_RBUTTON = 2,VK_CANCEL = 3,VK_MBUTTON = 4,VK_BACK = 8,VK_TAB = 9,VK_CLEAR = 12,VK_RETURN = 13,VK_SHIFT = 16,VK_CONTROL = 17,VK_MENU = 18,VK_PAUSE = 19,VK_CAPITAL = 20,VK_ESCAPE = 27,VK_SPACE = 32,VK_PRIOR = 33,VK_NEXT = 34,VK_END = 35,VK_HOME = 36,VK_LEFT = 37,VK_UP = 38,VK_RIGHT = 39,VK_DOWN = 40,VK_SELECT = 41,VK_PRINT = 42,VK_EXECUTE = 43,VK_SNAPSHOT = 44,VK_INSERT = 45,VK_DELETE = 46,VK_HELP = 47,VK_NUM0 = 48, //0VK_NUM1 = 49, //1VK_NUM2 = 50, //2VK_NUM3 = 51, //3VK_NUM4 = 52, //4VK_NUM5 = 53, //5VK_NUM6 = 54, //6VK_NUM7 = 55, //7VK_NUM8 = 56, //8VK_NUM9 = 57, //9VK_A = 65, //AVK_B = 66, //BVK_C = 67, //CVK_D = 68, //DVK_E = 69, //EVK_F = 70, //FVK_G = 71, //GVK_H = 72, //HVK_I = 73, //IVK_J = 74, //JVK_K = 75, //KVK_L = 76, //LVK_M = 77, //MVK_N = 78, //NVK_O = 79, //OVK_P = 80, //PVK_Q = 81, //QVK_R = 82, //RVK_S = 83, //SVK_T = 84, //TVK_U = 85, //UVK_V = 86, //VVK_W = 87, //WVK_X = 88, //XVK_Y = 89, //YVK_Z = 90, //ZVK_NUMPAD0 = 96, //0VK_NUMPAD1 = 97, //1VK_NUMPAD2 = 98, //2VK_NUMPAD3 = 99, //3VK_NUMPAD4 = 100, //4VK_NUMPAD5 = 101, //5VK_NUMPAD6 = 102, //6VK_NUMPAD7 = 103, //7VK_NUMPAD8 = 104, //8VK_NUMPAD9 = 105, //9VK_NULTIPLY = 106,VK_ADD = 107,VK_SEPARATOR = 108,VK_SUBTRACT = 109,VK_DECIMAL = 110,VK_DIVIDE = 111,VK_F1 = 112,VK_F2 = 113,VK_F3 = 114,VK_F4 = 115,VK_F5 = 116,VK_F6 = 117,VK_F7 = 118,VK_F8 = 119,VK_F9 = 120,VK_F10 = 121,VK_F11 = 122,VK_F12 = 123,VK_NUMLOCK = 144,VK_SCROLL = 145,middleup = 0x0040,xdown = 0x0080,xup = 0x0100,wheel = 0x0800,virtualdesk = 0x4000,absolute = 0x8000}}
}

某开发者自己封装了一个dll动态库,可参考这篇博文: 点击打开链接。

实际上,github上有许多类似的东西,可以找找合适的代码。

参考资料

C# 系统应用之鼠标模拟技术及自动操作鼠标

C#实现让鼠标点击任意绝对位置

C#窗体如何通过keybd_event()函数模拟键盘按键(组合键)产生事件

驱动级键盘模拟(C#)

Windows下对硬件端口的操作---WinIo库的使用

WinIo使用笔记

C#模拟鼠标和键盘操作相关推荐

  1. 控制台模拟鼠标、键盘操作

    控制台模拟鼠标.键盘操作 模拟鼠标.键盘操作,能让命令行顿然强大,想想,制作批处理版屏幕键盘等都不在话下(已制作过,效果很不错).虽然这也跟CUI无关.本教程教会你如何让命令行模拟鼠标.键盘的操作. ...

  2. selenium模拟鼠标和键盘操作的基本方法

    前言 前面已经把环境搭建好了,这从这篇开始,正式学习selenium的webdriver框架.我们平常说的 selenium自动化,其实它并不是类似于QTP之类的有GUI界面的可视化工具,我们要学的是 ...

  3. wpf 模拟鼠标和键盘操作

    我们需要用到的mouse_event函数,位于user32.dll这个库文件里面,所以我们要先声明引用. 复制代码 [System.Runtime.InteropServices.DllImport( ...

  4. C#模拟鼠标、键盘操作

    C语言 在程序中打开网页,模拟鼠标点击.键盘输入 一.简述         记--使用C语言 打开指定网页,并模拟鼠标点击.键盘输入.实现半自动填写账号密码,并登录网站(当然现在的大部分网站都有验证码 ...

  5. DELPHI编模拟鼠标,键盘操作,比键盘鼠标录制软件更强

    打网游朋友都知道,要下载一些录制鼠标键盘的操作,来代替频繁的手工操,但经过笔者测试发现,许多录制鼠标键盘软件,不是收费的,就是暗藏木马,得不偿失.会delphi编程朋友,用下面的代码,DIV自己的小程 ...

  6. python32模拟鼠标和键盘操作

    前言 Windows pywin32允许你像vc一样的形式来使用python开发win32应用.代码风格可以类似win32 sdk,也可以类似MFC,由你选择.如果你仍不放弃vc一样的代码过程在pyt ...

  7. python自带模块可以控制鼠标吗_python自带模块模拟鼠标和键盘操作

    复制自:https://www.cnblogs.com/wumac/p/5994923.html import win32api import win32con import win32gui fro ...

  8. C++实现鼠标控制 封装常见的模拟鼠标、键盘的操作函数

    API 或 MFC 视窗程序 里 有 函数, 例如 API 函数 设位置: BOOL SetCursorPos( int x, int y); 参数是屏幕坐标x,y 头文件 Winuser.h 链接库 ...

  9. python实现自动按键_Python使用pyautogui模块实现自动化鼠标和键盘操作示例

    本文实例讲述了Python使用pyautogui模块实现自动化鼠标和键盘操作.分享给大家供大家参考,具体如下: 一.pyautogui模块简要说明 ## 使用 pyautogui 模块相关函数,可以模 ...

  10. C# WPF 中用代码模拟鼠标和键盘的操作

    C# WPF 中用代码模拟鼠标和键盘的操作 原文:C# WPF 中用代码模拟鼠标和键盘的操作 原文地址 C#开发者都知道,在Winform开发中,SendKeys类提供的方法是很实用的.但是可惜的是, ...

最新文章

  1. 先自己看文档和百度再沟通
  2. 倒数日怎么显示在桌面_深圳暴风谷滑梯皮带提升机怎么选
  3. 开源,免费和跨平台 - MVP ComCamp 2015 KEYNOTE
  4. 随笔编号-04 AngularJS 相关小问题解决方案合集
  5. 关于Element UI中select组件中遇到的问题
  6. HTML/CSS/js 日期等其他函数 相关收藏
  7. 关于app跳转vueh5页面时获取url附带的参数_h5唤起app技术deeplink方案总结
  8. 一元二次方程极简新解法
  9. 三星Cortex-A53八核6818核心板
  10. ST188光电式传感器的应用
  11. 小白也会用的情人节表白神器
  12. 盘点2017企业服务领域最受关注的100家厂商(BPM平台篇)
  13. C语言编程统计八皇后问题的解的个数,八皇后问题C语言解法
  14. 百度地图天气接口api调用
  15. 使用python开发一个剪切视频、音频的小工具(完整案例)
  16. 推荐算法(一)--基本介绍
  17. Provides transitive vulnerable dependency - Intellij IDEA
  18. 考研英语核心词汇辨析(黑魔方系列2007版之一)
  19. vue后台管理系统实践方案总结(一)
  20. 软件安全与脆弱性分析-对于freenote小程序的Poc分析

热门文章

  1. 在安装Maven检查版本时出现Please set the JAVA_HOME variable in your environment to match the locat
  2. 360自研分布式存储系统Bada的架构设计和应用
  3. javase哪部分最难_JavaSe到底有多重要?
  4. 清除Mac电脑缓存的方法,非常实用哦
  5. cJSON库的使用(一)
  6. 生产稳定:JVM-MySQL-Tomcat-服务调用,调优相关
  7. 强网杯 2018 core ROP做法
  8. css 头像外圈白_如何使用css实现一个圆形头像框
  9. 字幕制作:一键合并双语字幕(2019.1.30)附所有工具
  10. 在学校,你需要知道的8种思维导图的类型及其工作方式