Cerber是一个可执行程序,它的感染后行为没有CryptXXX这么隐蔽,可以说分析它的行为并不困难,但是它对内部数据的保护比CryptXXX做的好。例如: 我可以写一个简单反向算法就可以将CryptXXX中的所有加密数据提取出来,但是对于Cerber它显然在对内部数据的保护上下足了功夫,但这并没有阻挡我提取出它所有的内部数据。下面会详细的分析它如何保护内部数据的逻辑。

还需要了解的是,Cerber是C语言实现的,并且它并没有使用微软的运行库,换句话说它没有采用微软的编译器,很有可能是Intel的编译器或者是Cross-Compiler的gcc,我不确定。能够肯定的是2点,没有采用微软编译器,没有使用C++标准库。

2016-7-8

更新一下,我原本通过分析CryptXXX,感觉CryptXXX的逻辑设计的非常巧妙,它的感染方式被分散到很多个Export函数中。现在通过最新对Cerber的分析,发现Cerber也是设计的非常巧妙,它去做一些具体的操作时会调用其它命令行进程隐蔽自己。可以看出作者对windows系统的理解程度非常之深,应该是个高级玩家。

CryptXXX有各种设计精美的Blackmail,它们是Html,Bmp,Txt,有2个web服务器节点,与web的交互非常频繁,相互做backup。维护这些都需要人员和精力,它很像是一个团队在做。

Cerber更像是一个人在做,它的勒索页面很简单也不美观,它也没有与web交互,应该是没有精力维护web站点,或者更好的隐藏自己。但作者应该是个windows资深玩家。

为什么这么讲,因为我发现cerber会给自己提权,也会去check UAC当前的状态,还用了很多windows的内置变量(%xxxx%)这种形式的,我感觉作者以前就是做病毒的。

Global Data Structure

Named ImageOffset Size Description
g_lpModuleFullPath 0x41B760 4 Cerber所在的当前目录加文件名
g_lpModulePath 0x41B440 4 Cerber所在的目录
g_pAddrContainMeta 0x41B43C 0x124 一个数据结构包含Meta信息
g_hEvent 0x41B438 4 全局Event,手动Set初始无信号
g_szCerCoProtMutex 0x41B648 4 全局Mutex的name
g_hHeap 0x419A50 4 全局对象,私有堆句柄
g_dwCurrentPID 0x419A54 4 当前进程ID
g_dwImmMap 0x419A3C 0x10 一组常量,用于计算字符串
g_bEncryptDone 0x41BF61 4 加密是否完成标志
g_hCryptProv 0x41A168 4 cryptographic service provider
g_szModuleFileName 0x41B970 4 当前进程的文件名
g_tedGlobalMeta 0x41A0F8   全局Meta数据结构
g_bMultiThread 0x41A12C 4 全局标志,是否多线程加密
g_dwMaxBlockSize 0x41A14C 4 最大块的大小
g_dwMaxBlocks 0x41A12D 4 最大块数量
g_dqMinFileSize 0x41A138 8 最小文件大小
g_dwRsaKeySize 0x41A154 4 RsaKey的大小
g_Cerber_key_place 0x41BEB8 4  
g_pJsonObject 0x41A134 4 全局json对象
g_pfnIsWow64Process 0x41BFFC   API
g_InitEnvpPrograms 0x41BFF8 4 判断以下4个全局变量是否初始化
g_envpPCPFile 0x419AA4 4 %CommonProgramFiles%
g_envpPCPW6432 0x419AA8 4 %CommonProgramW6432%
g_envpProgramFile 0x419AAC 4 %ProgramFiles%
g_envpProgramW6432 0x419AB0 4 %ProgramW6432%
g_dwStopReason 0x41952C 4 程序不执行的原因
g_bStartNoParam 0x41BF60 4 标志是否没有命令行参数
g_lpNewExtension 0x41BC98 4 加密后新的扩展名
g_szWatchdogMutex 0x41BB78 4 Watchdog的mutex
g_byteDebug 0x41A12E 4 全局的调试标记,打开可以在DbgView中收到调试信息
g_byteNetWork 0x41A12F 4 设置为1加密网络文件
g_AvgReadBytes 0x41A150 4 平均每个块的Bytes数量

Meta数据机构

包含一个初始化的临界区变量,一个当前进程PID,当前进程的TID。

Export Function

funcName ImageOffset Description
Start 0x406BED EP 没有什么特别,PE的主入口
RSA Key
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF2a3R5NXFocUV5ZFI5MDc2RmV2cAowdU1QN0laTm1zMUFBN0dQUVVUaE1XYllpRVlJaEJLY1QwL253WXJCcTBPZ3Y3OUsxdHRhMDRFSFRyWGdjQXAvCk9KZ0JoejlONThhZXdkNHlaQm0yY29lYURHdmNHUkFjOWU3Mk9iRlEvVE1FL0lvN0xaNXFYRFd6RGFmSThMQTgKSlFtU3owTCsvRytMUFRXZzdrUE9wSlQ3V1NrUmI5VDh3NVFnWlJKdXZ2aEVySE04M2tPM0VMVEgrU29FSTUzcAo0RU5Wd2ZOTkVwT3BucE9PU0tRb2J0SXc1NkNzUUZyaGFjMHNRbE9qZWsvbXVWbHV4amlFbWMwZnN6azJXTFNuCnFyeWlNeXphSTVEV0JEallLWEExdHAyaC95Z2JrWWRGWVJiQUVxd3RMeFQyd01mV1BRSTVPa2hUYTl0WnFEMEgKblFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==

解密Cerber的内部数据非常麻烦,它用了很多层技术防止提取出它的内部数据,这些数据虽然可以在调试时动态的跑出来但是对于静态分析代码效率太低下。因此我实现了这个解密算法。

The assembly list was implemented by me
 decrypt
The file be generated automatically
 GenerateCodes

下面是解密后与解密前的数据,解密之前的数据我并没有 dump出来,因为我累计在这部分已经花去了超过24个小时(包括编写汇编算法,抽取脚本,分析等),我不想再花更多的时间完善它(后来强迫症犯了,提供了一个diff版方便看代码)。

Encrypt Decrypt
 encrypt  decrypt

通过分析decrypt.txt文件,可以发现cerber隐藏的很多细节,包括我并没有发现的细节。因此解密内部数据这项工作应该更早的展开。

通过阅读解密后的数据,可以了解它使用了那些技术。挑选一些主要的写在下面。

select * from %s 难道有内部数据库,这是不可能的,有可能是应用了WMI。
FirewallProduct 这个好像不言自明
AntiVirusProduct
cerber_startup_fake_service Fake?
wireshark.exe 看起来会反调试反监控,但是事实上好像没有work
dumpcap.exe
ollydbg.exe
SOFTWARE\Oracle\VirtualBox 看起来是虚拟机检查,不过貌似也没有work
VBoxMouse.sys
VMWARE
SOFTWARE\VMware, Inc.\VMware Tools
CERBER_WATCHDOG_PROTECTION_MUTEX 这些串放到这里让人一看就知道他的行为,如果我写我会Debug和Release分别放置不同的值
CERBER_CORE_PROTECTION_MUTEX
CERBER_STATISTICS_PROTECTION_MUTEX
bcdedit.exe

/set {default} recoveryenabled no

这个… 很贱
Software\Microsoft\Windows\CurrentVersion\Run 看起来要自启动
AutoRun  
   

这是一个Json格式文件,被解密后释放出来。是一个配置文件

The file type of JSON which Extracted from decrypt
 config

这个Json后面会释放出3个勒索文件出来

 help_cerber注意,这个包没有病毒,但是在Windows10上会被Windows Defender警告,因为它检测到模式匹配

主程序行为详细分析。

先看一下下面链接的代码:

start

Cerber程序被执行后,会先解密一个内部数据并计算一个四字节的值存放到全局变量中。然后通过当前进程的Pid创建一个全局唯一的Mutex命名字符串并用它构造命名的Mutex对象(MSCTF.Shared.MUTEX.575906ab)。

紧接着将当前全路径提取出来,同时将文件名去掉只保留路径,并把这两个值保存到全局变量中去。

接着,开始尝试启动新的cerber进程。这个过程比较隐晦,它先枚举系统所有的窗口站,并查找有没有交互式窗口,如果找到了,将这个交互式窗口链接到当前进程,然后注册并启动一个Windows Service,这个服务只做一件事情,就是不停的尝试再启动一个Cerber进程。如果启动服务没有成功,那么就直接创建一个Cerber进程,这个过程也很麻烦,它会先获取当前活动的SessionID,并进而获得用户的Token,用用户的身份去创建一个新的Cerber,无论两种方法只要创建成功,那就不会返回,进程退出。

TED_CreateCerberAgain

CB_CreateThreadOnSCM

TR_CreateProcessUtillSucess

TED_CreateProcess

wrap_CreateProcessAsUser

这是程序的Self-Spawn流程。

解密内部数据流程

在进入真正搞破坏之前,程序还有些准备工作要做,其中就包括从自身的资源文件中提取一个Json文件出来,并用一个JsonParser生成一个Json对象出来。这里有可能使用了第三方的Json解析库,我不确定是Jsonc还是Jansson。因为我已经把Json内部的一些方法分析出来了,才判断出那是在构造一个Json对象,因此如果早一点介入调查第三方库会效率更高一些。

Ok,回到初始化JsonObject的部分,首先程序会把自己Image中的资源段文件读取出来,然后通过自定义的算法计算出Json文件的串并Hold在内存中。下面我来分析一下这个过程。

首先,它会用一个神奇的算法解密出制定的字符串,这个神奇的算法被普遍应用于解密字符串在这个程序中(我已经实现了这个算法,1000行汇编代码)。

前面我说过cerber对内部数据的保护比CryptXXX做的更好就是由于它的解密内部数据的算法需要固定3个值才能解密出对应的数据,而CryptXXX的算法是固定,只要有输入就可以解密内部数据。

PCTSTR __cdecl TED_MakeRealTString(BYTE *byteUnknown, int nResSize, DWORD dwHex, char bUseWStr);

TED_MakeRealTString

上面是解密内部数据的func原型,其中需要固定的数据有保存在rdata中的字节,字节长度,一组解密用的key(4字节16进制数据)。

这个解密函数非常复杂,首先如果是第一次调用需要要进行一下初始化,工作包括将内部数据的链表头指向空,初始化meta信息,meta信息中包括了临界区变量,进程PID,TID。

然后,根据传入的rdata中的字节和字节长度计算一个CRC,然后遍历链表,如果匹配到了相同的CRC,就把当前链表中的item取出来,返回保存在item中WStr或者Str,这个是根据上面原型的第四个参数指定的。

如果通过CRC没有找到数据,那说明数据还没有被缓存,执行计算过程。首先为链表分配一个新的节点,并把CRC放到新的item里面,然后开始执行计算。

计算的过程也很复杂,首先在栈上构造一个ASCII码表,然后把输入的字节解释称0-255的整数数组,并用一个会自动循环并且最大值不会超过输入字节长度的索引值不停的循环的取出输入数据中的值并当成整形与ASCII码表中整数计算出一个可变的数值,这个数值会与ASCII码表中的数据交换,经过这样循环的处理,会因为输入的不同得出不同的结果。(复杂代码用语言描述太难了,还是看代码吧)

TED_CalculateSpecialString

然后,它用被交换,或者说是扰乱过的ASCII码表与输入的加密后的数据做异或操作(一个复杂的异或操作,加减偏移什么的),计算出Real的数据出来。

我这里讲的是它解密出了一块Json的数据,下一步还要解析这个Json数据把它转换成内部的数据结构。

链表在这里的作用是缓存内部数据。

构造Json Object流程

在生成Json对象之前首先要了解Json的基本数据类型,json共有5种基本数据类型,它们是逻辑值,字符串,数值,数组和对象。其它信息如果想了解就去google一下吧。

程序首先构造了一个json对象:

TED_GetJsonMem

然后把这个对象和json串一起送入Parser中,Parser根据输入的Json会去填充这个JsonObject。这像是某一种第三方Json Parse库,因此我没有必要继续分析它的作用。看一下就知道是在解析Json。

JSON_Parse

看的出来,这个JSON_Parse被多个函数调用,但我们这里只需要关注Start这条线就可以了。

因为他上面调用它的生成Json对象的方法是一个通用方法,程序的其它位置会调用它来生成其它的Json对象。

TED_GenerateJsonObject

TODO:JsonStructure

JsonObject被初始化完毕后,在程序中提取配置信息等操作就由相应的操作函数来完成。这个对象是在内存中分配出来的,因此在全局数据区是发现不到它的。这个对象会再接下来的程序生命期中被多次使用到。

配置全局变量

接下来程序需要初始化一些全局数据成员,通过从Json中提取出key对应的value。这些全局变量是加密用户数据所需要指定的配置信息,它们包括:多线程加密标志,最大块尺寸,最大块数量,最小文件尺寸,RSAkey的size。

接下来,程序要进行一些加密前的检查工作,它先要去检查一下指定的注册表位置,第一次执行时这个位置是空的,因此这段code会直接返回,还不会开始加密用户的数据。

“Printers\Defaults\{89873163-8DC1-7563-E594-6622BDBB1978}”这个注册表位置会被检查,如果不是第一次运行,在这个位置会有几堆数据,这些数据包含是一个被序列化的Json对象,一组RsaKey,那么程序的就会解析这个Json对象,解析后的Json对象指针会被放到g_pJsonObject(看最开头有列表)这个位置,并且程序会提取出RasKey并计算一个hash并放到全局变量中去,完成这些工作后程序返回。

上面这个过程就是加快操作的功能,如果从注册表中提取不到Rsa信息,那么就从全局的json对象中构造,首先从json对象中提取一个用来加密的Key,这个key实际上可以从资源文件中提取出来(最上面有列出)。然后把这些信息写到注册表中,让下一次操作可以从注册表中提取数据。整个的流程如下:

TED_GetCerberKeyFromRegister

完整的加密配置准备工作的流程如下:

TED_PrepareEncrypt

CoreProtectionMutex

接下来程序初始化了一个全局mutex,这个mutex被Cerber当作Core Protection Mutex,然后创建了一个。

然后还会创建一个全局的Event。

GlobalMeta

然后程序开始分配一些内存,为创建全局的数据结构,这个结构包含一个meta信息,一组句柄,还有标记句柄数据中保存了多少句柄的数值。Meta信息是另一个数据结构,包含当前进程Pid,线程的tid,以及一个临界区对象。

struct tedStruData {

int dwCount;

tedGlobalMeta meta;

HANDLE* pObjHandle[64];

};

紧接着,他会创建一个线程,并把这条线程加入到全局数据结构的句柄数组中去。这条线程启动了之后会创建一个隐藏的窗口,这个窗口的窗口过程接收END_SESSION消息,当收到这条消息后,会对全局Event设置信号,这个事件是为了保证系统中其它的Cerber进程能够有效的退出。

CB_WindowProc

接下来,程序处理一下命令行参数,因为程序是命令行应用,第一次执行时不带参数,当启动后,会释放程序内的加密数据,这些数据包含了很多命令行参数。当cerber spawn一堆cerber的时候它们都会带一些特殊的参数进来,在这里,程序的行为将发生变化。它的核心逻辑就在这里了,分析这部分代码还是很有收获的,提权功能,关闭UAC功能,反AV产品功能,反虚拟机功能,判断是不是Admin功能,在64位操作系统上禁用文件系统重定向等在这里被大量使用,也导致了程序在不同的平台上行为不一样,学习到很多病毒的做法。也正是因为分析了这部分代码,我才感觉作者在windows领域浸淫已久,很牛逼。这些技术CryptXXX并没有出现。

TED_ProcessCommand

ProcessCommand里面的内容非常多,一一展开很费心力,我们先跳过这里,看看后面做了什么,最后在回过头看这部分。

2016-7-12 back to here

这个Func被重命名为TED_ProcessCommand,因为程序会根据不同的执行流选择做具体的操作,而具体的操作都是通过创建自身的一个副本,并提供一些不同的参数从而实现了同一个程序的不同行为,因此代码中需要有一个解析并处理Command Argv的地方,这个地方就是ProcessCommand函数。

这个函数被调用后,首先尝试启动调试权限,然后会判断当前的启动路径是不是一个特殊的路径(C:\Users\Administrator\AppData\Roaming\{64E3EBE0-DAE1-314A-731D-B7440DC4E389})在我机器上如上述路径,如果不是,就把程序拷贝到这里面去,并创建一个Process再次执行,当前Process退出。

如果是的话,接下来会去判断传入的参数数量,进入不同的if块中,这里有3种选择,没有参数,有2个参数,以及超过3个参数的条件。

没有参数的情况是直接执行本程序,这时候他会去简单当前是不是Admin的权限,如果是它尝试用管理员的身份重新启动cerber并带上-eval {dwCurrentPid}参数,但是它在这么做之前会先检查一下UAC的状态,如果UAC没有工作,那么它会这么做。如果UAC正在工作中,它会用cmd去启动当前的Process的镜像文件。{cmd /d /c start c:\tmp\cerber.exe –eval pid}. 这个原因导致调试困难。

如果程序启动后被传入了2个参数,这里有好几个可选项(eval,shadow,watchdog,stat,antiav)。如果shadow,那么将删除卷快照,并将启动恢复设置为no。

TED_PC_shadow

如果参数是watchdog,程序先初始化watchdog的专用mutex,先启动一个watchdog创建线程,创建watchdog process,然后这个线程会每隔1秒监控watchdog process是否始终活动,如果超过4秒watchdog process仍然活跃,那么久杀了这个process再创建一个,这么做是不是为了防止watchdog process被轻易的发现?

TED_PC_watchdog

TR_Watchdog

如果不是第一次运行,那么watchdog可能已经被创建了,程序打开watchdog的全局mutex,如果发现mutex的句柄不存在,说明程序可能出现了问题,例如被调试等,那么程序会继续检查Core Protection Mutex在不在,如果也不存在,那么杀死所有在系统中的cerber进程,隐蔽它的行为。

如果参数是antiav,它会检查是不是具有管理员权限,然后执行AntiAVProduct函数,内部逻辑异常复杂,涉及到权限,SID,Boot等设置,放代码读下不解释了。

TED_AnitAv

如果参数是stat,那么它开始执行真正的加密工作,在开始加密之前,需要进行一些准备工作。首先把json中的encrypt对象中的数据全部load到内存中,包括一组扩展名列表,然后打开注册表Printers\Defaults\{89873163-8DC1-7563-E594-6622BDBB1978},在这个位置把RsaKey取出来,如果取不到,它会先从内存中把Key给弄进去,下次就会取到Key了。

然后,进入Encrypt函数,开始执行真正的加密工作。这里有个debug标志,如果把这个标志打开,会把一些调试信息写到文件中,可能得到一些有效的信息。如果json文件中指定多线程加密,那么就会根据指定的数量创建多条线程进行加密。

TED_Encrypt

TR_DoEncrypt

TED_EncryptData主要加密函数,入参是路径+文件名,加密的核心算法就在这个func里面

在加密之前还有一写逻辑用来枚举所有驱动器,网络驱动器的,把需要加密的磁盘递归的加入一个链表中(这个链表数据结构很复杂,我很想弄出来,不过由于太耗费时间放弃了)。

TED_EnumDevice 枚举所有可以访问并有写权限的驱动器

TED_RecursionFolder 递归的将所有文件加入链表

退出部分

经过上面的逻辑后,程序会抛出很多的Process,函数返回后用户的数据也已经被加密了,剩下的部分是准备推出部分,这部分不是特别重要,但是它却展示了程序内部数据结构的构成,以及如何管理大量的句柄。

这次先放代码,看后分析

TED_PrepareLeave 主调

TED_WaitAllObject 具体

主调函数先进入临界区,然后调用waitAllObject去等待所有的句柄相应。句柄中大量的process和thread。WaitAllObject函数已经被我分析好了,这部分代码很有意思,理解它需要有一些领域知识,如果没有写过高并发服务器的话,估计很难看得懂这部分代码(指未翻译之前的代码)。

首先有一个背景知识,就是一个WaitForMutliObject句柄组上限是64个,超过这个数量就没法管理了(不信可以去google)。那么如果需要在wait中管理超过64个句柄怎么办,有一个办法,类似虚拟地址映射(页表,页目录),可以创建一些管理线程,这些线程每一个管理另外64个句柄,这样可以无限放大(二级,三级这样)。有了这些知识再看代码就清晰了,可以读一下我翻译过的代码。

继续往下,都是一些释放操作没什么好说的。最后如果加密完成,它要通知其他系统中的cerber进程退出,这些进程可能是上面wait不到的。反正要退出了,这里也比较暴力,枚举所有cerber然后调用Terminate。后面还有一个force的做backup,是通过cmd.exe调用taskkill来杀。最后在自杀。放一个小片段吧。

SR_start

原文地址: http://ec2-52-196-167-189.ap-northeast-1.compute.amazonaws.com/wordpress/index.php/2016/07/15/cerber-analysis/

Ransomware Cerber Analysis相关推荐

  1. Ransomware CryptXXX Analysis

    Global Data Structure Named ImageOffset Description pConfig 0x4259A0 全局配置信息,窗口句柄,全局标志位,当前进程是否是Svchos ...

  2. Ransomware Locky Analysis

    Locky的变种非常的多,这个样本来自下面的Url,是最新的一种变种. 这是程序在刚开始执行时与释放了Image并替换了之后的对比,很明显发生了进程替换,因此进行分析之前有必要把它内部释放出来的ima ...

  3. Algorithm, Secret key and Protocol

    最近在对基于区块链构建的信任社会(未来社会形态)非常感兴趣,区块技术去中心化的特性,让没有金融机构成为了可能(包括央行,以及各种商业银行). 除了在数字货币领域大放异彩外,在包括供应链,网络购物,公平 ...

  4. 【翻译】WannaCry ransomware attack

    来源[维基百科-wannacray] WannaCry ransomware attack From Wikipedia, the free encyclopedia The WannaCry ran ...

  5. Tryhackme-Malware Analysis

    Malware Analysis 文章目录 Malware Analysis **History of Malware** task1 Introduction task2 The Creeper P ...

  6. All Your PLCs Belong to Me: ICS Ransomware Is Realistic

    你所有的PLC都属于我:ICS勒索软件是现实的 一.摘要 二.介绍 三.系统模型和威胁模型 (一)系统模型 (二)威胁模型 四.实验评估 (一)评估指标 1. PLC评估 2.监控计算机评估 (二)攻 ...

  7. TensorRT Analysis Report分析报告

    TensorRT Analysis Report 一.介绍 TensorRT是一个高性能的深度学习推理(Inference)优化器,可以为深度学习应用提供低延迟.高吞吐率的部署推理.TensorRT可 ...

  8. PCA(Principal Component Analysis)的原理、算法步骤和实现。

    PCA的原理介绍:  PCA(Principal Component Analysis)是一种常用的数据分析方法.PCA通过线性变换将原始数据变换为一组各维度线性无关的表示,可用于提取数据的主要特征分 ...

  9. 机器学习与高维信息检索 - Note 7 - 核主成分分析(Kernel Principal Component Analysis,K-PCA)

    Note 7 - 核主成分分析(Kernel Principal Component Analysis) 核主成分分析 Note 7 - 核主成分分析(Kernel Principal Compone ...

最新文章

  1. c语言的编译过程,程序编译过程
  2. Grid_Oracle Grid Infrastructure概念介绍(概念)
  3. Php基础数学运算篇
  4. 【转载】给不同 type 的 input 自动设置样式
  5. java消息推送与接收
  6. php对接钉钉_php实现钉钉业务报警机器人
  7. 火出边际的Serverless,你居然还不了解?
  8. Android Studio 使用笔记:工具窗口浮动与布局恢复
  9. 流程控制语句反汇编(1)(Debug版)
  10. oripa手机版_ORIPA - Origami Pattern Editor
  11. 一张网络路由器与能源路由器对照表(2015年)
  12. 搭建云上博客——阿里云实验室 学习笔记
  13. win7计算机的蓝牙,教你win7电脑蓝牙在哪里打开
  14. Xcode打包后,找不到dSYM文件
  15. PCIe/PCI插槽不够用怎么办
  16. SIEBEL功能组件,eScript入门
  17. 微信表情包小程序,更新登录接口,增加举牌功能
  18. 统计每个日期新用户的次日留存率
  19. Hooks 常见的问题
  20. 运算符-12-多学一招原码反码补码,隐式和强制转换,位运算

热门文章

  1. 命令行下django-admin.py参数不起作用的问题解决
  2. !!基础---c# 下载网页+图片
  3. tf.stack()和tf.unstack()的用法
  4. Python学习笔记:错误和异常
  5. Python学习笔记:访问数据库
  6. Python:windows程序打包
  7. C++中的内联函数inline总结
  8. C++引入抽象基类和纯虚函数的作用和目的
  9. iis无法启动计算机上的服务器,Win7系统iis无法启动怎么解决?
  10. Python与matlab在存储三维数组上的区别