ZeroAccess分析
来源:http://bbs.pediy.com/showthread.php?t=141124&highlight=ZeroAccess
总序
这分成四个部分的系列文章,是一个完全的一步一步来分析ZeroAccess Rootkit的教程。它也被叫做Smiscar恶意软件,或叫做Max++ rootkit。透过循着教程一步步的看下去,你可以更深入更好的理解到在分析复杂到此种程度的现代rootkit时的思考过程。我们还推荐你把文中提到的工具们下载下来、以及找一个ZeroAccess的样本,并在阅读文章的同时,自己能亲手亲自看一看的逆向它。如果你想使用文中分析的具体这个样本的话,可以从这儿下载到它: Max++ Malware。注意样本的压缩包已经用密码保护起来以避免误操作了,引号中 "infected"。从本文楼层的附件中也可以下载到它。
InfoSec(原作者所在的公司)把ZeroAccess定义作一个复杂且高端的超级rootkit程序。它拥有4个主要模块,也就是我们接下来每篇文章将要对应着做详细分析的。ZeroAccess它是一个模块化的恶意rootkit,它就像一个给应用程序提供生存空间的操作系统般的平台,其主功能是能够安装可控数量的病毒程序到中毒机器上。它的能力还让它自己以及它所安装的病毒程序对于专业级别用户也几乎是完全不可能清理掉的了,同时,它还对自己做了些让专业的病毒分析师难以着手分析的处理。
文章的最后,在整个分析结尾的时候,我们会去跟踪Zeroccess Rootkit犯罪分子所在的源头。我们会发现这整个rootkit的目的是要建立起来一个强壮,不可检测而且不可切除的平台,以供病毒在中毒机器上进行繁衍和破坏。我们也会看到在现在,ZeroAccess正在用来传播一个叫做FakeAntivirus(假杀毒)的犯罪程序,它的罪行是哄骗用户花70美元(大约400 RMB)以移除掉自己这个杀毒软件。假设100%的受害用户都付了这笔钱的话,那就将是一笔数额达到 $17,500,0000(约10 5000 0000 RMB)的巨额黑金。现实的,不会有100%的用户付给罪犯这钱,但假定会有约30$的用户受骗的话,结果也有总额达到 $5,250,0000 (3 1500 0000 RMB)的黑金流进RBN网络犯罪团伙的腰包了。
ZeroAccess Rootkit拥有如下的特征和能力:
# 先进稳固的劫持(hook)住了操作系统-这使得在不影响操作系统稳定性的前提下,很难移除掉它
# 使用底层API,从硬盘上分出一块完全在受害机器上隐藏着的新分区(盘符),这把对它进行的传统磁盘扫描变成不可能的,或也是很难做到了。
# 复杂并有高可靠性的修改了被害操作系统的驱动,让病毒代码可以依此来启动。
# 高技巧的过杀软机制
# 抗扫描技术- ZeroAccess使用底层的磁盘和文件系统调用来对抗常见的磁盘中、内存中扫描技术
# 提供一个强键有力的恢复其他病毒,并可以安装新病毒的平台
# 通过APC(Asynchronous Procedure Calls 异步过程调用)插入到所有的用户空间和内核空间的进程、还会跟着插进去自己的模块,以在内核级别监视住整个系统的功能,并且可以无缝的插入代码到任何被监视的模块中。
在我们这个教程中,我们会从头到尾同时不对病毒做干预的进行分析,我们跟着一条病毒对全新又无辜的用户机子感染的过程来进行我们的分析。这种分析方法,实际在文中的表现就是,我们会把rootkit的注入方法和用来感染机器的执行流程,以类似于年度总结的形式描述出来。年度总结似的分析体现给我们的会是一种思路性的rootkit注入过程,它所表现出来的注入思路有大量以前分析过的其他高端rootkit也使用到了。所以,有必要提醒你,在这一篇章中认真的感觉rootkit进行注入的流程,然后,在以后的病毒分析中你会很经常的发现这个经验是用的上的。
通常的,一个rootkit感染进一个主机时,它执行流程可以一步步的分为下面的格式:
1.传播rootkit,以使其可以接触到被害系统的运输工具. (如对用户引导性的下载,或是客户端上的溢出漏洞,或下载者)
2.用户模式的感染中介程序
3.对驱动的解密和执行起驱动
4.驱动在内核中进行系统级别的隐藏
5.从受害主机上冒出来,并从内核级别进行数据的监视/盗取
5.把盗取到的数据发送到一个隐蔽的数据通道里
我们也对应上面的一步步,把ZeroAccess的分析划成了一系列的文章:
章节1: 总序。还有对用户模式的感染中介程序的反混淆,以及逆向分析
章节2: 逆向分析内核模式中的数据盗取功能驱动
章节3: 逆向分析内核模式中的进程注入功能的驱动
章节4: 利用逆向工程跟踪到使用ZaeroAccess rootkit的罪恶之源
我们的分析将从用户层的代理程序开始,最后结束于rootkit丢出来的二个内核中的病毒设备驱动程序。
从源头开始分析:
ZeroAccess rootkit,在传播的源头是以一个恶意的可执行程序的形式存在的,它首先是通过引导性的下载来传播。引导性的下载有三个类型,每个都代表一种意外的从网上就下载到东西的情况:
1.下载行为是一个用户所允许了的下载,但用户当时并不明白将有什么后果(例,她下载了一个未知发行者或伪造发行者的可执行程序,或是ActiveX组件、Java小程序)
2.任何的,用户不知道的下载行为
3.广告程序、计算机病毒或任何其他能在用户机器上进行用户不知道它存在的隐式下载程序
引导性下载,可能会出现在用户访问一个网站,或者打开一个e-mail时,也可能因为他相信了虚构弹窗广告而点击了一个而产生。比方说,谋个弹出广告会说,一个计算机内部的错误被发现了,还有的可能弹出来一个看起来是无害的广告,但却带个关闭按钮。不论怎样,只要用户点击到了伪造的位置,内容提供者(比方浏览网页内容的IE)可能会认为是用户"同意做某事"了,于是内容提供商就会去下载一个虽然也许是不知道,并且还不想要甚至就是病毒性质的程序了。一个带有触发windows metafile漏洞的网页就,是一个这种引导下载的例子。
回到说ZeroAccess,它是有一些很强力的rootkit特征的:
*抗文件系统扫描的能力,它感染了关键的系统驱动(disk.sys, atapi.sys),还盗取了PIC驱动对象以及做了IRP Hooking
*感染了系统的设备驱动
*在内核模式中对用户模式进程做监听,并对其注入rootkit的DLL
*DLL的隐藏,过杀软能力
*面对修复感染行动时的绝对耐打
章节1: 对用户模式的下载者代理程序的反混淆与逆向分析
我们的Rootkit是一个被混淆了的程序,存在于我们面前的,是个通常名字为‘Max++ downloader install_2010.exe',加壳的可执行文件。我们分析的具体文件的hash值是:
MD5: d8f6566c5f9caa795204a40b3aaaafa2
SHA1: d0b7cd496387883b265d649e811641f743502c41
SHA256: d22425d964751152471cca7e8166cc9e03c1a4a2e8846f18b665bb3d350873db
对它的基本分析,显示出这个可执行文件有如下的PE区段和导入表:
Sections: .text .rdata .rsrc
Imports: COMCTL32.dll
这个导入表中只剩下些对我们分析很无力的信息。这在一般意义上就是说,我们的这个rootkit程序所必须要和不必须用的那些API函数,都是它在运行时才进行API导入的。我们现在到入口点观察下:
程序入口的代码,以一个函数的标准来说很标准,只是,除了一个特别的有意思指令除外,如你所见的,在00413BD5 我们遇到了一个出众的int 2dh指令.
int 2dh指令是windows内核模式调试机制的一部分,它用来提供个主动访问调试机制的接口。当一个int 2d被调用的时候,系统会创建出一个相应EXCEPTION_RECORD结构,并在里面填充异常代码为STATUS_BREAKPOINT,结构的其他部分则填充成具体异常时的线程环境。默认的对这个异常的处理,是调用KiDebugRoutine来完成的。
int 2dh是被ntoskrnl.exe用来中断给调试服务的指令,但在用户模式我们也是可以使用它的。如果我们试着去在正常情况使用它的话(没调试器附加上去时),我们肯定会得到一个标准的异常。然而,若附加了一个调试器上去的话,这个标准的异常就不会出现了。
(更多关于int 2dh的信息可以到此阅读 )
当int 2dh被调用到的时候,我们遇到了第一次ZeroAccess抗逆向分析的行为,也是它的一个花指令。系统在碰到这个中断时,会将程序的EIP加上一,直接越过中断位置后的一个字节,这就把中断后的那个指令的opcode给断开了。而这,会实质性的导致指令的执行,跟反汇编器里明白显示出来的指令流程不再一样了,也即调试器的反汇编的指令顺序成了错误的。
为了让它可以正常执行,我们需要一个能模拟int 2dh正确处理流程的方法,需要实现越过指令一字节,来允许我们跳转到被切开的那条指令的东西。为了这个,我们就要给OD加上StrongOD Olly了,你可以从这儿下载到它 (unpack.cn上的是最新版)
StrongOD安装好后,我们就可以在单步过int 2dh指令了,其后我们遇到了下面的流程:
这儿对我们而言最有意思的指令Call 00413bb4。就在它后面,我们看到了一堆新的垃圾指令,那就先进入这个call看看,你看到的会是下面的代码块:
再一次的,我们碰到了个int 2dh,因为它,我们要先拐到RETN指令其后的一个字节处好继续执行。在它后面的指令,作用是解密出周围的一片代码,往下再跟踪一段后,我们就到达了下图的地方:
上图的调用会解密出另一个代码块,在它执行完后,代码跳转到了下面的地方:
一个FS:[18],这地址对应的是线程的TEB结构(Thread Environment Block),程序找到TEB的目的,是为了获取到PEB(Process Environment Block)的地址,它保存在TEB的地址+ 0x30的地方。
而PEB+ 0xC 指向的元素,是个PPEB_LDR_DATA LdrData
要你是在用WinDBG做分析的话,你现在可以使用一个快捷技巧来得到在 结构->偏移->元素之间的具体关系,只需使用指令:
0:004> dt nt!_PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
+0×000 Length : Uint4B
+0×004 Initialized : UChar
+0×008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY
+0×014 InMemoryOrderModuleList : _LIST_ENTRY
+0x01c InInitializationOrderModuleList : _LIST_ENTRY
+0×024 EntryInProgress : Ptr32 Void
+0×028 ShutdownInProgress : UChar
+0x02c ShutdownThreadId : Ptr32 Void
如你所见的,上述的恶意代码找到了_PEB_LDR_DATA + 0x1C,通过对照WinDBG的输出你会发现ECX现在指向的是InInitializationOrderModuleList元素。而在其后的代码,负责的就是定位到导入表函数的地址,接着现在就像空中飞人一样构造出了一个程序所用的导入表。导入表完成后,又有了一片非常复杂之的一串串嵌套调用,很明晰的,是用作解压ZeroAccess关键代码的一层一层的东西。我们现在是不会去分析它的。因为这段代码是相当的长,还因为是用复制粘贴方式写出来的,它无比的无趣,最后还跟我们要分析的rootkit功能部分是没一点点关系的。
对导入表函数的地址加密做的很成功,只有在调用到具体函数的时候,那个函数的地址才会被临时的解压出来。让我们看一个API调用具体怎么样的:
Call 00401172负责解密API 地址,并把值返回在EAX中,后面的JMP EAX就是调用API了。在上面一个代码片段中,所调用的API是VirtualAlloc.这儿申请的内存,将被用来保存此感染代理程序之后要抖啊抖出来的几个指令块。那些被感染代理抖出来的块,最终要构成一个新可执行文件的。
现在,主模块构造好(主模块我们指的是此感染载体程序,我们刚才也叫它为感染代理)并且丢出了数个文件到受害机器的硬盘上,当然,它们在主模块的内存中也是保存着的。不管它在硬盘还是内存中,它们被调用到的顺序总是一样的:
继续,让我们试着判定出这段代码是在解压出些什么东西来。我们现在要在0040162B处下一个断点,它是上图Next Block跳转后的第一个指令。而Next Block代码块的结束,代表着代码解密过程已经执行结束了,执行到此,我们已经可以在程序申请到的内存中找到"MZ"标记了,这让我们确定解压出的可执行程序,已经是可以执行的地步了。在继续下一步分析前,我们建议你把完整的可执行程序dump到硬盘上,可以用Olly的备份功能实现.
你也可以直接从这儿下载: dumped.rar.
下面的代码块,被用一个VEH(Vectored Exception Handler )给保护起来了,它是由RtlAddVectoredExceptionHandler 和RtlRemoveVectoredExceptionHandler加上去和卸载的。跟进去,我们碰到的是一段真的很重要的代码块。它的功能都是调用未文档化的API实现的,包括用 LdrLoadDll载入lz32.dll,以及要创建一个Section Object.
Section Object代表的是,一个可以被进程间共享的内存区块,一个进程可以利用一个Section Object和其他的进程共享一片内存地址。而Section Object还提供了让进程可以把一个文件映射(map)到它的内存空间的能力。
注意,看一下上图红线圈起来的部分,程序在此执行进了一个储存在EAX中的地址003C24FB。如你所见,这地址是属于刚刚LdrLoadDll载入的那个lz32.dll的地址的。因为这个call,执行流程跃进了lz32.dll模块的地址空间内,但是事实上,包裹里面现在不是LZ32.dll,包的却是感染代理解压出的那段真正的核心恶意程序。 (#:这里面的代码,是在前面那个call里处理完成的。这儿它和lz32.dll的关系是不可共存关系。lz32.dll的实际作用是提供一个利用LdrLoadDll PE载入功能的机会而:程序用一个在ZwMapViewSection单步断点的断点断在LdrLoadDll里面,将lz32.dll载入过程时ZwMapViewSection的使用的地址修改成前面那个Section Object的地址,让操作系统本为lz32.dll准备的PE文件载入过程用在程序自己的PE文件上。最终,释放PE文件头所在的一个0x1000大小内存块完成这个看着就很无力的反调试流程)
新的程序,我们一跳过去就看到下面这个样子的代码了:
如果跟踪进 Call 003C23DB,我们会发现一个有超长的函数,这儿就是再下一步的感染过程。再遵循实际严格点的说,我们在这儿发现的是内核模块的安装过程。我们会看到一系列的新奇代码,为的是躲开那些经典杀软的拦截,包括对Section Objects的利用,还有把自己的东西放进入系统文件映射中等。
让我们检查下这整个感染代理的最核心过程吧。我们现在会一片片的把它给分析干净:
当分析一个复杂病毒的复杂片段时,把句柄窗口、模块窗口都打开在OD界面上是一个很好的习惯和练习。这可帮助你保持能跟紧在病毒载入、卸载模块,以及及时的发现它在打开一个文件/object/线程/等东西。现在,让我们来看看在003C2461处调用到了的Call 003C1C2C 中发生了些什么(在ExitProcess的后面)。
一进来,我们就看到对\system32\drivers目录中驱动的枚举,接着又有下面的一块代码:
在这儿碰到的是一个很有意思的算法。它在枚举了驱动程序之后,一个随机数也被制造出来了,把它挤进[0- 0xff]的范围中后,随机数被用来随机的从获取到的驱动列表中选择一个驱动出来。最后,还有一个字符串的格式化,字符串样子是:
\._driver_name_
让我们打开句柄窗口,看看现在发生了什么事:
如你可见的,一个根据随机选中的驱动来创建的Section Object冒出来了,在下面,这个Section就要被映射(View)到程序的内存空间了。
值得注意的是,这个Section访问权限的值被设置成了0xf001f。我们先说下为什么这个权限值得注意。在分析一个病毒的过程中,感觉上你就像在进行一个法医调查,做调查的一个基础,是要调查好对手访问到了几个部分,据此我们可以为我们的调查指明正确要投入力气的方向。逆向分析中的这种信息,可以通过检查变量们上所附的访问权限来获取的。
让我们找找0xf001f访问权限到底指的是什么,winnt.h中有:
#define SECTION_ALL_ACCESS 0xf001f
SECTION_ALL_ACCESS意味着,这句柄指向的SECTION_OBJECT中的数据能允许的行为已包括了读取、写入、查询还有执行。这个,可不就是最佳的恶意代码滋生地点了。让我们继续分析:
上述代码把之前选好的那个驱动,一起领着然后把它写入到注册表:
\registry\MACHINE\SYSTEM\CurrentControlSet\services\
在这个CurrentControlSet下面的\services 项里面,它包含了设备驱动、文件系统驱动、还有win32服务驱动的设置的信息。对于每一个服务,都要在这儿有一个对应于服务名称的子键。
我们的注册表键的名字会是字符串 \._driver_name_
我们的服务启动类型为0x3,意思是 -> Load on Demand
服务类型为0x1,意思是 -> Kernel Device Driver
可执行文件路径是 -> \*
事情总是这样的,现在驱动程序的文件被打开了,接着,它的文件句柄被穷尽怪异的使用了,这次是用ZwFsControlCode,用到的是一个FSCTL(File System Control Code文件系统控制码)。再看下运行时给它的参数,显示出来的FSCTL代码是9C040。这个代码的含义是FSCTL_SET_COMPRESSION。它为某个硬盘卷上面的一个文件或者一个目录设置个压缩标志位,当然,先需要对应的文件系统支持对文件和目录进行预操作的压缩功能。
在其后,一个新的可执行文件会被通过之前用到过解压方法给解压出来,并通过ZwLoadDriver来完成驱动的载入。(#在ZwLoadDriver之前,先用了个ZwCreateSymbolicLinkObject把驱动文件名"\*"和之前随机出的驱动做了硬连接了)。我们分析的样本中这个驱动载入过程会应用在二个设备驱动上面:
1. 第一个驱动是随机选中的驱动,它会完成IRP Hooking,和对某些Object,包括对disk.sys/pic.sys的Object的盗取(之后我们要比现在详细得多的对这个事情进行个分析)
2. 第二个驱动,叫做B48DADF8.sys,它是负责望风,警惕着进程创建行为的驱动,还包含着一个像小说写的一样精彩的DLL注入系统(之后我们也要对它进行一个详细得多的分析)
驱动的注入一完成,我们就发现了下面的代码:
在这儿,我们看到了名叫fmifs.dll的DLL。这个DLL是用来安装文件系统的格式化管理器,同时它还提供了一个完整的文件系统管理函数集合。
这一次被使用到的函数是 FormatEx。下面是有文档说明的FormatEx的一点点信息:
VOID
STDCALL
FormatEx(
PWCHAR DriveRoot,
DWORD MediaFlag,
PWCHAR Format,
PWCHAR Label,
BOOL QuickFormat,
DWORD ClusterSize,
PFMIFSCALLBACK Callback
);
这个函数,如它名字所示的是用来格式化硬盘卷的。在我们的情况中盘符号是 \\?\C2CAD972#4079#4fd3#A68D#AD34CC121074 ,而要格式化成的格式是NTFS。这个进行格式化的地方,就是这个让这个rootkit显得超群而卓尔不凡的地方。它调用fmifs.dll的API创建了一个隐藏的分区(盘符),这个分区将会用来保存rootkit的驱动还有ZeroAcess感染媒介所丢出来的那个DLL。即便是现在被感染了,这些恶意文件的存在也还是完全隐藏在受害者的面前,还是查不到它们的(something we teach in our ethical hacking course: http://resources.infosecinstitute.com/ethical-hacking-training/)
格式化了新区之后,感染代理接着要做的事情是生产新物品,使用和前面说过的相同的解压过程,其他的几个病毒程序也将被放进刚刚创建的那个隐藏分区中去。有两个文件:
*B48DADF8.sys
*max++.00,x86.dll
在最后,它们都会保存在隐藏盘符中,\\?\C2CAD972#4079#4fd3#A68D#AD34CC121074\L\。
我们分析到现在,在总体上对于ZeroAccess在用户模式的所作所为,已经有了一个很好的认识了,那就可以把我们的注意都投向内核模式那边了,为此,我们就要分析两个新的驱动文件和它丢出来的那个DLL。
让我们在接下来继续跟踪这个rootkit的工作流程。如果你是沿着我们的分析路线来逆向的,接下来的分析就应该根据代码执行的逻辑关系来进到感染代理丢出来的二进制文件了。我们的第一个随机名称的未命名驱动文件分析已经完成了,内容是在第二个章节中。
#我很不喜欢这样的乱补充,不过有一个地方自己跟的时候不注意的话真的很头疼。在进入那个病毒PE模块后,第二个call里设置进程的权限,然后注入到了一般是smss.exe的某个进程中,创建了一个SUSPEND的线程并且在配置好了堆栈后给它设置EIP启动了,具体是用ZwSetInformationFile来删除文件自身的。为什么提这个呢,为了把那二个驱动给复制出来,我运行了它好多次,每次都来删一下,7、8次后就给气的头皮发麻了。
----------------------------------------------------------------------------------------------------------------------
ZeroAccess分析的更多相关文章
- alias导致virtualenv异常的分析和解法
title: alias导致virtualenv异常的分析和解法 toc: true comments: true date: 2016-06-27 23:40:56 tags: [OS X, ZSH ...
- 火焰图分析openresty性能瓶颈
注:本文操作基于CentOS 系统 准备工作 用wget从https://sourceware.org/systemtap/ftp/releases/下载最新版的systemtap.tar.gz压缩包 ...
- 一起来玩echarts系列(一)------箱线图的分析与绘制
一.箱线图 Box-plot 箱线图一般被用作显示数据分散情况.具体是计算一组数据的中位数.25%分位数.75%分位数.上边界.下边界,来将数据从大到小排列,直观展示数据整体的分布情况. 大部分正常数 ...
- 应用工具 .NET Portability Analyzer 分析迁移dotnet core
大多数开发人员更喜欢一次性编写好业务逻辑代码,以后再重用这些代码.与构建不同的应用以面向多个平台相比,这种方法更加容易.如果您创建与 .NET Core 兼容的.NET 标准库,那么现在比以往任何时候 ...
- UWP中新加的数据绑定方式x:Bind分析总结
UWP中新加的数据绑定方式x:Bind分析总结 0x00 UWP中的x:Bind 由之前有过WPF开发经验,所以在学习UWP的时候直接省略了XAML.数据绑定等几个看着十分眼熟的主题.学习过程中倒是也 ...
- 查看w3wp进程占用的内存及.NET内存泄露,死锁分析
一 基础知识 在分析之前,先上一张图: 从上面可以看到,这个w3wp进程占用了376M内存,启动了54个线程. 在使用windbg查看之前,看到的进程含有 *32 字样,意思是在64位机器上已32位方 ...
- ZIP压缩算法详细分析及解压实例解释
最近自己实现了一个ZIP压缩数据的解压程序,觉得有必要把ZIP压缩格式进行一下详细总结,数据压缩是一门通信原理和计算机科学都会涉及到的学科,在通信原理中,一般称为信源编码,在计算机科学里,一般称为数据 ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
随机推荐
- c++ string去除左右空格
res.substr(res.find_first_not_of(' '),res.find_last_not_of(' ') + 1)
- linux运维、架构之路-nfs网络文件系统
一.nfs介绍 NFS是Network File System的缩写,是网络文件系统,它的主要功能是通过网络(一般是局域网)让不同的主机系统之间可以共享文件或目录,主要存储用户上传的图片附件等信息. ...
- UITableView和MJReFresh结合使用问题记录
1. 代码主动调用下拉刷新, [self.tableView.mj_header beginRefreshing]; 调用会走: [MJRefreshNormalHeader headerWithRe ...
- mysql FOREIGN KEY约束 语法
mysql FOREIGN KEY约束 语法 作用:一个表中的 FOREIGN KEY 指向另一个表中的 PRIMARY KEY. DD马达 说明:FOREIGN KEY 约束用于预防破坏表之间连接的 ...
- 货币系统 Money Systems
母牛们不但创建了它们自己的政府而且选择了建立了自己的货币系统.由于它们特殊的思考方式,它们对货币的数值感到好奇. 传统地,一个货币系统是由1,,, 或 ,, 和 100的单位面值组成的. 母牛想知道有 ...
- php日志托管给apache处理
php.ini配置: log_errors = On;不显示错误display_startup_errors = Offdisplay_errors = Off ;除了notice级别错误外,报告所有 ...
- 安装memcached和elasticsearch服务并systemctl管理
[root@izbp18dv3a3metugyd02qxz bin]# rpm -qa | grep memcache [root@izbp18dv3a3metugyd02qxz bin]# yum ...
- php开发IDE选择
优先选择Netbeans,理由如下:: 1.ZendStudio有的方便特性Netbeans也提供,如:ctrl+f5也支持ctrl+shift+r的文件选择功能,[git | svn]团队代码管理. ...
- lambda匿名函数和内置函数
对于简单的函数,也存在一种简便的表示方式,即:lambda表达式 定义函数(普通方式) def func(arg): return arg + 1 执行函数 result = fun ...
- ionic框架+angular开发项目
ionic框架组件地址:https://ionicframework.com/docs/api/tab ionic文档地址:https://ionicframework.com/docs/angula ...