最近一段时间由于使用MinHook的API挂钩不稳定,经常因为挂钩地址错误而导致宿主进程崩溃。听同事介绍了一款智能强大的挂钩引擎EasyHook。它比微软的detours好的一点是它的x64注入支持是免费开源的。不想微软的detours,想搞x64还得购买。

好了,闲话不多说,先下载EasyHook的开发库,当然有兴趣的同学可以下载源码进行学习。下载地址:http://easyhook.codeplex.com/releases/view/24401。我给的这个是2.6版本的。

EasyHook提供了两种模式的注入管理。一种是托管代码的注入,另一种是非托管代码的注入。我是学习C++的,所以直接学习了例子中的非托管项目UnmanagedHook。里面给了一个简单的挂钩MessageBeep API的示例。我需要将其改造成支持远程注入的。下面先给出钩子DLL代码:

[cpp] view plaincopy
  1. // dllmain.cpp : 定义 DLL 应用程序的入口点。
  2. #include "stdafx.h"
  3. #include "HookApi.h"
  4. #include "easyhook.h"
  5. #include "ntstatus.h"
  6. ptrCreateFileW realCreateFileW = NULL;
  7. ptrCreateFileA realCreateFileA = NULL;
  8. HMODULE                 hKernel32 = NULL;
  9. TRACED_HOOK_HANDLE      hHookCreateFileW = new HOOK_TRACE_INFO();
  10. TRACED_HOOK_HANDLE      hHookCreateFileA = new HOOK_TRACE_INFO();
  11. NTSTATUS                statue;
  12. ULONG                   HookCreateFileW_ACLEntries[1] = {0};
  13. ULONG                   HookCreateFileA_ACLEntries[1] = {0};
  14. int PrepareRealApiEntry()
  15. {
  16. OutputDebugString(L"PrepareRealApiEntry()\n");
  17. // 获取真实函数地址
  18. HMODULE hKernel32 = LoadLibrary(L"Kernel32.dll");
  19. if (hKernel32 == NULL)
  20. {
  21. OutputDebugString(L"LoadLibrary(L\"Kernel32.dll\") Error\n");
  22. return -6002;
  23. }
  24. OutputDebugString(L"LoadLibrary(L\"Kernel32.dll\") OK\n");
  25. realCreateFileW = (ptrCreateFileW)GetProcAddress(hKernel32, "CreateFileW");
  26. if (realCreateFileW == NULL)
  27. {
  28. OutputDebugString(L"(ptrCreateFileW)GetProcAddress(hKernel32, \"CreateFileW\") Error\n");
  29. return -6007;
  30. }
  31. OutputDebugString(L"(ptrCreateFileW)GetProcAddress(hKernel32, \"CreateFileW\") OK\n");
  32. realCreateFileA = (ptrCreateFileA)GetProcAddress(hKernel32, "CreateFileA");
  33. if (realCreateFileA == NULL)
  34. {
  35. OutputDebugString(L"(ptrCreateFileA)GetProcAddress(hKernel32, \"CreateFileA\") Error\n");
  36. return -6007;
  37. }
  38. OutputDebugString(L"(ptrCreateFileA)GetProcAddress(hKernel32, \"CreateFileA\") OK\n");
  39. return 0;
  40. }
  41. void DoHook()
  42. {
  43. OutputDebugString(L"DoHook()\n");
  44. statue = LhInstallHook(realCreateFileW,
  45. MyCreateFileW,
  46. /*(PVOID)0x12345678*/NULL,
  47. hHookCreateFileW);
  48. if(!SUCCEEDED(statue))
  49. {
  50. switch (statue)
  51. {
  52. case STATUS_NO_MEMORY:
  53. OutputDebugString(L"STATUS_NO_MEMORY\n");
  54. break;
  55. case STATUS_NOT_SUPPORTED:
  56. OutputDebugString(L"STATUS_NOT_SUPPORTED\n");
  57. break;
  58. case STATUS_INSUFFICIENT_RESOURCES:
  59. OutputDebugString(L"STATUS_INSUFFICIENT_RESOURCES\n");
  60. break;
  61. default:
  62. WCHAR dbgstr[512] = {0};
  63. wsprintf(dbgstr, L"%d\n", statue);
  64. OutputDebugString(dbgstr);
  65. }
  66. OutputDebugString(L"LhInstallHook(GetProcAddress(hKernel32, \"CreateFileW\"),MyCreateFileW,(PVOID)0x12345678,hHookCreateFileW); Error\n");
  67. return;
  68. }
  69. OutputDebugString(L"Hook CreateFileW OK\n");
  70. statue = LhInstallHook(realCreateFileA,
  71. MyCreateFileA,
  72. /*(PVOID)0x12345678*/NULL,
  73. hHookCreateFileA);
  74. if(!SUCCEEDED(statue))
  75. {
  76. switch (statue)
  77. {
  78. case STATUS_NO_MEMORY:
  79. OutputDebugString(L"STATUS_NO_MEMORY\n");
  80. break;
  81. case STATUS_NOT_SUPPORTED:
  82. OutputDebugString(L"STATUS_NOT_SUPPORTED\n");
  83. break;
  84. case STATUS_INSUFFICIENT_RESOURCES:
  85. OutputDebugString(L"STATUS_INSUFFICIENT_RESOURCES\n");
  86. break;
  87. default:
  88. WCHAR dbgstr[512] = {0};
  89. wsprintf(dbgstr, L"%d\n", statue);
  90. OutputDebugString(dbgstr);
  91. }
  92. OutputDebugString(L"LhInstallHook(GetProcAddress(hKernel32, \"CreateFileA\"),MyCreateFileA,(PVOID)0x12345678,hHookCreateFileA); Error\n");
  93. return;
  94. }
  95. OutputDebugString(L"Hook CreateFileA OK\n");
  96. // 一定要调用这个函数,否则注入的钩子无法正常运行。
  97. LhSetExclusiveACL(HookCreateFileA_ACLEntries, 1, hHookCreateFileA);
  98. LhSetExclusiveACL(HookCreateFileW_ACLEntries, 1, hHookCreateFileW);
  99. }
  100. void DoneHook()
  101. {
  102. OutputDebugString(L"DoneHook()\n");
  103. // this will also invalidate "hHook", because it is a traced handle...
  104. LhUninstallAllHooks();
  105. // this will do nothing because the hook is already removed...
  106. LhUninstallHook(hHookCreateFileA);
  107. LhUninstallHook(hHookCreateFileW);
  108. // now we can safely release the traced handle
  109. delete hHookCreateFileA;
  110. hHookCreateFileA = NULL;
  111. delete hHookCreateFileW;
  112. hHookCreateFileW = NULL;
  113. // even if the hook is removed, we need to wait for memory release
  114. LhWaitForPendingRemovals();
  115. }
  116. BOOL APIENTRY DllMain( HMODULE hModule,
  117. DWORD  ul_reason_for_call,
  118. LPVOID lpReserved
  119. )
  120. {
  121. switch (ul_reason_for_call)
  122. {
  123. case DLL_PROCESS_ATTACH:
  124. {
  125. OutputDebugString(L"DllMain::DLL_PROCESS_ATTACH\n");
  126. // 准备好原始地址与目的地址
  127. int errCode = PrepareRealApiEntry();
  128. if (errCode != 0)
  129. {
  130. OutputDebugString(L"PrepareRealApiEntry() Error\n");
  131. return FALSE;
  132. }
  133. // 开始挂钩
  134. DoHook();
  135. break;
  136. }
  137. case DLL_THREAD_ATTACH:
  138. {
  139. OutputDebugString(L"DllMain::DLL_THREAD_ATTACH\n");
  140. break;
  141. }
  142. case DLL_THREAD_DETACH:
  143. {
  144. OutputDebugString(L"DllMain::DLL_THREAD_DETACH\n");
  145. break;
  146. }
  147. case DLL_PROCESS_DETACH:
  148. {
  149. OutputDebugString(L"DllMain::DLL_PROCESS_DETACH\n");
  150. // 卸载钩子
  151. DoneHook();
  152. break;
  153. }
  154. }
  155. return TRUE;
  156. }
[cpp] view plaincopy
  1. <pre name="code" class="cpp">// HookSvr.cpp
  2. #include "stdafx.h"
  3. #include "HookApi.h"
  4. #include "easyhook.h"
  5. HANDLE WINAPI MyCreateFileW(
  6. __in     LPCWSTR lpFileName,
  7. __in     DWORD dwDesiredAccess,
  8. __in     DWORD dwShareMode,
  9. __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  10. __in     DWORD dwCreationDisposition,
  11. __in     DWORD dwFlagsAndAttributes,
  12. __in_opt HANDLE hTemplateFile
  13. )
  14. {
  15. HANDLE hHandle = NULL;
  16. // 执行钩子
  17. if (realCreateFileW == NULL)
  18. {
  19. OutputDebugString(L"realCreateFileW is NULL\n");
  20. return INVALID_HANDLE_VALUE;
  21. }
  22. else
  23. {
  24. OutputDebugString(L"realCreateFileW is not NULL\n");
  25. hHandle = (realCreateFileW)(lpFileName, dwDesiredAccess, dwShareMode,
  26. lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
  27. OutputDebugString(L"MyCreateFileW : ");
  28. OutputDebugString(lpFileName);
  29. OutputDebugString(L"\n");
  30. }
  31. return hHandle;
  32. }
  33. HANDLE WINAPI MyCreateFileA(
  34. __in     LPCSTR lpFileName,
  35. __in     DWORD dwDesiredAccess,
  36. __in     DWORD dwShareMode,
  37. __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  38. __in     DWORD dwCreationDisposition,
  39. __in     DWORD dwFlagsAndAttributes,
  40. __in_opt HANDLE hTemplateFile
  41. )
  42. {
  43. HANDLE hHandle = NULL;
  44. // 执行钩子
  45. if (realCreateFileA == NULL)
  46. {
  47. OutputDebugString(L"realCreateFileA is NULL\n");
  48. return INVALID_HANDLE_VALUE;
  49. }
  50. else
  51. {
  52. OutputDebugString(L"realCreateFileA is not NULL\n");
  53. hHandle = (realCreateFileA)(lpFileName, dwDesiredAccess, dwShareMode,
  54. lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
  55. OutputDebugString(L"MyCreateFileW : ");
  56. OutputDebugStringA(lpFileName);
  57. OutputDebugString(L"\n");
  58. }
  59. return hHandle;
  60. }</pre><br>
  61. 钩子这一部分我弄了比较久,主要是API不熟悉,不过好在弄好了。
  62. <pre></pre>
  63. <p><br>
  64. </p>
  65. <p></p><pre name="code" class="cpp">// HookSvr.h
  66. #pragma once
  67. #include <Windows.h>
  68. #ifndef _M_X64
  69. #pragma comment(lib, "EasyHook32.lib")
  70. #else
  71. #pragma comment(lib, "EasyHook64.lib")
  72. #endif
  73. HANDLE WINAPI MyCreateFileW(
  74. __in     LPCWSTR lpFileName,
  75. __in     DWORD dwDesiredAccess,
  76. __in     DWORD dwShareMode,
  77. __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  78. __in     DWORD dwCreationDisposition,
  79. __in     DWORD dwFlagsAndAttributes,
  80. __in_opt HANDLE hTemplateFile
  81. );
  82. typedef HANDLE (WINAPI *ptrCreateFileW)(
  83. __in     LPCWSTR lpFileName,
  84. __in     DWORD dwDesiredAccess,
  85. __in     DWORD dwShareMode,
  86. __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  87. __in     DWORD dwCreationDisposition,
  88. __in     DWORD dwFlagsAndAttributes,
  89. __in_opt HANDLE hTemplateFile
  90. );
  91. extern ptrCreateFileW realCreateFileW;
  92. HANDLE WINAPI MyCreateFileA(
  93. __in     LPCSTR lpFileName,
  94. __in     DWORD dwDesiredAccess,
  95. __in     DWORD dwShareMode,
  96. __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  97. __in     DWORD dwCreationDisposition,
  98. __in     DWORD dwFlagsAndAttributes,
  99. __in_opt HANDLE hTemplateFile
  100. );
  101. typedef HANDLE (WINAPI *ptrCreateFileA)(
  102. __in     LPCSTR lpFileName,
  103. __in     DWORD dwDesiredAccess,
  104. __in     DWORD dwShareMode,
  105. __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  106. __in     DWORD dwCreationDisposition,
  107. __in     DWORD dwFlagsAndAttributes,
  108. __in_opt HANDLE hTemplateFile
  109. );
  110. extern ptrCreateFileA realCreateFileA;</pre><br>
  111. <br>
  112. <p></p>
  113. <p>接下来是注入工具,这里指提供核心代码。本来EasyHook还提供了一个叫<span style="color:black">Rh</span>InjectLibrary()方法直接注入,这种方法相当稳定,推荐使用。我本来也用它,但是发现注入会失败,所以就采用了比较通用的远程注入代码,如下:</p>
  114. <pre name="code" class="cpp">BOOL RtlFileExists(WCHAR* InPath)
  115. {
  116. HANDLE          hFile;
  117. if((hFile = CreateFileW(InPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
  118. return FALSE;
  119. CloseHandle(hFile);
  120. return TRUE;
  121. }
  122. BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
  123. {
  124. TOKEN_PRIVILEGES tp;
  125. HANDLE hToken;
  126. LUID luid;
  127. if( !OpenProcessToken(GetCurrentProcess(),
  128. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  129. &hToken) )
  130. {
  131. return FALSE;
  132. }
  133. if( !LookupPrivilegeValue(NULL,             // lookup privilege on local system
  134. lpszPrivilege,    // privilege to lookup
  135. &luid) )          // receives LUID of privilege
  136. {
  137. return FALSE;
  138. }
  139. tp.PrivilegeCount = 1;
  140. tp.Privileges[0].Luid = luid;
  141. if( bEnablePrivilege )
  142. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  143. else
  144. tp.Privileges[0].Attributes = 0;
  145. // Enable the privilege or disable all privileges.
  146. if( !AdjustTokenPrivileges(hToken,
  147. FALSE,
  148. &tp,
  149. sizeof(TOKEN_PRIVILEGES),
  150. (PTOKEN_PRIVILEGES) NULL,
  151. (PDWORD) NULL) )
  152. {
  153. return FALSE;
  154. }
  155. if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
  156. {
  157. //The token does not have the specified privilege.
  158. return FALSE;
  159. }
  160. return TRUE;
  161. }
  162. typedef DWORD (WINAPI *PFNTCREATETHREADEX)
  163. (
  164. PHANDLE                 ThreadHandle,
  165. ACCESS_MASK             DesiredAccess,
  166. LPVOID                  ObjectAttributes,
  167. HANDLE                  ProcessHandle,
  168. LPTHREAD_START_ROUTINE  lpStartAddress,
  169. LPVOID                  lpParameter,
  170. BOOL                   CreateSuspended,
  171. DWORD                   dwStackSize,
  172. DWORD                   dw1,
  173. DWORD                   dw2,
  174. LPVOID                  Unknown
  175. );
  176. BOOL MyCreateRemoteThread(HANDLE hProcess, LPTHREAD_START_ROUTINE pThreadProc, LPVOID pRemoteBuf)
  177. {
  178. HANDLE      hThread = NULL;
  179. FARPROC     pFunc = NULL;
  180. BOOL bHook;
  181. // 判断系统版本
  182. OSVERSIONINFO osvi;
  183. //BOOL bIsWindowsXPorLater;
  184. ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
  185. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  186. GetVersionEx(&osvi);
  187. if (osvi.dwMajorVersion == 6)
  188. {
  189. bHook = TRUE;
  190. }
  191. else
  192. {
  193. bHook = FALSE;
  194. }
  195. if(bHook)    // Vista, 7, Server2008
  196. {
  197. pFunc = GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx");
  198. if( pFunc == NULL )
  199. {
  200. //GetLastError());
  201. return FALSE;
  202. }
  203. OutputDebugString(L"MyCreateRemoteThread");
  204. ((PFNTCREATETHREADEX)pFunc)(&hThread,
  205. 0x1FFFFF,
  206. NULL,
  207. hProcess,
  208. pThreadProc,
  209. pRemoteBuf,
  210. FALSE,
  211. NULL,
  212. NULL,
  213. NULL,
  214. NULL);
  215. if( hThread == NULL )
  216. {
  217. return FALSE;
  218. }
  219. }
  220. else                    // 2000, XP, Server2003
  221. {
  222. hThread = CreateRemoteThread(hProcess,
  223. NULL,
  224. 0,
  225. pThreadProc,
  226. pRemoteBuf,
  227. 0,
  228. NULL);
  229. if( hThread == NULL )
  230. {
  231. return FALSE;
  232. }
  233. }
  234. if( WAIT_FAILED == WaitForSingleObject(hThread, INFINITE) )
  235. {
  236. return FALSE;
  237. }
  238. return TRUE;
  239. }
  240. BOOL InjectDll(DWORD dwPID, const wchar_t *szDllName)
  241. {
  242. HANDLE hProcess = NULL;
  243. LPVOID pRemoteBuf = NULL;
  244. FARPROC pThreadProc = NULL;
  245. DWORD dwBufSize = wcslen(szDllName)*sizeof(wchar_t)+2;
  246. if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
  247. {
  248. return FALSE;
  249. }
  250. pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
  251. MEM_COMMIT, PAGE_READWRITE);
  252. WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName,
  253. dwBufSize, NULL);
  254. pThreadProc = GetProcAddress(GetModuleHandle(L"kernel32.dll"),
  255. "LoadLibraryW");
  256. if( !MyCreateRemoteThread(hProcess, (LPTHREAD_START_ROUTINE)pThreadProc, pRemoteBuf) )
  257. {
  258. return FALSE;
  259. }
  260. VirtualFreeEx(hProcess, pRemoteBuf, dwBufSize, MEM_RELEASE);
  261. CloseHandle(hProcess);
  262. return TRUE;
  263. }
  264. int DoInject(DWORD aPid, const WCHAR *aFullpath)
  265. {
  266. if (wcslen(aFullpath) <= 0)
  267. {
  268. return -1;
  269. }
  270. //判断dll是否存在
  271. HANDLE hFile = CreateFile(aFullpath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  272. if(hFile != INVALID_HANDLE_VALUE)
  273. {
  274. DWORD dwsize = GetFileSize(hFile, NULL);
  275. CloseHandle(hFile);
  276. if (dwsize < 10)
  277. {
  278. return -2;
  279. }
  280. }
  281. else
  282. {
  283. return -3;
  284. }
  285. BOOL bSuc=SetPrivilege(SE_DEBUG_NAME, TRUE);
  286. bSuc=InjectDll((DWORD)aPid, aFullpath);
  287. if (bSuc)
  288. {
  289. return -4;
  290. }
  291. return 0;
  292. }
  293. // 真实注入的时候应该这样调用
  294. DoInject(m_processId, L"E:\\src\\easyhook\\trunk\\Debug\\x86\\HookSvr.dll");
  295. </pre><br>
  296. 这样就能保证注入的钩子能正常工作了。

EasyHook远程代码注入相关推荐

  1. 远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例

    远程代码注入及DLL注入教程 说明 ​ 本人刚开始学习逆向,不知道有没有动力学下深去,这一块也没有详细的实战教学,学多少就上传多少,希望能给想学的朋友一点帮助吧,本教程想通过植物大战僵尸这一经典游戏来 ...

  2. Apache Log4j 远程代码注入漏洞

    漏洞说明 2021年12月9日,Apache Log4j2 Java 日志模块存在远程命令执行漏洞可直接控制目标服务器问题,攻击者攻击难度极低.由于 Apache Log4j2 某些功能存在递归解析功 ...

  3. java远程代码注入_Java RMI远程反序列化任意类及远程代码执行解析(CVE-2017-3241 )...

    原标题:Java RMI远程反序列化任意类及远程代码执行解析(CVE-2017-3241 ) 本打算慢慢写出来的,但前几天发现国外有研究员发了一篇关于这个CVE的文章,他和我找到的地方很相似.然而不知 ...

  4. [免费专栏] Android安全之利用JDB调试Android应用程序(动态代码注入技术)

    也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...

  5. 代码注入之远程线程篇

    引子 前些日子由于项目要求,在网上到处找资料,于无意中发现了 CodeProject 上的一篇很老的文章,文章标题为: Three Ways to Inject Your Code into Anot ...

  6. 【Android 逆向】Android 进程代码注入原理 ( 进程注入原理 | 远程调用流程 | 获取函数地址 | 设置 IP 寄存器 | mmap 申请内存 | 设置 SP 寄存器 )

    文章目录 一.进程注入原理 二.远程调用流程 ( 获取 so 动态库地址 | 获取函数地址 | 设置 IP 寄存器 | mmap 申请内存 | 设置 SP 寄存器 ) 一.进程注入原理 调试进程 At ...

  7. XXE漏洞利用技巧(XML注入):从XML到远程代码执行

    目录 什么是XXE 基本利用 Blind OOB XXE 场景1 - 端口扫描 场景2 - 通过DTD窃取文件 场景3 - 远程代码执行 场景4 - 钓鱼 场景4 - HTTP 内网主机探测 场景5  ...

  8. 利用DOCX文档远程模板注入执行宏代码

    利用DOCX文档远程模板注入执行宏代码 简介 本地文件中在没有宏代码的情况下,攻击者可以尝试执行远程文件中宏代码.其中来自APT28的最新样本将此技术展现的淋漓尽致.该样本是docx文件,文件内没有任 ...

  9. 软件安全之代码注入技术 向目标 PE 文件注入 DLL notepad lpk.dll 远程线程函数 提权函数 OpenProcess VirtualAllocEx

    实验 4 代码注入技术 引言 1.实验说明 代码注入是将用户代码注入到其他进程或者可执行文件中,实现拦截目标进程运行过程的关键信息.改变目标进程或可执行文件原本执行流程等目的 2.实验目的 本实验通过 ...

最新文章

  1. Vue之@click、事件修饰符@click.stop与@click.prevent、按键修饰符@keyup.enter
  2. 如何发布php网站_php网站如何发布【货币问答】- 联合货币
  3. QT的QGLFormat类的使用
  4. ASP.NET Core 3.0 gRPC 身份认证和授权
  5. linux默认归档目录,Linux系统管理(第4章:目录和文件管理二)
  6. java调用javascript函数_使用Java程序中的参数调用Javascript函数
  7. scrum 11.6
  8. 地域和地方的区别_区域经济学入门级名词内涵的探索(区域、地域、地区、空间和区位的区别与联系)...
  9. Java中的final、static、this、super 关键字
  10. python数据分析收获与心得体会_初次数据分析--我的心得体会
  11. 自动阅读专业版第七次更新---原薅羊毛专业版
  12. 测试进阶必备,这5款http接口自动化测试工具真的很香
  13. Java实现 LeetCode 741 摘樱桃(DFS || 递推 || 传纸条)
  14. java系统化基础-day02-运算符、选择结构、循环结构
  15. 用matlab绘制三维图和三视图
  16. VB完全控制IE浏览器,操作ie对象,响应ie事件
  17. 闲话虚拟仪器开发历史
  18. chmod 777 修改文件权限
  19. Eclipse使用教程1
  20. IDC服务商开启行业自律新篇章

热门文章

  1. Bootstrap组件_分页
  2. Java消息队列--ActiveMq 初体验
  3. CyclicBarrier详解
  4. SpringMVC+Spring+mybatis项目搭建详细过程
  5. Istio流量管理实践之(3): 基于Istio实现流量对比分析
  6. web开发的跨域问题详解
  7. 关于javascript中私有作用域的预解释
  8. 大数据时代的技术hive:hive介绍
  9. 什么是空间复杂度(What is actually Space Complexity ?)
  10. 善于总结所做所学的内容