您的位置 首页 > 爱车一族

奔驰cls320同价位车型aq

WannaCry 打单软件是2017年最热点的打单软件,它应用微软破绽在环球范围内提议的进击令世界上100多个国度的不计其数的用户深受影响。俨然一场环球范围内的网络平安普及教育。

作为一位平安行业工作者,我对WannaCry停止了深度的阐发。 完备的阐发可以或许赞助平安行业工作者懂得黑客的的最新进击手腕,为防护范畴的办理方案供应有价值的信息,更进一步测验考试发明软件缺点,为客户供应有价值的数据恢复办事。

我以前对CryptXXX,Cerber和Locky这几种高热点打单软件停止过深入阐发,Wannacry这款打单软件显著汲取了以前打单软件的计划履历,并退化进级出了一个二阶段进击办法。

甚么是二阶段进击呢?简略来讲,便是歹意法式自己并无进击行动,好比Wannacry法式自己并不做数据加密的工作,它会将真正具备进击行动的代码静态开释出来,将数据加密的行动暗藏到开释出来的代码中。 这听起来相似壳的行动,好比Locky便是一款带壳的打单软件,它运转起来后会经由过程壳代码开释歹意代码。 但壳代码自己也是一个很显著的特性,好比加过壳的代码无奈停止反汇编,而且同样平常情况下信息熵很高,许多防护软件会对这类法式进步风险品级,用户一旦碰着,或许这类法式一旦Drop到用户的机械上就会被Detect到。 然则Wannacry分歧,它的第一阶段法式完备没有任何壳代码,EntryPoint也没有做任何的定制,没有任何觉得改动的陈迹,大概这也是Wannacry会在应用微软破绽大规模被流传到Endpoint上没有被发明的缘故原由吧。

Hack Weapon

在开端阐发以前,想先讲一下Hack Weapon这个观点,依据许多业余机构的阐发,这条玄色产业链已经成长的异常成熟了。 在暗网,你可以或许买到林林总总的进击组件,构成一个有用的进击实体;比方,有破绽,有加载器,有加密模块等,那末我可以或许依据我的必要去买适合的模块,而后在停止组装成一个真正具备进击性的歹意法式。此中这些模块便是Hack Weapon,咱们网络到的许多Sample都是许多模块的组合,那经由过程in-depth的逆向阐发后发清楚明了一款歹意软件中存在有多种进击兵器的时候可否将这些进击兵器提取出来,零丁树立一个Hack Weapon并对这些Hack Weapon停止特性建模,行动建模来完美咱们的平安产物呢?

从技巧的角度来讲,Machine Instruction级其余复用是相对同等性的,并不像源代码级其余复用会由于编译器选项的欠亨招致终极Machine Instruction产生纤细变更。黑客兵器的供应者,应当不会供应源代码,而是将兵器作为binary供应给下流黑客,以是树立这类兵器库,供应了很强的特性性。 而从兵器的角度去婚配歹意法式具备更精确的辨别才能,好比说我觉得一款隐藏的Peloader模块属于歹意软件的一种,那末针对这类Weapon停止特性或行动建模将更能针对应用了这款Weapon的一切歹意软件,会是的平安防护软件具备袭击多点的才能。

我已经抽取过一款针对法式中String停止加密,并在一直以前停止解密的Hack Weapon,其时我并无想到歹意软件会有模块化这类计划,直到在别的一款歹意软件中见到异样的行动,而且停止抽取发明全体function的flow乃至Instruction都完备同等。

此次在阐发的过程当中,我至多已经可以或许辨认出两种Hack Weapon: PeLoader 和 Resource Extractor,前者用于加载一个PE file到内存中,而且在不挪用微软API(不精确,少少挪用,由于分派内存还必要挪用VirtualAlloc)的情况下办理Import Table,Base Relocation等超费事的事变,以至于应用Monitor等法式都发明不了他有一个LoadDll的行动;后者用于从Resource中提取一个加密过的进击用的PE payload,这两个模块都异常的精美。 我想就算是作者也没有从新写一遍的动机,这必要停止大批细心的编码和大批的测试,由于轻微一些忽略就会招致末了的进击功败垂成(指的是加载Payload失败)。

在此次阐发停止后,我会将这两个Hack Weapon抽取出来,并写一个POC代码,复用这两个组件。

phase one

tasksche

Wannacry的主法式会被改动会,起这个名字的目标是为了困惑用户。 上面咱们来一起来阐发一下这个法式,从WinMain函数开端。

为了轻易阐明法式的行动,如下的代码片断将全体应用C++说话来表述,除非碰着C++表白凌乱或许因编译器优化等缘故原由招致C++无奈表白的情况下,会采纳汇编说话并加详细阐明。

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){ char **argv; void *lpPEFile; CPeBuilder *pPeBuilder; void (__stdcall *fpTaskStart)(_DWORD, _DWORD); CProgram Program; char szModuleFileName[520]; int nFileSize; szModuleFileName[0] = szSelfName; memset(&szModuleFileName[1], 0, 516u); *&szModuleFileName[517] = 0; szModuleFileName[519] = 0; GetModuleFileNameA(0, szModuleFileName, 520u); CreateRandomSequence(szServiceName); if ( *_p___argc() != 2 || (argv = _p___argv(), strcmp(*(*argv + 1), aI)) || !CreateHiddenData(0) || (CopyFileA(szModuleFileName, FileName, 0), GetFileAttributesA(FileName) == INVALID_FILE_ATTRIBUTES) || !StartMalware() ) { if ( strrchr(szModuleFileName, '\\') ) *strrchr(szModuleFileName, '\\') = 0; SetCurrentDirectoryA(szModuleFileName); WriteRegistery(1); ExtractFromResource(0, WNcry); ModifyOneByte(); StartProcess(CommandLine, 0, 0); // attrib +h : Sets the hidden file attribute. StartProcess(aIcacls_GrantEv, 0, 0); if ( InitKernel32Funcs() ) { CProgram::ctor(&Program); if ( CProgram::Initialize(&Program, 0, 0, 0) ) { nFileSize = 0; lpPEFile = CProgram::GetPeFile(&Program, aT_wnry, &nFileSize); if ( lpPEFile ) { pPeBuilder = WncryLoadPE(lpPEFile, nFileSize); if ( pPeBuilder ) { fpTaskStart = WncrySeek2TaskStart(pPeBuilder, szTaskStart); if ( fpTaskStart ) fpTaskStart(0, 0); } } } CProgram::dtor_0(&Program); } } return 0; }

WinMain的代码很轻易理解,声清楚明了几个变量,此中CProgram对象,CPeBuilder指针,和fpTaskStart是全体WinMain运转的症结,WinMain的目标是静态加载一个Pe dll到内存中并运转起来,全体过程做的相称的隐藏。 WinMain函数在栈上声清楚明了一个520 byte的数组,用来获得以后过程的完备门路,法式会应用这个门路将文件复制一份并命名为,并挪用StartMalware启动自己。

在这里Malware已经经由过程破绽进入Endpoint中而且被launch起来了,我觉得这里存在一个common的阶段,我把这个阶段界说为暗藏阶段。作甚暗藏阶段,换句话说在这个阶段Malware起首要做的不是履行进击代码,而是在实行进击以前起首藏匿自己的行迹,假装自己为一个看起来失常的法式,并在日后的日记查询拜访中困惑阐发职员。

if ( *_p___argc() != 2|| (argv = _p___argv(), strcmp(*(*argv + 1), aI))

细心看一下这段代码,Wannacry启动后的第一时间停止了几个症结操纵:

反省启动参数

创立暗藏数据(folder)

重命名为

再次启动自己

反省启动参数与重命名可以或许间接从表白式中断定出来,不复杂。

创立暗藏数据

int __cdecl CreateHiddenData(wchar_t *p){ int result;

上面的函数终极创立了**%WinDir%\ProgramData,%WinDir%\Intel**两个folder并将其属性设置为Hidden。

int __cdecl CreateFolder(LPCWSTR lpPathName, LPCWSTR lpFileName, wchar_t *String){ int result;

上面是CreateFolder的代码,很轻易理解,重要是对经由过程此函数创立的Folder设置Hidden属性。

再次启动自己

BOOL StartMalware(){ char szFullPath[520];

留意末了一行的代码,Wannacry会用2中种方法再次启动自己,起首测验考试将自己假装成办事法式,假如失败了,在测验考试通用的过程方法。

办事方法

在应用办事方法启动的时候,Wannacry起首会应用在WinMain中初始化的一个随机序列作为办事称号。 上面是天生随机序列的代码:

int __cdecl CreateRandomSequence(char *displayname)

这段代码应用了用户的计算机称号,经由过程对计算机称号的每个自己停止乘法溢出运算,会获得一个初始化随机产生器的种子值,并应用随机数产生器来天生随机的办事称号。 这个随机的办事称号由’a-z”0-9′构成。

signed int __cdecl StartService(char *pFullpath){ signed int result;

Wannacry应用StartService函数将自己作为一个带有随机办事称号的办事停止启动,这是最轻易掩人耳目标隐藏手腕。 然则假如没有权限操纵办事管理器,或许不测失败了,Wannacry另有惯例启动办法作为备选。

int __cdecl StartProcess(LPSTR lpCommandLine, DWORD dwMilliseconds, LPDWORD lpExitCode){ int result;

StartProcess函数作为备选应用惯例方法启动一个过程,这里被传入的CommandLine便是已经被悛改称号为的法式门路了,至多用这个称号这也有一些困惑感化,而且一个小细节,设置了非窗口形式。

signed int __cdecl WaitMutex4Times(int param){ int nCount;

不管应用哪一种方法再次启动了自己,法式都邑挪用上面的函数去期待Global\MsWinZonesCacheCounterMutexA0这个体系Mutex,而且在接下来测验考试期待60次,每次1秒,合计60秒。 这么看来这个Named的Mutex对象应当在真正提议合计的模块中,这是惯例的假定。

固然假如上面的办法都失败了,Wannacry也不会废弃这独一的一次机遇加密用户的数据,既然暗藏不可,那就间接干吧。 而间接干这部分代码与暗藏后再来干的代码是同样的,恰好归并阐发。 在开端以前另有一些工作要做:

WriteRegistry(1); ExtractFromResource(0, WNcry); SetBitcoinAddress(); StartProcess(CommandLine, 0, 0); // attrib +h : Sets the hidden file attribute. StartProcess(aIcacls_GrantEv, 0, 0); // icacls . /grant Everyone:F /T /C /Qif ( InitKernel32Funcs() )

写注册表

提取Payload

将Bitcoin钱包地点写入c.wnry

将以后工作目次暗藏

将以后目次受权一切人完备拜访权限

初始化体系挪用

这表面暗藏工作目次,提权,和初始化体系挪用都没有甚么好说的,剩下的几个操纵中,最难的是ExtractFromResource,它从资本段中开释Payload。

写注册表

signed int __cdecl WriteRegistry(int flag){ size_t nlen;

Wannacry应用WriteRegistryFunc将在”HKLM\Software\WanaCrypt0r”和”HKCU\Software\WanaCrypt0r”上面创立一个wd的键并写入以后的工作目次,咱们先假定它这么做是为了避免把自己也加密了吧。

WINDBG>du 0012f71c0012f71c "Software\WanaCrypt0r"

提取Payload

以前提到的法式逻辑都很简略,从这里开端今后就开端有意思了,阐发这些代码照样很难的,同时也异常有意思。 起首来看看 Payload Extractor的完成。

Wannacry在自己的res segment中存放了许多加密过的Data,这表面有许多有歹意的法式,要想懂得它们都是做甚么的,咱们必要先懂得它的内存结构。

提取函数起首将资本数据加载到内存中:

hRes = FindResourceA(hModule, (LPCSTR)'\b\n', aXIA);if ( hRes

以后挪用StartVersion函数初始化一个CResource对象,这个对象会将load出来的Resource数据加载出来,之以是用这个函数称号,是认为它另有别的版本的加载办法,此中一个版本是经由过程一个文件句柄来加载Payload,也便是说这个Payload Extractor同时也支撑从文件中提取Payload。

struct CResource{

CResource如上面结构所示,有一些数据至今我还不清楚是做甚么用的,然则最紧张的数据结构是一个名为CWnBigResData的数据结构,这个数据结构的前0×20是CWnResData它包括了Payload的一些紧张信息。 别的pSignture是这个对象的一个特性在这里是表面传入的数据“WNcry@20l7”。

CWnResult *__cdecl StartVersion3(HANDLE hRes, int nSize, int version, char *WNcry@2ol7){

StartVersion3是被StartVersion挪用的,而且传入了3给version这个参数,由于这个参数的感化,法式会从Memory中读取Payload而不是文件句柄。 这个函数创立了CResource对象并挪用了它的Initialize办法停止初始化。 初始化Payload的成果会记载在全局变量g_StatusCode中,假如初始化失败,会析构并加入,同时法式将无奈胜利。假如胜利会创立一个CResult对象,这个对象备两个数据成员,bLoadSuccess用来表现初始化胜利,pResource指针指向CResource对象。

pWnResData->bIsFile = 0;

上面的代码片断是当version参数即是3时候的行动,有两个紧张的数据pWnResData->lpResourceData被赋值为指向资本段的指针,pWnResData->nResourceSize是资本段的巨细值。

最紧张的一个函数,来自于GetDataFromResource,这个办法真正的从Resource中加载数据到内存数据结构中。

struct CWnResData{ char bIsFile;

这里的风趣的是,针对ResourceData的load是从后往前读的,这与惯例的数据加载从Memory低往高读取的方法是反的,可以或许看出作者这么做真是用心良苦。

34:961Fh: 50 4B 05 06 00 00 00 00 24 00 24 00 D8 0D 00 00 PK......$.$.Ø...

上面的数据是我从Resource中提取出来的原始数据的末了22字节,经由过程对末了22个字节的阐发,我将展现法式若何说明这类自界说协定的数据格局。

signed int __cdecl FindPkSignPosition(CWnResData *pResData){

FindPkSignPosition函数的感化是将pResourceBuffer指向末了一个PK0506的标志,上面的函数中有两个ResetCurrentOffset函数,两次挪用中末了一个参数一次是2一次是0, 2代表指针以后地位是Buffer的末端,0代表指针的值是一个绝为地位。以是第一挪历时,将指针的地位移动了Buffer的末端,第二次挪历时指针已经指向了Buffer末端,Offset的偏移即是一个Block的巨细1028个自己。 以是这两次操纵后,指针实际上即是从Buffer末端往前偏移了1028字节,以是指针的以后地位即是Buffer总巨细减去一个Block的巨细。

pBuffer = pBuffer + nTotalSize - nBlock

定位完指针后,法式读取了一个Block巨细的数据进了BlockBuffer中。

if ( pBuffer[index] == 'P' && pBuffer[index + 1] == 'K' && pBuffer[index + 2] == 5 && pBuffer[index + 3] == 6 )

留意这个代码片断,法式开端从这个Block的末了一个字节反向搜刮‘PK56’这个特别的标志值。 它会不停读取直到读到我在上面贴出来的数据片断为止。

察看上面的数据,法式会继承读取,将这些数据写入内存数据结构中,读取办法有2种,一次读取2个字节和一次读取4个字节。就我列出的数据,数据结构如下:

Magic: 504B0506Reserved1: 0Reserved2: 0Unknown1: 24Unknown1: 24Current segment offset: 00000dd8

此中前一个段的相对地位加上以后端的偏移即是以后段的相对地位,假如测验考试将这两个值相加,会发明即是我上面贴出的数据的以后地位,是以经由过程这个办法咱们就能够或许赓续的定位到前一个段,不停遍历到Memory的开首。

由于Payload Extractor的代码异常多,它必要将一切段中的数据都提取出来,而且经由过程写文件的办法开释出来,以是就不在这里继承阐发了,假如感兴趣,经由过程我上面的启迪应当可以或许自己测验考试的阐发代码将一切的数据提取出来。

PS. 咱们在法式运转过程当中看到过的 c.wnry u.wnry 等都是在这个阶段开释出来的。

DropFileCategoryUsageData
c.wnryTextC&C Server; Bitcoin addressgx7ekbenv2riucmf.onion;
r.wnryTextQ & AQ: What’s wrong with my files?
s.wnryPK FormatNone
t.wnryEncryptFileAttack PayloadNone
u.wnryPEUI InterfaceWana Decrypt0r 2.0

写Bitcoin钱包地点

这个过程比拟简略,起首有3个bitcoin的钱包地点,随机抉择一个,写到buffer的第178字节开端的后35字节中,而后把buffer写到c.wnry中。

int SetBitcoinAddress(){ int result; int iRandom;

PeLoader

到这里Payload也已经胜利的提取出来了,那咱们以前说过Wannacry是分两阶段进击的,第一阶段另有一个最紧张的义务便是将加密法式悄无声息的加载起来。 如今到了激动人心的时候,经由过程接下来的阐发,咱们可以或许控制PeLoader的运转道理。 作为第二阶段进击的最紧张的Weapon,Peloader将忽略微软API,来完成Pe dll的静态加载。

假如不想翻到最上面看的话,这里有一个代码片断:

CProgram::ctor(&Program);if ( CProgram::Initialize(&Program, 0, 0, 0) )

这表面有两个数据结构比拟紧张CProgram和CPeBuilder,先来看一下CProgram:

class CProgram{ void *vtable;struct CWnCryptContext{ void *vtable;

法式起首结构一个CProgram对象,并初始化外部的CWnCryptContext与CWnAES成员,此中微软的CWnCryptContext应用的CSP是“Microsoft Enhanced RSA and AES Cryptographic Provider”算法是RSA,RSA秘钥是从法式的数据段中读出来的。

void *__thiscall CProgram::GetPeFile(CProgram *this, LPCSTR lpFileName, int *nRet)

上面的CProgram::GetPeFile函数应用法式数据段中的PublicKey对t.wnry中的一段数据停止解密,这段数据解密出来后是AES初始化向量,而后用它去初始化AES加密Key,以后继承读取将用这个AESKey加密的PE文件读取到内存中,并用初始化后的AESKey停止解密,这个函数前往一个在内存中的PE文件,接下来就轮到PeBuilder上场了。

经由过程这部分,咱们可以或许懂得一个被加密的文件的加密结构,经由过程前面没有提到的二阶段进击阐发,我发明纵然是用户被加密的文件的加密后格局也与这个文件同等。

OffsetSummaryData
0×0000:0×0007MagicCode“WANACRY!”
0×0008:0x000BSize of Encrypt AES Vector
0x000C:0x010BBody of Encrypt AES Vector
0x010C:0x011FBlank 4 bytes
0×0110:0×0117File Size
0×0118:Numble of sizeFile Content

PeBuilder

适才Wannacry已经将在ResourceData中的t.wnry开释出来,并经由过程上面的解密举措胜利在内存中搁置了一个PE File,如今可以或许应用PeBuilder停止静态加载了。 PeBuilder异常的精美,也很繁琐。

CPeBuilder *__cdecl BuildPEExecutable(DOS_Header *fileBuffer, size_t nFileSize, LPVOID (__cdecl *VirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD, int), int (__cdecl *VirtualFree)(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType, int), HMODULE (__cdecl *LoadLibraryA)(LPCSTR lpLibFileName), int (__cdecl *GetProcAddress)(HMODULE, LPCSTR, DWORD), BOOL (__cdecl *FreeLibrary)(HMODULE hLibModule), int zero)

我信任纵然是翻译成C++代码,看到上面的大篇幅代码也没有读下去的能源了,况且我看的是汇编级其余代码。 那我简略讲一下好了,这个函数会前往一个PeBuilder对象,它会办理加载这个pe对象的一切体系级其余工作。

!WncryBuildPESection(fileBuffer, nFileSize, pPEHead, pPeBuilder))

上面是一些紧张的片断,假如你没有细心读上面大片的代码,那末看看这个也是可以或许的。

这个PeBuilder凶猛的地方在于,只应用了如下5个体系API,是以纵然应用Monitor对象也无奈发明有一个Loaddll的举措在表面。

VirtualAlloc

VirtualFree

LoadLibraryA

GetProcAddress

FreeLibrary

固然有LoadLibrary在然则貌似没有应用。

CPeBuilder的数据结构如下:

struct CPeBuilder{

此中ImageBase指向一块内存中的Buffer,它是一个被加载起来的Dll模块的基地点。

获得了CPeBuilder对象后,Wannacry应用Seek2TaskStart办法来定位到Dll中导出的TaskStart函数,并挪用这个函数,真正开端第二阶段的进击。

int __cdecl Seek2TaskStart(CPeBuilder *pPeBuilder, char *szTaskStart)

上面这个函数接收CPeBuilder和一个Func Name作为参数,法式在PE的Export中搜刮名为FuncName的导出函数,并前往这个导出函数。

while ( stricmp(szTaskStart, ImageBase + *AddressOfNames) )

这部分代码便是在搜刮导出函数表。

fpTaskStart = Seek2TaskStart(pPeBuilder, szTaskStart); if ( fpTaskStart ) fpTaskStart(0, 0);

挪用导出函数,进击开端。

至此,tasksche悄无声息的将一个PE dll加载并运转,全体过程无奈经由过程Proc monitor等软件发明。本日阐发的是Wannacry中的进击主体,它是已经落到用户机械上的payload,下次我会给人人带来Peloader的阐发,和详细加密行动的详细阐发。

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“奔驰cls320同价位车型aq”边界阅读