专栏首页一个程序员的修炼之路Windows C++堆破坏场景及分析

Windows C++堆破坏场景及分析

一个堆破坏的老故事

还记得第一次碰到堆破坏的时候,大概十年前了,当时在学校开发一个Wireshark插件,可是有一个问题我久久未能解决: 二次开发后的Wireshark,启动的时候偶尔会出现程序崩溃,那时候也不会用Windbg, 后来用Visual Studio启动Wireshark, 也是偶尔报错,这个时候可以看到堆栈,只记得当时是在一个很正常的内存分配或者释放的时候出现崩溃。那么总结为两点:

  1. 偶尔重现,那么也就是我们常说的还能跑起来,跑不起来那么就重启进程,重启进程无效,那就万能方法重启机器。这里想到一个名词叫做SRE (Site Reliability Engineering),有时候又戏称为Software Restart Engineer 或者 System Restart Engineer
  2. 在内存充足的情况,居然在申请内存或者释放内存的时候报错, 而且并不是直接导致内存破坏的地方。

那时候对于一个开发经验还不是很丰富的学生来说,搜索这种错误都不知道如何搜索。后来一个偶然的机会,听到了Applicaton Verifier,于是我就使用了这个工具进行了相关进程的配置,然后用Visual Studio启动了Wireshark, 在一个内存溢出的操作的时候,中断了,断点停在,我写的一个strcpy语句处,原来内存溢出导致了堆的破坏。

似懂非懂的知道了堆被破坏了其他变量的内存,但是为什么会导致堆破坏?并且Application Verifier是通过什么原理检测到这种错误的?还有阅读这篇文章的读者,你是否也曾碰到这种诡异的场景呢?那么让我们一起来看看Windows中的堆破坏和分析方法。

堆破坏

<<谈一谈Windows中的堆>>中比较详细地讲解了堆的结构,这里我们简单说一说堆中对象存储的基本结构。

堆的结构主要分为三层层:

  1. 一个堆由若干个Segment组成,每个Segment是连续的空间
  2. 一个Segment一般由若干个连续的Entry构成。
  3. 我们每次使用malloc/new申请的内存就占用一个Entry,一个Entry中只有User Data部分是malloc/new返回给应用程序的使用的地址,其他的部分为当前Entry的元数据,用于堆管理,在调试模式下还有一些用于调试的信息。

下图展示的了堆是如何破坏的,假设有两处应用程序申请的内存,分别为Entry1Entry2管理, 并且是连续的内存。那么这个时候拷贝信息到Entry1User Data, 然而没有控制好拷贝的长度,覆盖了Entry2Meta和部分User Data

这里我们问一个问题, 当出现上述堆破坏的时候,堆会直接报错吗? 并不会,因为此时执行的是内存拷贝操作,并不会做堆的任何检查操作。而是下次在堆上分配或者释放内存的时候,和这个Entry相关联的操作检查到堆破坏,从而导致程序崩溃。

其实程序崩溃有时候反而是好事,如果在堆被破坏发生时,其他对象的数据被修改,并且不立即导致程序崩溃,而继续运行进入了错误逻辑,可能会导致严重的后果。

那么我们要去检测堆破坏,能够抓取到破坏时候的函数调用栈吗?可以的,但是在讲解这种方法之前,先讲解下: 如果非第一现场检测到堆破坏,如何进行分析。

堆破坏之分析堆块内容

为什么要先讲解这种方法,而不是直接使用终极绝招,抓取第一现场呢?

  1. 如果你的软件在客户的环境中,他们在收集Dump后,并不一定配合帮你在他们机器上调试。请你直接分析已经Crash的Dump。
  2. 这种方法利于读者对于堆结构的理解,并且提供了解决堆破坏的思路。记得前段时间看过罗翔说的一句话,记得大概含义侦探小说里,一个一个看似不起眼的碎片,终究连成了一条线,构成了真相。 我们学习的每一个方法也是,并不仅仅是茴香豆的几种写法,而是一种思路,一种启发,也许未来的某一个会起到作用。

首先来看一个样例程序:

#include <iostream>
void HeapCorruptionFunction()
{
  char * pStr1 = new char[5];
  char * pStr2 = new char[5];
  printf("%p %p\n", pStr1, pStr2);
  strcpy(pStr1, "This is a heap corruption test");
  delete[]pStr2;
  delete[]pStr1;
}

int main()
{
  getchar();
  HeapCorruptionFunction();
  return 0;
}

这个程序比较简单, 对pStr1的拷贝操作内存越界了。需要声明的有两点:

  1. 这个程序不一定是百分百必现,因为pStr1pStr2的内存不一定是连续的或者靠近的
  2. 这个程序是启动后,再用Windbg附加调试,或者产生Dump。如果直接用调试器启动,那么堆的Entry分配会增加填充块用于调试,而直接启动进程后,再用调试器附加进程,这样堆的管理模式和实际发布版本运行时候的效果一样,接近在发布环境运行的状态。

接下来将讲解详细的分析步骤: 第一步 查看堆栈,对照代码,可以看到在delete[]pStr2;,正常的内存释放的地方出现了堆错误。那么这个时候我们可以联想到,是不是出现了堆破坏呢?

0:000> k
 # ChildEBP RetAddr  
00 0079f84c 772716c0 ntdll!RtlReportCriticalFailure+0x4b
01 0079f858 7726f5cf ntdll!RtlpReportHeapFailure+0x2f
02 0079f88c 77279e6e ntdll!RtlpHpHeapHandleError+0x6e
03 0079f8a0 772201ea ntdll!RtlpLogHeapFailure+0x41
04 0079f8fc 758af43b ntdll!RtlFreeHeap+0x4ccda
05 0079f910 758af408 ucrtbase!_free_base+0x1b
06 0079f920 00121061 ucrtbase!free+0x18
07 0079f94c 0012110e HeapCorruption!HeapCorruptionFunction+0x61 [c:\personal\sync\beyourbest\cpp\windbgsample\heapcorruption\source.cpp @ 8]
08 0079f954 00121328 HeapCorruption!main+0xe [c:\personal\sync\beyourbest\cpp\windbgsample\heapcorruption\source.cpp @ 16]
09 (Inline) -------- HeapCorruption!invoke_main+0x1c [d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
0a 0079f99c 75f38494 HeapCorruption!__scrt_common_main_seh+0xfa [d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
0b 0079f9b0 771f40e8 KERNEL32!BaseThreadInitThunk+0x24
0c 0079f9f8 771f40b8 ntdll!__RtlUserThreadStart+0x2f
0d 0079fa08 00000000 ntdll!_RtlUserThreadStart+0x1b

第二步 确认堆破坏, 可以看到HEAP ERROR DETECTED, 说明出现了堆破坏操作。

0:000> !heap -s
......
**************************************************************
*                                                            *
*                  HEAP ERROR DETECTED                       *
*                                                            *
**************************************************************

Details:

Heap address:  001b0000
Error address: 001c3fe8
Last known valid blocks: before - 001c3ef0, after - 001c40f8
Error type:    HEAP_FAILURE_MULTIPLE_ENTRIES_CORRUPTION
Details:       The heap manager detected multiple corrupt heap entries.
Follow-up:     Enable pageheap.

Stack trace:
                77279e6e: ntdll!RtlpLogHeapFailure+0x00000041
                772201ea: ntdll!RtlFreeHeap+0x0004ccda
                758af43b: ucrtbase!_free_base+0x0000001b
                758af408: ucrtbase!free+0x00000018
                00121061: HeapCorruption!HeapCorruptionFunction+0x00000061
                0012110e: HeapCorruption!main+0x0000000e
                00121328: HeapCorruption!__scrt_common_main_seh+0x000000fa
                75f38494: KERNEL32!BaseThreadInitThunk+0x00000024
                771f40e8: ntdll!__RtlUserThreadStart+0x0000002f
                771f40b8: ntdll!_RtlUserThreadStart+0x0000001b

LFH Key                   : 0xc159285b
Termination on corruption : ENABLED
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                    (k)     (k)    (k)     (k) length      blocks cont. heap 
-------------------------------------------------------------
001b0000 00000002    1128    528   1020    154    21     1    1      0   LFH
00520000 00001002      60     12     60      1     2     1    0      0      
02500000 00001002    1188     88   1080      1     5     2    0      0   LFH
-------------------------------------------------------------

第三步 根据堆破坏错误地址001c3fe8,可以切换到相应的栈帧,查看到其正好为pStr2Entry地址 (0x001c3ff0-0x8, 因为这个是32位程序,_HEAP_ENTRY元数据占用8个字节)。这个程序比较简单,也许你此时通过代码审查已经可以找到问题所在了。我们还要继续看看更多的信息,便于我们在相对复杂的场景做出分析。

0:000> .frame 0n7;dv /t /v
eax=00000000 ebx=772a58d0 ecx=c0000374 edx=0079f651 esi=00000002 edi=001b0000
eip=7726845c esp=0079f7b8 ebp=0079f84c iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244
ntdll!RtlReportCriticalFailure+0x4b:
7726845c cc              int     3
07 0079f94c 0012110e HeapCorruption!HeapCorruptionFunction+0x61 [c:\personal\sync\beyourbest\cpp\windbgsample\heapcorruption\source.cpp @ 8]
0079f948          char * pStr1 = 0x001c3fd0 "This is a heap corruption test"
0079f944          char * pStr2 = 0x001c3ff0 "???"

第四步 查看Entry对应的信息,Size显示为0,这个明显也是不对的,印证了堆破坏。

0:000> !heap -x 001c3fe8
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
001c3fe8  001c3ff0  001b0000  ffffffff         0      -            b  LFH;busy

第五步 那么现在我们关心的一个是当前内存是什么内容?一般出现堆破坏很大可能是堆的上溢,那么前一个堆块是什么?我们先来看看当前堆块的内容。使用了WindbgMemory窗口, 特意向前偏移了0x24个字节查看更多信息,可以看到0x001c3fe8这个Entry的元数据部分已经被字符串覆盖了。而此时我们可以看到这个字符串是This is a heap corruption test, 根据这个字符串信息就比较容易找到溢出的位置了。

第六步 虽然这个时候你可能已经找到了问题所在,那你是否还有刚才的疑问,那上一个堆块是什么呢?我们根据堆的地址0x001b0000查看其所有的申请的Entry, 找到被破坏的堆块最接近的Entry0x001c3ef0。然后用!heap -x 001c3ef0查看这个地址,居然得不到任何堆块信息,然而根据我们的样例来看,Windbg显示的这个结果也是不正确的,所以也不要完全相信调试器的结果。

还有一个方法,那就是我们可以用当前堆块减去1个字节去查看, 但是很明显这个Entry也不对,因为其起始地址加上大小后,超过了下一个Entry(即被破坏的Entry)的位置。

0:000> !heap -x 0x001C3FE8-0x1
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
001c3fd8  001c40f8  001b0000  001b0000     10308     108b8      35d8  busy user_flag  internal

0:000> ? 001c3fd8+0x10308
Evaluate expression: 1917664 = 001d42e0

这个时候可能是Windbg显示错误了,还有一种可能就是当前的Entry也被破坏了,调试过程中有时候也离不开猜测,而这些猜测也是你已有技术为基础的。那么我们再向前看一个Entry呢?这下看上去有点正确了,而且可以看到0x001c3fc8刚好为pStr1Entry起始地址(001C3FD0-0x8, 因为这个是32位程序,_HEAP_ENTRY元数据占用8个字节),原来如此,刚好和我们之前能够判断的pStr1的拷贝操作导致了接下来的连续两个Entry被覆盖了。

0:000> !heap -x 001c3fd8-0x1
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
001c3fc8  001c3fd0  001b0000  001b9a10        10      -            b  LFH;busy

往往真实的分析比这个要复杂许多,我们要从内存的内容中不放过任何的蛛丝马迹。

堆破坏分析之填充模式

在讲填充模式之前,我们先来想一想,如果你来想查看一个堆块是否被破坏会怎么做?这样的思考有利于自己更好的理解和加深这种方法的本质,可以运用在其他的地方。 填充模式,本质就是一种标记, 比如在下图中,在应用程序申请的内存之后填充一个叫做Post Pattern的部分,本人在Win10中测试32位程序填充的是8个字节的0xab

如果你使用调试器启动程序,比如Windbg, 当你操作内存溢出的时候会覆盖Post Pattern部分,而这个部分被覆盖后,当释放这块内存的时候,会校验是否这块内容发生了变化,如果发生了变化,则说明这块内存出现了溢出, 并且中断调试器。实际上如果用调试器启动在堆块的头部也会加上Pre Pattern,检测程序的下溢问题。

这个方法可以帮大家找出一些内存溢出问题,比如查看当前出现错误的堆块对应的操作代码进行审查,但是具有滞后性,无法在堆破坏的时刻保留第一现场,在有些场景分析堆破坏问题仍然非常困难: 比如当前被破坏的堆块,可能是由前面的堆块溢出而导致的破坏。

堆破坏分析之完全页堆(Full Page)

这种方法就是文中开头那个故事中用到的方法。使用Application Verifier, 配置如下图,开启对进程HeapCorruption.exe的Heap配置(其实也是一些操作系统的注册表配置),那么这个时候启动进程,将启用了完全页堆(Full Page)的调试技术。

当然你也可以使用Windows调试工具集中的gflag开启使用命令对进程开启Full Page调试技术: gflags /i ImageFileName +hpa 或者gflags /p /enable ImageFileName /full

在做了如上配置后,开启Dump收集(参考<<Windows程序Dump收集>>), 或者使用调试器直接启动进程。如果有内存的溢出则产生Dump,或者调试器中断程序。比如之前的测试代码,使用了Full Page后,用调试器运行则会出现Access Violation的错误。 查看此时的函数调用栈, 对比代码则可以看出来正是strcpy(pStr1, "This is a heap corruption test"); 这个函数的调用导致了异常的发生,也就是说,在内存溢出的时候,就直接检测到了这个第一现场。

0:000> k
 # ChildEBP RetAddr  
00 00f0fbf0 00e0104f ucrtbase!strcat+0x89
01 00f0fc20 00e01108 HeapCorruption!HeapCorruptionFunction+0x4f [c:\personal\sync\beyourbest\cpp\windbgsample\heapcorruption\source.cpp @ 7]
02 00f0fc28 00e01318 HeapCorruption!main+0x8 [c:\personal\sync\beyourbest\cpp\windbgsample\heapcorruption\source.cpp @ 16]
03 (Inline) -------- HeapCorruption!invoke_main+0x1c [d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
04 00f0fc70 75f38494 HeapCorruption!__scrt_common_main_seh+0xfa [d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
05 00f0fc84 771f40e8 kernel32!BaseThreadInitThunk+0x24
06 00f0fccc 771f40b8 ntdll!__RtlUserThreadStart+0x2f
07 00f0fcdc 00000000 ntdll!_RtlUserThreadStart+0x1b

那么Full Page是如何做到的呢?Windows中的为最小的内存管理单元,默认为4KBytesFull Page技术,使得应用程序申请的每个内存,对应的Entry后面,紧跟着一个PAGE_NOACCESS的页。那么存储的模式如下图:

那么当你访问上述Entry越界的时候,将会访问到下一个PAGE_NOACCESS的页面,此时将会有Access violation - code c0000005异常产生,直接中断程序,找到内存溢出的第一现场。

我们来看一看,刚刚的程序中断的时候pStr1的地址为0x0b29cff8

0:000> k
 # ChildEBP RetAddr  
00 00f0fbf0 00e0104f ucrtbase!strcat+0x89
01 00f0fc20 00e01108 HeapCorruption!HeapCorruptionFunction+0x4f [c:\personal\sync\beyourbest\cpp\windbgsample\heapcorruption\source.cpp @ 7]
02 00f0fc28 00e01318 HeapCorruption!main+0x8 [c:\personal\sync\beyourbest\cpp\windbgsample\heapcorruption\source.cpp @ 16]
03 (Inline) -------- HeapCorruption!invoke_main+0x1c [d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
04 00f0fc70 75f38494 HeapCorruption!__scrt_common_main_seh+0xfa [d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
05 00f0fc84 771f40e8 kernel32!BaseThreadInitThunk+0x24
06 00f0fccc 771f40b8 ntdll!__RtlUserThreadStart+0x2f
07 00f0fcdc 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000> .frame 0n1;dv /t /v
01 00f0fc20 00e01108 HeapCorruption!HeapCorruptionFunction+0x4f [c:\personal\sync\beyourbest\cpp\windbgsample\heapcorruption\source.cpp @ 7]
00f0fc1c          char * pStr1 = 0x0b29cff8 "This is "
00f0fc18          char * pStr2 = 0x0b2a0ff8 "???"

然后查看当前页, 可以看到当前页已经提交,并且属性为PAGE_READWRITE,可以读写。并且页的边界地址与申请的内存之间相隔了8个字节(0x0b29d000-0x0b29cff8=0x8)的大小,那么也就是说虽然我们申请了5个字节的数据,但是32位中的最小分配粒度为8个字节。

0:000> !address 0x0b29cff8 

Usage:                  PageHeap
Base Address:           0b29c000
End Address:            0b29d000
Region Size:            00001000 (   4.000 kB)
State:                  00001000          MEM_COMMIT
Protect:                00000004          PAGE_READWRITE
Type:                   00020000          MEM_PRIVATE
Allocation Base:        0b1a0000
Allocation Protect:     00000001          PAGE_NOACCESS
More info:              !heap -p 0x5b31000
More info:              !heap -p -a 0xb29cff8


Content source: 1 (target), length: 8
0:000> ? 0b29d000-0x0b29cff8 
Evaluate expression: 8 = 00000008

接着我们看当前页的紧邻的页的状态, 当前页的状态还处于MEM_RESERVE,还没有提交页,那么这个页是无法读写数据的,一旦访问,就会出现Access violation - code c0000005

0:000> !address 0b29d000+0x1

Usage:                  PageHeap
Base Address:           0b29d000
End Address:            0b29e000
Region Size:            00001000 (   4.000 kB)
State:                  00002000          MEM_RESERVE
Protect:                <info not present at the target>
Type:                   00020000          MEM_PRIVATE
Allocation Base:        0b1a0000
Allocation Protect:     00000001          PAGE_NOACCESS
More info:              !heap -p 0x5b31000
More info:              !heap -p -a 0xb29d001


Content source: 0 (invalid), length: fff

完全页堆这种方法用于追踪堆的破坏问题,确实是利器,这个技术确实能够有效地找出产品或者是第三方模块的堆破坏问题。尤其是第三方模块的第一现场尤为重要,我们知道堆可能在第三方模块破坏了,但是在产品模块中才开始报错,这就导致了责任不明确。如果抓取了第三方模块内的第一现场,第三方模块的负责人们才会承认和他们关联,也避免了责任推脱。

当然这种方法也有一点小小的缺陷,基本上一个页就存放了一个Entry,那就导致了内存消耗的巨大,要注意内存稍微大一些。

另外一个值得提醒的就是,可能你的调试环境中,配置了Full Page, 但是久而久之你忘记了,后来又在你测试环境运行程序,发现内存消耗比以前很多,而且程序运行效率下降,这个时候也要注意看看,是否是自己开启了一些调试选项哈:)

另外一个建议就是,希望在产品发布之前,都开启Full Page技术,测试自己的程序。

相关阅读

  1. <<谈一谈Windows中的堆>>
  2. <<C++常见的三种内存破的场景和分析>>
  3. <<Windows程序Dump收集>>

参考

Mario Hewardt / Daniel Pravat的<<Windows高级调试>>

本文分享自微信公众号 - 一个程序员的修炼之路(CoderStudyShare),作者:河边一枝柳

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++常见的三种内存破的场景和分析

    当出现以上场景的时候,你该思考一下,是不是出现了内存破坏的情况了。而本文主要通过展示和分析常见的三种内存破坏导致覆盖相邻变量的场景,让读者在碰到类似的场景,不至...

    河边一枝柳
  • 关于Adobe PDF 0day的故事

    今天StarLabs 发布了他们之前在天府杯上用的adobe pdf 完整exploit,该漏洞是一个 JS API UAF漏洞,利用代码公布在github上,...

    泉哥
  • 如何成为一个黑客?

    很多人要成为高大上的黑客需要学习哪些基本功? 能盗取账号,能攻击服务器? 再牛的黑客起码是一个合格的程序员 所以说想成为黑客先成为合格的程序再说,说别的就是空谈...

    程序员互动联盟
  • 微软Office曝存在17年之久的“全版本影响”老洞,无需用户交互实现恶意程序远程植入

    当人们仍在着急处理“没有补丁”的微软MS Office内置DDE功能威胁时,有研究人员又发现了Office的另一个严重漏洞,攻击者可以利用该漏洞,无需受害者用户...

    FB客服
  • 内存泄漏检测工具

    内存泄漏(memory leak),指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。

    阳光岛主
  • Windows客户端C/C++编程规范“建议”——函数

    等级:【要求】 说明:每个函数的代码行数控制应该控制在80行以内。如果超过这个限制函数内部逻辑一般可以拆分。如果试图超过这个标准,请列出理由。但理由不包含如下...

    方亮
  • 浅谈 Windows 编程中的堆

    提起堆,大部分人都不陌生,但是其实很多人也不见得就很了解。我见过的大部分人,对堆的理解其实还停留在,全局的一种内存,速度没有栈快,不会自动销毁,需要开发人员自己...

    Harper
  • Office 远程溢出漏洞测试与分析

    在 2017 年 11 月,微软发布的 11 月更新布丁中,微软将隐藏许久的 office 远程代码执行漏洞 (CVE-2017-11882)给修复了,由于该漏...

    信安之路
  • 在java中String类为什么要设计成final?

    String很多实用的特性,比如说“不可变性”,是工程师精心设计的艺术品!艺术品易碎!用final就是拒绝继承,防止世界被熊孩子破坏,维护世界和平!

    用户1205080
  • 黑客入侵加密企业所有服务器,嚣张留言勒索9.5比特币

    近日,腾讯御见威胁情报中心接到某公司反馈,公司里数台Windows服务器中了勒索病毒,电脑除了C盘,其他磁盘分区都被整个加密,公司业务已接近停摆。此外,该勒索病...

    FB客服
  • 大牛书单|安全技术方向好书分享

    导语:读书,伴随技术人的一生。技术人通过读书实现自我提升,学习优秀知识沉淀。TEG书知道本期特邀腾讯腾讯应用运维安全总监胡珀、TEG安全平台部专家工程师罗喜军、...

    腾讯技术工程官方号
  • 嵌入式开发中静态代码分析器的七种用途

    当前标准的C语言编译器存在普遍只能找出代码中潜在的缺陷,而对程序方案设计并没有效。使用静态代码分析器有助于提升固件和捕获编译器难以察觉的问题。以下是每一位嵌入式...

    CSDN技术头条
  • 误删除 文件 磁盘 格式化 勒索 加密 数据 恢复 指南

    R-Studio这个软件是Windows电脑和Windows服务器上都能运行的、可以恢复Windows文件系统的绝好软件,我试过了5种以上的恢复软件,就这个软件...

    shawyang
  • CountDownLatch和CyclicBarrier 傻傻的分不清?超长精美图文又来了

    并发编程的三大核心是分工,同步和互斥。在日常开发中,经常会碰到需要在主线程中开启多个子线程去并行的执行任务,并且主线程需要等待所有子线程执行完毕再进行汇总的场景...

    用户4172423
  • 【团队分享】刀锋铁骑:常见Android Native崩溃及错误原因

    王竞原,负责网游刀锋铁骑项目,高级开发工程师,使用C++已有10年,非常喜欢C++,特别是C++11。希望能与广大的C++爱好者多交流。 一、什么是Androi...

    腾讯Bugly
  • 云天励飞王孝宇:安防企业做零售的商业逻辑

    雷锋网《AI掘金志》频道:只做 AI +「安防、医疗、零售」三大传统领域的深度采访报道。

    AI掘金志
  • 腾讯安全推出御界NDR「横移检测版」,全面检测域渗透攻击

    由于企业内部资产及用户量庞大,大多数企业选择 AD 域作为用户和主机统一管理的方案,然而由于防护体系不完善,攻击者往往通过攻击域控进而攻击企业内部核心设备,获取...

    腾讯安全
  • C++编译器可自行编译出漏洞当C ++编译器写入VULN时

    由C ++编程错误引起的漏洞完全是司空见惯的。但是,当程序员编写正确的C ++程序并且编译器将其转换为包含漏洞的目标代码时,这是罕见的。这就是我在去年10月份所...

    C4rpeDime
  • [系统安全] 十七.Windows PE病毒概念、分类及感染方式详解

    作者前文介绍了PE文件格式,熟悉各种PE编辑查看工具,针对目标EXE程序新增对话框等;这篇文章将介绍Windows PE病毒, 包括PE病毒原理、分类及感染方式...

    Eastmount

扫码关注云+社区

领取腾讯云代金券