过 DNF TP 驱动保护(二)

文章目录:

01. 博文简介:

02. 环境及工具准备:

03. 分析 TP 所做的保护:

04. 干掉 NtOpenProcess 中的 Deep InLine Hook:

05. 干掉 NtOpenThread 中的 Deep InLine Hook:

06. 干掉 NtReadVirtualMemory 中的 InLine Hook:

07. 干掉 NtWriteVirtualMemory 中的 InLine Hook:

08. 干掉 KiAttachProcess 的 InLine Hook:

09. 干掉 NtGetContextThread 中的 InLine Hook:

10. 干掉 NtSetContextThread 中的 InLine Hook:

11. 干掉 DbgkpQueueMessage 中的 InLine Hook:

12. 干掉 DbgkpSetProcessDebugObject 中的 InLine Hook:

13. 干掉 Debug 清零:

共四篇,本篇为第二篇。

06. 干掉 NtReadVirtualMemory 中的 InLine Hook:

前面已经干掉了 TP 对 NtOpenProcess 以及 TP 对 NtOpenThread 所做的 Hook,

这样的话,我们已经可以使用 OD 或者 CE 看到 DNF 的游戏进程了,

弄过一些游戏方面内容的朋友应该都是知道 CE 的,这东东是开源的,不过是基于 Delphi 的,

工具做得很不错,可以通过 CE 来修改一些游戏进程的内存数据,比如可以修改一些简单的游戏的血值啊之类的,

但是,此时我们可以用 CE 来扫描一下 DNF 游戏进程的内存,你会发现根本扫描不到任何数据,

其实原因也很简单,TP 对 NtReadVirtualMemory 进行了浅层的 InLine Hook,

从而可以达到防止其他进程读取 DNF 游戏进程内存的目的,

而 CE 的话,在 Ring3 下是通过 ReadProcessMemory 来读取游戏内存的,

而 ReadProcessMemory 进入 Ring0 后又是调用的 NtReadVirtualMemory,

由于 NtReadVirtualMemory 被 TP 干掉了,所以自然用 CE 是扫描不出任何东西的,

要干掉 TP 对 NtReadVirtualMemory 所作的浅层 InLine Hook 其实是比较简单的,

因为 TP 并没有对 NtReadVirtualMemory 做检测,所以可以直接用 SSDT 来对抗掉浅层的 InLine Hook 就 OK。

至于原理的话,很简单,直接用 SSDT Hook 替换掉系统服务 NtReadVirtualMemory,

然后在 SSDTHookNtReadVirtualMemory 这个我们自己写的系统服务中判断,

如果是 DNF 进程的话,直接调用原来的 SSDT 系统服务就好,如果不是 DNF 进程的话,

我就跳过 TP 对 NtReadVirtualMemory 所做的 InLine Hook,也就是跳过前面 7 个字节就 OK 了。

对于这种干掉 InLine Hook 的原理,堕落天才的文章讲的最清楚了,我这里就不再班门弄斧了。

(继续为堕落天才打广告)

对于用 SSDT Hook 干掉浅层的 InLine Hook 可以参考看雪上堕落天才的文章:

文章名称:《SSDT Hook 的妙用 - 对抗 Ring0 InLine Hook》

文章地址:http://bbs.pediy.com/showthread.php?t=40832

有的童鞋可能会问,咱是如何知道 NtReadVirtualMemory 被 TP 干掉了呢 ?

很简单,还是采用前面的做法,用 Kernel Detective 就可以看到了,具体可以看下面的截图:

至于如何安装 SSDT Hook 或者 SSDT Hook 是啥玩意来着的话,大家有兴趣的可以参考我的下面博文系列:

《进程隐藏与进程保护(SSDT Hook 实现)》系列,共三篇。

http://www.cnblogs.com/BoyXiao/archive/2011/09/03/2164574.html

http://www.cnblogs.com/BoyXiao/archive/2011/09/04/2166596.html

http://www.cnblogs.com/BoyXiao/archive/2011/09/05/2168115.html

下面先贴出安装 SSDT 钩子的代码,该代码用来干掉 TP 对 NtReadVirtualMemory 的 InLine Hook:

   1:  /************************************************************************/
   2:  /* 安装钩子从而过掉 TP 保护所 Hook 的 NtReadVirtualMemory - 让 TP 失效
   3:  /* 保存 NtReadVirtualMemory 第 4,5,6,7 个字节(其实就是一个 ULONG 跳转地址)
   4:  /* 因为这几个字节在不同的 XP 上会改变,所以在 SSDT Hook 之前保存下来
   5:  /* 从而避免在此处进行硬编码
   6:  /************************************************************************/
   7:  VOID InstallPassTPNtReadVirtualMemory()
   8:  {
   9:      if(g_SSDTHookNtReadVirtualMemory > 0)
  10:      {
  11:          /* 获得 NtReadVirtualMemory 的地址 */
  12:          ULONG uNtReadVirtualMemoryAddr = oldSysServiceAddr[g_SSDTHookNtReadVirtualMemory];
  13:   
  14:          /* 如果是 DNF 进程,则跳到 NtReadVirtualMemory 执行,即不处理,从而让 DNF InLine Hook 生效 */
  15:          uTPHookedNtReadVirtualMemoryJmpAddr = uNtReadVirtualMemoryAddr;
  16:          /* 如果不是 DNF 进程,则跳过 TP 的 InLine Hook,从而使 TP 失效 */
  17:          uMyHookedNtReadVirtualMemoryJmpAddr = uNtReadVirtualMemoryAddr + 7;
  18:          /* 保存下未 Hook 之前的 NtReadVirtualMemory 的第 4,5,6,7 个字节 */
  19:          uNtReadVirtualMemoryAddr_3 = *((ULONG *)(uNtReadVirtualMemoryAddr + 3));
  20:   
  21:          InstallSysServiceHookByIndex(g_SSDTHookNtReadVirtualMemory, SSDTHookNtReadVirtualMemory);
  22:   
  23:          KdPrint(("Pass TP - NtReadVirtualMemory Installed."));
  24:      }
  25:  }

下面再给出 SSDT Hook 的中继 API 的实现代码:

   1:  /************************************************************************/
   2:  /* 自定义的 NtReadVirtualMemory,用来实现 SSDT Hook Kernel API
   3:  /************************************************************************/
   4:  NTSYSHOOKAPI VOID SSDTHookNtReadVirtualMemory()
   5:  {
   6:      /* 开始过滤 */
   7:      if(ValidateCurrentProcessIsDNF() == TRUE)
   8:      {
   9:          __asm
  10:          {
  11:              /* 如果是 DNF 进程调用的话,则调用已经被 TP Hook 的 NtReadVirtualMemory */
  12:              jmp    uTPHookedNtReadVirtualMemoryJmpAddr
  13:          }
  14:      }
  15:   
  16:      __asm
  17:      {
  18:          /* 已经做了针对硬编码的处理 */
  19:          /* 如果不是 DNF 进程调用的话,则跳过 TP Hook 的 NtReadVirtualMemory */
  20:          push    0x1C
  21:          push    uNtReadVirtualMemoryAddr_3
  22:          jmp     uMyHookedNtReadVirtualMemoryJmpAddr
  23:      }
  24:  }

好,到这里就已经干掉了 TP 对 NtReadVirtualMemory 所做的 InLine Hook了,

对此最直白的效果就是用 CE 打开 DNF 游戏进程进行内存扫描,你会发现,On Year,可以正常扫描到 DNF 内存了。

07. 干掉 NtWriteVirtualMemory 中的 InLine Hook:

上面又干掉了 TP 对 NtReadVirtualMemory 的 InLine Hook 了,从而实现了 CE 读取 DNF 进程的内存。

但是你可以试着用 CE 修改 DNF 进程的内存,你很快就会发现,虽然可以扫描内存,但是并不可以读取内存,

而后你也肯定能够想到,既然有防止读取内存,那肯定也有防止写入内存,

而后你又自然会找到 NtWriteVirtualMemory 上来,等你找到 NtWriteVirtualMemory 后,

你又基本能够确定,搞定这个 API 和搞定 NtReadVirtualMemory 应该是差不多的,

因为这两个 API 就是一对啊,一个读,一个写,所以 TP 肯定也是采用相同的做法来处理这两个 API,

当然,你上面的猜想都是正确的,所以咱还是用 SSDT 来干掉 TP 对 NtWriteVirtualMemory 所做的 InLine。

代码和上面的 NtReadVirtualMemory 差不多,这里也还是贴出来一下吧,俺先放两幅截图,然后再贴代码出来:

下面先贴出安装 SSDT 钩子的代码,该代码用来干掉 TP 对 NtWriteVirtualMemory 的 InLine Hook:

   1:  /************************************************************************/
   2:  /* 安装钩子从而过掉 TP 保护所 Hook 的 NtWriteVirtualMemory - 让 TP 失效
   3:  /* 保存 NtWriteVirtualMemory 第 4,5,6,7 个字节(其实就是一个 ULONG 跳转地址)
   4:  /* 因为这几个字节在不同的 XP 上会改变,所以在 SSDT Hook 之前保存下来
   5:  /* 从而避免在此处进行硬编码
   6:  /************************************************************************/
   7:  VOID InstallPassTPNtWriteVirtualMemory()
   8:  {
   9:      if(g_SSDTHookNtWriteVirtualMemory > 0)
  10:      {
  11:          /* 获得 NtWriteVirtualMemory 的地址 */
  12:          ULONG uNtWriteVirtualMemoryAddr = oldSysServiceAddr[g_SSDTHookNtWriteVirtualMemory];
  13:   
  14:          /* 如果是 DNF 进程,则跳到 NtWriteVirtualMemory 执行,即不处理,从而让 DNF InLine Hook 生效 */
  15:          uTPHookedNtWriteVirtualMemoryJmpAddr = uNtWriteVirtualMemoryAddr;
  16:          /* 如果不是 DNF 进程,则跳过 TP 的 InLine Hook,从而使 TP 失效 */
  17:          uMyHookedNtWriteVirtualMemoryJmpAddr = uNtWriteVirtualMemoryAddr + 7;
  18:          /* 保存下未 Hook 之前的 NtReadVirtualMemory 的第4,5,6,7 个字节 */
  19:          uNtWriteVirtualMemoryAddr_3 = *((ULONG *)(uNtWriteVirtualMemoryAddr + 3));
  20:   
  21:          InstallSysServiceHookByIndex(g_SSDTHookNtWriteVirtualMemory, SSDTHookNtWriteVirtualMemory);
  22:   
  23:          KdPrint(("Pass TP - NtWriteVirtualMemory Installed."));
  24:      }
  25:  }

下面再给出 SSDT Hook 的中继 API 的实现代码:

   1:  /************************************************************************/
   2:  /* 自定义的 NtWriteVirtualMemory,用来实现 SSDT Hook Kernel API
   3:  /************************************************************************/
   4:  NTSYSHOOKAPI VOID SSDTHookNtWriteVirtualMemory ()
   5:  {
   6:      /* 开始过滤 */
   7:      if(ValidateCurrentProcessIsDNF() == TRUE)
   8:      {
   9:          __asm
  10:          {
  11:              /* 如果是 DNF 进程调用的话,则调用已经被 TP Hook 的 NtWriteVirtualMemory */
  12:              jmp    uTPHookedNtWriteVirtualMemoryJmpAddr
  13:          }
  14:      }
  15:   
  16:      __asm
  17:      {
  18:          /* 已经做了针对硬编码的处理 */
  19:          /* 如果不是 DNF 进程调用的话,则跳过 TP Hook 的 NtWriteVirtualMemory */
  20:          push    0x1C
  21:          push    uNtWriteVirtualMemoryAddr_3
  22:          jmp     uMyHookedNtWriteVirtualMemoryJmpAddr
  23:      }
  24:  }

好,到这里就已经拿下了 TP 对 NtWriteVirtualMemory 所做的 InLine Hook了,

对此最直白的效果大家也都可以想象得到了,那就是直接用 CE 打开 DNF 游戏进程进行内存修改,

你会发现,此时可以正常修改掉 DNF 游戏的内存了。

08. 干掉 KiAttachProcess 中的 InLine Hook:

前面已经干掉 NtOpenProcess 和 NtOpenThread 了,按道理来说,咱可以开始用 OD 来调试 DNF 游戏进程了,

不过你可以用 OD 尝试附加一下 DNF 的游戏进程,而后你会发现根本附加不上去,这又是为何呢 ?

首先需要明白 Ring3 在何种操作下,会导致内核调用 KiAttachProcess,

从字面意思上看,就是附加进程,在 Ring3 下的附加进程操作一般会出现在调试进程的时候,

比如用 OD 附加进程来进行调试,或者用 Visual Studio 附加进程进行调试,

其实咱猜的没错,就是当附加进程的时候会导致内核调用 KiAttachProcess,

TP 对这个未导出的 API 进行了 InLine Hook,从而使得 DNF 游戏进程不能被附加,

这样的话,咱的 OD 或者 Visual Studio 都是不能够附加上 DNF 的游戏进程来进行调试了。

正如前面分析的 NtReadVirtualMemory 和 NtWriteVirtualMemory 这两个 API 一样,

TP 对 KiAttachProcess 也是做的浅层的 InLine Hook,也就是只是 Hook 了函数头 7 个字节,

且 TP 对 KiAttachProcess 的 InLine Hook 也没有检测,所以干掉这个 API 还是比较简单的,

不过我们不可以像干掉 NtRead/WriteVirtualMemory 一样用 SSDT Hook 来对抗掉,

因为 KiAttachProcess 并没有在 SSDT 表中,不过我们可以直接恢复 TP 对 KiAttachProcess 的 Hook 即可。

前面也提到了 KiAttachProcess 是一个未导出的内核 API,

所以我们不能使用 MmGetSystemRoutineAddress 来获得它的地址,

不过好在 KeAttachProcess 这是一个导出的内核 API,

(一般 Kixxx 都是未导出的 API,而 Kexxx 则是导出的 API)

在 KeAttachProcess 的内部实质上是调用的 KiAttachProcess 来完成功能的,

所以咱可以通过 KeAttachProcess 来定位到 KiAttachProcess 的地址,

并且让人欣喜的是在 KeAttachProcess 中的第一个 call 就是 call KiAttachProcess,

所以搜索特征码也更加简单了,直接搜索第一个 e8 就 OK。

如果用 WinDbg 的话,你可以输几个命令就可以把 KeAttachProcess 的地址打印出来,但是现在咱走点弯路,

在驱动里面用 KdPrint 打印出 KeAttachProcess 的地址,然后我们再分析。

   1:  KdPrint(("KeAttachProcess: %x.", MmGetSystemFunAddress(L"KeAttachProcess")));

好,这里得到了 KeAttachProcess 的地址了,

那么咱就可以在 Kernel Detective 中来看看 KeAttachProcess 的反汇编代码了,

具体的就看图说话,俺也就不多说了,因为截图里面都明明白白摆着在哪里,

下面给出获取 KiAttachProcess 地址的代码:

   1:  /************************************************************************/
   2:  /* 获取函数 KiAttachProcess 的地址
   3:  /* KiAttachProcess 在 KeAttachProcess 中的第一个 Call(e8 指令) 位置
   4:  /************************************************************************/
   5:  ULONG GetKiAttachProcessAddr()
   6:  {
   7:      ULONG uCallAddr = 0;
   8:      ULONG uKeAttachProcessAddr = 0;
   9:      ULONG uKiAttachProcessAddr = 0;
  10:      CHAR szCode[1] = 
  11:      {
  12:          (char)0xe8
  13:      };
  14:   
  15:      /* 获取 KeAttachProcess 的地址 */
  16:      uKeAttachProcessAddr = MmGetSystemFunAddress(L"KeAttachProcess");
  17:   
  18:      /* 搜索特征码 e8 */
  19:      uCallAddr = SearchFeature(uKeAttachProcessAddr, szCode, 1);
  20:      if (uCallAddr == 0)
  21:      {
  22:          uKiAttachProcessAddr = 0;
  23:      }
  24:      else
  25:      {
  26:          /* 获取 KiAttachProcess 的地址 */
  27:          uKiAttachProcessAddr = *((ULONG *)uCallAddr) + uCallAddr + 4;
  28:      }
  29:   
  30:      return uKiAttachProcessAddr;
  31:  }

得到了 KiAttachProcess 的地址,那么下面就要来干掉 KiAttachProcess 了,

为了简单实现,我一开始干掉 KiAttachProcess 的做法是采用的硬编码,

在得到 KiAttachProcess 地址后,可以用 Xuetr 来反汇编这个地址,

从而看到 KiAttachProcess 的反汇编指令,所以我的做法就是,

直接将 Xuetr 中 KiAttachProcess 的头 7 个字节以硬编码的形式保存在数组中,

然后等 TP 启动后,我用保存下来的这 7 个字节直接去恢复 KiAttachProcess 就 OK,

这种方式在我的虚拟机 XP 里面是 OK 的,因为我就是从这台 XP 上获取的 7 个字节的硬编码,

但是在别的 XP 系统上就不 OK 了,原因是 KiAttachProcess 头 7 个字节并不是所有的 XP 都相同的,

直接拿不一致的硬编码去恢复肯定是会 BSOD 的,不过后来我用了一种简单的办法就干掉这个问题了,

办法很简单,在 TP 启动之前,我动态去读取 KiAttachProcess 的头 7 个字节,并且保存在数组中,

等 TP 启动后,我就用数组中的这 7 个字节去恢复 TP 对 KiAttachProcess 所做的 InLine Hook。

实现的具体代码很简单,下面也贴出来:

先在 TP 启动之前保存 KiAttachProcess 的头 7 个字节:

   1:      PUCHAR pKiAttachProcessAddr = NULL;
   2:   
   3:      pKiAttachProcessAddr = (PUCHAR)GetKiAttachProcessAddr();
   4:   
   5:      KdPrint(("KiAttachProcess: %x.", uKiAttachProcessAddr));
   6:   
   7:      /* 保存 KiAttachProcess 被 DNF Hook 之前的头 API_HOOK_HEADER_LEN 个字节 */
   8:      for(uIndex = 0; uIndex < API_HOOK_HEADER_LEN; uIndex++)
   9:      {
  10:          szKiAttachProcessOriginCode[uIndex] = pKiAttachProcessAddr[uIndex];
  11:      }

TP 启动之后恢复被 TP 修改掉的 KiAttachProcess 的头 7 个字节:

   1:  /************************************************************************/
   2:  /* 恢复 KiAttachProcess 头 9 个字节从而恢复 KiAttachProcess - 让 TP 失效
   3:  /* 这里使用了硬编码,去掉硬编码的思路如下:
   4:  /* 在 TP 未启动之前,先保存下 KiAttachProcess 的头 9 个字节
   5:  /* 然后在 TP 启动之后,恢复这 9 个字节就 OK
   6:  /************************************************************************/
   7:  VOID RecoveryTPHookedKiAttachProcess()
   8:  {
   9:      ULONG uIndex = 0;
  10:      ULONG uOldAttr = 0;
  11:   
  12:      KIRQL kOldIRQL = PASSIVE_LEVEL;
  13:      PUCHAR pKiAttachProcessAddr = NULL;
  14:   
  15:      /* 获取到 KiAttachProcess 的地址 */
  16:      pKiAttachProcessAddr = (PUCHAR)GetKiAttachProcessAddr();
  17:   
  18:      EnableWriteProtect(&uOldAttr);
  19:      kOldIRQL = KeRaiseIrqlToDpcLevel();
  20:   
  21:      /* 恢复 KiAttachProcess 的头 API_HOOK_HEADER_LEN 个字节 */
  22:      for(uIndex = 0; uIndex < API_HOOK_HEADER_LEN; uIndex++)
  23:      {
  24:          pKiAttachProcessAddr[uIndex] = szKiAttachProcessOriginCode[uIndex];
  25:      }
  26:   
  27:      KeLowerIrql(kOldIRQL);
  28:      DisableWriteProtect(uOldAttr);
  29:   
  30:      KdPrint(("Pass TP - KiAttachProcess Installed."));
  31:  }

到这里,我们又把 KiAttachProcess 给搞定了,所以此时咱可以用 OD 来附加 DNF 游戏进程试试看了,

此时你会发现咱可以将 OD 附加上去了,不过可惜的是,就算附加上去了,离使用 OD 来调试 DNF 还远着呢,

因为 TP 对 DNF 游戏进程还有其他的保护措施,它在内核里面 Hook 的 API 可不止这么点哦。

总结:

《过 DNF TP 驱动保护》的第二篇到这里就结束了,经过上面的处理,

我们已经过掉了 TP 所做 InLine Hook 的 5 个 API 了,

首先是 NtOpenProcess 和 NtOpenThread 的深层 InLine Hook,

然后是 SSDT 系统服务函数 NtReadVirtualMemory 和 NtWriteVirtualMemory 的浅层次 InLine Hook,

最后我们也干掉了 TP 对未导出内核函数 KiAttachProcess 所做的浅层次 InLine Hook。

虽然也干掉了不少 TP 在内核中 Hook 的 API 了,但是这离过 DNF TP 驱动保护还比较远的,

详情还得留到下回分解了。

版权所有,欢迎转载,但转载请注明: 转载自  Zachary.XiaoZhen - 梦想的天空

过 DNF TP 驱动保护(二)相关推荐

  1. 过 DNF TP 驱动保护(一)

    文章目录: 01. 博文简介: 02. 环境及工具准备: 03. 分析 TP 所做的保护: 04. 干掉 NtOpenProcess 中的 Deep InLine Hook: 05. 干掉 NtOpe ...

  2. 高通平台tp驱动(二)

    上一篇大概讲了一下gsl680驱动的初始化部分,这部分是比较基础的,下面就开始第二部分:gsl680驱动的前期工作 首先会对一开始声明的input_dev结构体进行一个申请操作,但是这部分是与Inpu ...

  3. QQ游戏系列(寻仙,DNF等等)驱动保护TesSafe.sys

    QQ游戏系列(寻仙,DNF等等)驱动保护TesSafe.sys &V~l(1  1.用RKU看一下SSDT和SSDTShadow,发现SSDT并没有被HOOK,SSDTShadow HOOK了 ...

  4. 过驱动保护第一套视频(63课)

    63.白名单与校验的对抗.7z 62.过DebugProt.7z 61.过梦幻西游NECHECK保护.7z 60.过龙之谷GPK游戏保护.7z 59.真正过TP让CE可以附扫描.7z 58.DNF 驱 ...

  5. linux 块设备驱动(二)——块设备数据结构

    linux 块设备驱动(二)--块设备数据结构 本文来源于: 1. http://www.cnblogs.com/dyllove98/archive/2013/07/01/3165567.html 块 ...

  6. MTK 驱动(64)---Mtk touch panel驱动/TP驱动详解

    Mtk touch panel驱动/TP驱动详解 TP还算是比LCM好理解的多. 在启动过程中,先注册/mediatek/custom/command/kernel/touchpanel目录下的具体驱 ...

  7. input子系统与tp驱动

    1.层级结构 jiang-pc:~/build_projects2/build/60_ali/kernel-3.18/drivers/input$ ls apm-power.c ff-core.c g ...

  8. 【梅哥的Ring0湿润插入教程】【番外篇四】抓取盛大GPK驱动保护文件及简略逆向...

    [梅哥的Ring0湿润插入教程] Email:mlkui@163.com 转载请注明出处,谢绝喷子记者等,如引起各类不适请自觉滚J8蛋! 番外篇四:抓取盛大GPK驱动保护文件及简略逆向 [湿润前言] ...

  9. 最新零基础C++逆向辅助+过驱动保护+lua

    最新零基础C++逆向辅助+过驱动保护+lua 地址:资源共享吧 最新零基础C++逆向辅助+过驱动保护+lua-资源共享吧-专业共享VIP视频 最新零基础C++逆向辅助+过驱动保护+lua https: ...

  10. 基于SDM710 TP驱动

    一.TP 相关概念 基本报点原理:当屏幕感应到手指的接触或者靠近,会产生一个外部中断给 CPU,在中断中,一般中断下半部,通过I2C总线,从TP的IC中读取相关的信息,经过一定的数据处理,上报X,Y坐 ...

最新文章

  1. java.io.file.sync_Java(25)IO流和File类
  2. 程序人生:女程序员的求职奋斗史
  3. app抢购脚本如何编写_如何用1个记事本文件征服全世界?——cmd批处理脚本编写...
  4. 4699元!三星Galaxy A90 5G国行版开启预售 10月25日正式开售
  5. 真人拳皇项目第七次Scrum总结——史经浩
  6. 作业一:建立博客、自我介绍、速读教材、学习进度总结(1)
  7. 强化学习在游戏中的作用_游戏中的强化学习
  8. 【1】Matlab深度学习环境配置-入门
  9. 批量图片压缩、生成PDF文件及PDF文件压缩
  10. 微信小程序校园介绍板
  11. 建筑师们终于在虚拟空间开双年展了! | 绿洲 · 虚拟现实专栏
  12. C语言数据类型谜题总结
  13. 杨辉三角 c语言 二维数组
  14. POE网络变压器的检测要求及具体测试标准
  15. KNN和K-means的区别 为什么KNN算法里的K越小模型会越复杂? 过拟合和欠拟合的偏差和方差问题
  16. 国际标准:ISO 8601
  17. Tachyon的安装、配置和使用
  18. Linux常用命令小合集
  19. Outlook Express出0x800C0133错误的解决
  20. 信息系统项目管理:如何对项目实施变更?

热门文章

  1. 阿兹猫 的 Java模拟登陆QQ邮箱获取好友列表的实现
  2. java数组的四种拷贝方式
  3. Linux 服务器上传文件到阿里网盘
  4. 苏州外包php,【苏州IT外包经验】Ubuntu 17.10系统下配置PHP+Apache+Mysql
  5. 初中英语语法(004)-代词
  6. 企业如何通过TPM管理降低设备维修费用?
  7. 哪里看最新行业研报?
  8. Failed to connect to github.com port 443: Connection refused问题解决
  9. 分分钟免费搭建一个简单而美博客
  10. visual studio 2019 无法启动程序 系统找不到指定文件