内容:
分析病毒结构,写出病毒如何利用漏洞进行攻击,详细剖析勒索病毒的运行过程,使用了什么加密算法,调用了什么系统API。
进阶:
中了该勒索病毒,怎么恢复数据
样本分析
首先是看下病毒样本的哈希值
图片.png
查壳信息,win32程序无壳:
图片.png
载入IDA后,先查看字符串,看到有RSA和AES,猜测之后会使用这两种加密方式:
图片.png
用IDA的findcrypto插件,找到了AES算法的特征:
图片.png
第一部分
之后开始对病毒样本进行详细分析,首先是程序入口点:
图片.png一开始病毒获取它的运行绝对路径,用OD动态调试时可得:
图片.png在函数 sub_401225中,会根据当前的计算机名随机生成一组字符串,后面会用到
图片.pngOD动调结果:
图片.png如果参数有/i,就把病毒自身复制到之前随机字符串的文件夹中,名称为tasksche.exe,之后去运行这个,但是我没用命令行去运行,所以直接就跳到下一步。之后将路径切换到了当前的文件夹下,在sub_4010FD里面会添加一条新的注册表项
图片.png 图片.png但是在OD调试的时候发现在注册表中没有这一项,跟到函数里面,发现在执行完RegSetValueExA退出了,怀疑是权限问题导致注册表没能成功添加
图片.png分析下在sub_401DAB里面进行的操作,主要对资源文件进行获取和释放到当前文件夹,用到了FindResourceA,LoadResource等对资源操作的函数,下图的Type是XIA,可以通过Resource Hacker来找到程序释放的资源
图片.png找到名字叫做XIA的资源段,里面一看是PK头,意识到是一个zip压缩包,直接用foremost从程序中提取出zip压缩包,密码是压到栈里的参数WNcry@2ol7
图片.png配合OD,执行完释放资源的函数后,文件夹中的内容多了一堆,应该都是之后病毒要使用的文件,十六进制编辑器打开后发现有PE文件,压缩包,被加密的文件等
图片.png之后执行两条命令
attrib +h .
icacls . /grant Everyone:F /T /C /Q
图片.png这两条命令搜了一下用法,
attrib为批处理文件命令 具体解释如下:- 清除属性。 R 只读文件属性。 A 存档文件属性。 S 系统文件属性。 H 隐藏文件属性。
这个地方命令用错了,对当前文件下文件隐藏不需要加点,直接attrib +h就可以,这里意思是想把这些病毒使用的文件全部隐藏,结果没什么用
图片.png第二条指令:
icacls
Intergrity Control Access Control List: 完整性权限控制列表 Windows系统下控制文件及文件夹的访问权限的命令行指令,相当于Linux中的chmod 原命令cacls已经被废弃。
MS documentation: 1, F = Full Control
• 777
2, CI = Container Inherit - This flag indicates that subordinate containers will inherit this ACE.
• 子文件夹继承父文件夹权限
3, OI = Object Inherit - This flag indicates that subordinate files will inherit the ACE.
• 子文件继承父文件夹权限
4, /T = Apply recursively to existing files and sub-folders. (OI and CI only apply to new files and sub-folders).
• 递归传递权限
在函数sub_4014A6里,对之前解压的t.cnry进行解压,并加载到内存里,在内存可以看到PE结构的文件,直接用PEload转存出一份内存文件,再用foremost提取出一个PE文件:
图片.png之后进入函数sub_4021BD回去判断是一个PE文件,在堆上申请了空间,并把这个PE文件加载到堆上
图片.pngwcry.exe的后半部分的流程如下图:
图片.png在提取出的dll文件中,打开导出函数可以看到TaskStart,说明在v8处执行了这个函数
图片.png之后OD运行到call eax,进入了dll的函数,整个病毒就开始进行加密了
图片.png可以发现wcry.exe其实并没有进行文件加密等病毒操作,大部分功能都是在释放资源和分配空间,直到开始运行释放出来的dll中的导出函数才会开始加密,所以这里直接把这个调用TaskStart的call给nop掉就不会中毒,因为wcry.exe的功能已经分析完了,测试了下想法,修改完保存二进制程序,此时程序就只是释放所有资源文件,不会加密文件
图片.png这部分的API函数总结:
1.GetModuleFileNameA
函数功能:获取当前运行程序的绝对路径
DWORD GetModuleFileNameA(
HMDULE hModule, //所需路径的模块的句柄。如果该参数为NULL,函数会获取当前进程的运行文件(.exe文件)的全路径。
LPSTR lpFilename, //一个指向接收存储模块的全路径的缓冲区的指针。如果路径的长度小于nSize参数定义的长度,返回路径为一个结尾为空终止('\0')的字符串;如果路径的长度超过nSize参数所定义的长度,字符串会截断到nSize个字符的长度,并在最后及nSize-1的位置包含一个终止符('\0')。
DWORD nSize //lpFilename缓冲区的长度。
); //如果函数运行成功,返回值为字符串的长度。如果字符串的长度大于nSize字节,返回值为nSize。如果函数运行失败,返回值为0。
2.GetComputerNameW
函数功能:获取计算机名
BOOL GetComputerNameA(
LPSTR lpBuffer, //指向接收计算机名称或群集虚拟服务器名称的缓冲区的指针。缓冲区大小应足够大,以包含MAX_COMPUTERNAME_LENGTH + 1个字符。
LPDWORD nSize //在输入时,在TCHAR中指定缓冲区的大小。在输出时,复制到目标缓冲区的TCHAR数,不包括终止空字符。如果缓冲区太小,则函数失败,GetLastError返回ERROR_BUFFER_OVERFLOW。所述lpnSize参数指定缓冲器的所需要的大小,包括终止空字符。
);
3.MultiByteToWideChar
函数功能:该函数映射一个字符串到一个宽字符(unicode)的字符串。由该函数映射的字符串没必要是多字节字符组。
函数原型:
int MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int cchMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
);
4.GetWindowsDirectory
函数功能:获取Windows目录的完整路径名
UINT WINAPI GetWindowsDirectory(__out LPTSTR lpBuffer, __in UINT uSize);
lpBuffer [out]
一个缓冲区,是一个接收路径的指针。这条路径并没有结束,除非以反斜杠Windows目录是根目录。例如,如果Windows目录被命名为Windows的C驱动器上的Windows目录的路径检索这个函数是C:\窗口。如果系统在驱动器C的根目录,检索安装路径是C:\。
lpBuffer String,指定一个字串缓冲区,用于装载Windows目录名。除非是根目录,否则目录中不会有一个中止用的“\”字符
uSize [in]
至于由lpBuffer参数指定的缓冲区的最大尺寸,在TCHARs。此值应设置为MAX_PATH。
nSize Long,lpBuffer字串的最大长度
5.GetFileAttributes
函数功能:为一个指定的文件或目录返回文件系统的属性。可以使用GetFileAttributesEx 函数获得更多的属性信息。如果要实现交互式操作,可以使用GetFileAttributesTransacted 函数。
DWORD WINAPI GetFileAttributes(
__in LPCTSTR lpFileName
);
参数
lpFileName [in]
文件或目录的名字,对于ANSI版本,名字不能大于MAX_PATH。
返回值
如果函数成功,返回值包含文件或目录的属性。如果函数失败,返回值是INVALID_FILE_ATTRIBUTES。
SetFileAttributes
函数功能:设置文件属性
SetFileAttributes(文件名, FILE_ATTRIBUTE_READONLY); // 设定为只读
SetFileAttributes(文件名, FILE_ATTRIBUTE_HIDDEN );//设定为隐藏
SetFileAttributes(文件名, FILE_ATTRIBUTE_SYSTEM);//设定为系统
SetFileAttributes(文件名, FILE_ATTRIBUTE_ARCHIVE);//设定为保存
SetFileAttributes(文件名, FILE_ATTRIBUTE_NORMAL);//设定为一般 (取消前四种属性)
设定二种以上的属性:
设定为只读 + 隐藏
SetFileAttributes(文件名, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
设定为只读 + 隐藏 + 系统 + 保存
SetFileAttributes(文件名, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN _
| FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
取消所有设定
SetFileAttributes(文件名, FILE_ATTRIBUTE_NORMAL);
6.GetTempPathW
函数功能:获取为临时文件指定的路径
DWORD WINAPI GetTempPath(
__in DWORD nBufferLength,
__out LPTSTR lpBuffer
);
nBufferLength:指lpbuffer所占的空间,以TCHAR为单位。
(TCHAR:当UNICODE模式下为wchar_t,非UNICODE模式下为char。)
lpBuffer:保存找的文件夹路径,该文件路径以‘\’结束,例如“C:\temp\”。
其返回值为一个dword类型,当函数执行成功时返回文件夹路径长度,以TCHAR为单位,不包括结束字符在内。如果返回的路径长度大于nbufferlength,那么按返回值保存路径。
7.CreateDirectoryW
函数功能:CreateDirectory这个函数的作用是创建一个新的目录。如果底层文件系统支持文件和目录上的安全描述,该功能可将指定的安全描述到新的目录。
函数原型:
BOOL CreateDirectory(
LPCTSTR lpPathName,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
参数:
pPathName:长指针,指向一个空结束的字符串,该字符串指定要创建的目录的路径。有一个默认的字符串大小限制为MAX_PATH字符的路径。此限制是关系到这个函数是如何解析路径。字符串的长度不超过MAX_PATH。
lpSecurityAttributes:忽略,一般设置为NULL。
返回值:
非零表示成功,零表示失败。若想获得更多的错误信息,调用GetLastError函数。
备注:
这个函数不是递归的。它可以在一个路径中创建唯一的最终目录。也就是说,如果父目录或中间目录不存在,该函数将失败并显示错误消息ERROR_PATH_NOT_FOUND。
8.SetCurrentDirectory
函数功能:设置当前路径,然后就可以以相对路径访问一些你程序相关的文件,不需要绝对路径
TCHAR chCurDir[MAX_PATH] = {0};
GetCurrentDirectory(MAX_PATH, chCurDir);
SetCurrentDirectory(_T("E:\\test\\"));
m_hDLL = LoadLibrary(_T("MyTest.dll"));
SetCurrntDirectory(chCurDir);
9.CopyFileA
函数功能:复制文件
BOOL CopyFile(
LPCTSTR lpExistingFileName, // pointer to name of an existing file
LPCTSTR lpNewFileName, // pointer to filename to copy to
BOOL bFailIfExists // flag for operation if file exists
);
其中各参数的意义:
LPCTSTR lpExistingFileName, // 你要拷贝的源文件名
LPCTSTR lpNewFileName, // 你要拷贝的目标文件名
BOOL bFailIfExists // 如果目标已经存在,不拷贝(True)并返回False,覆盖目标(false)
如:
//拷贝文件c:\log.txt到d:\log.txt,如果D:\log.txt已经存在,就覆盖
CopyFile("c:\\log.txt","d:\\log.txt",false);
10.GetFullPathName
函数功能:获取指定文件的完整路径名
DWORD WINAPI GetFullPathName(
_In_ LPCTSTR lpFileName,
_In_ DWORD nBufferLength,
_Out_ LPTSTR lpBuffer,
_Out_ LPTSTR *lpFilePart
);
参数说明:
lpFileName [in]
文件名。该参数既可以是一个短文件名,也可以是一个长文件名,还可以是共享名或卷名。
nBufferLength [in]
接收路径的缓冲区的长度。
lpBuffer [out]
这是一个输出参数,指向路径缓冲区的指针。
lpFilePart [out]
输出参数,指向路径缓冲区中文件名部分的指针。该参数可以是NULL。如果lpBuffer指向的缓冲区内存放的是一个目录而非文件,lpFilePart为0。
11.OpenSCManager
函数功能:函数建立了一个到服务控制管理器的连接,并打开指定的数据库。
SC_HANDLE WINAPI OpenSCManager(
_In_opt_ LPCTSTR lpMachineName,
_In_opt_ LPCTSTR lpDatabaseName,
_In_ DWORD dwDesiredAccess
);
参数:
1. lpMachineName:目标计算机名,NULL表示本地计算机
2. lpDatabaseName:服务管理程序系统组件数据库,可以设为SERVICES_ACTIVE_DATABASE,如果为NULL,表示默认打开SERVICES_ACTIVE_DATABASE数据库
3. dwDesiredAccess:对SCM的权限
12.OpenServiceA
函数功能:打开一个已经存在的服务
SC_HANDLE WINAPI OpenService(
_In_ SC_HANDLE hSCManager,
_In_ LPCTSTR lpServiceName,
_In_ DWORD dwDesiredAccess
);
参数:
hSCManager:SCM数据库句柄;OpenSCManager
lpServiceName:要打开服务的名字,这和CreateService形参lpServiceName一样,不是服务显示名称。
dwDesiredAccess:服务权限
13.StartService
函数功能:启动一个服务
BOOL StartServiceA(
SC_HANDLE hService,
DWORD dwNumServiceArgs,
LPCSTR *lpServiceArgVectors
);
参数
hService
服务的句柄。此句柄由OpenService或 CreateService函数返回 ,并且必须具有SERVICE_START访问权限。有关更多信息,请参阅 服务安全性和访问权限。
dwNumServiceArgs
lpServiceArgVectors数组中的字符串数。如果lpServiceArgVectors为NULL,则此参数可以为零。
lpServiceArgVectors
以null结尾的字符串作为参数传递给服务的ServiceMain函数。如果没有参数,则此参数可以为NULL。
14.CloseServiceHandle
函数功能:关闭服务控件管理器或服务对象的句柄。
BOOL CloseServiceHandle(
SC_HANDLE hSCObject
);
参数
hSCObject
服务控件管理器对象或要关闭的服务对象的句柄。OpenSCManager函数返回服务控制管理器对象的 句柄,OpenService或 CreateService函数返回服务对象的句柄 。
15.RegCreateKey
函数功能:注册表中创建一个新的项
(HKEY_LOCAL_MACHINE,"Software\\mykey",&key);
RegCreateKeyEx 插入主键
RegSetValueEx 插入键值
RegCloseKey 关闭
16.RegQueryValueExA
函数功能:读取字符串数据出来
WINADVAPI
LONG
APIENTRY
RegQueryValueExA (
__in HKEY hKey,
__in_opt LPCSTR lpValueName,
__reserved LPDWORD lpReserved,
__out_opt LPDWORD lpType,
__out_bcount_opt(*lpcbData) LPBYTE lpData,
__inout_opt LPDWORD lpcbData
);
WINADVAPI
LONG
APIENTRY
RegQueryValueExW (
__in HKEY hKey,
__in_opt LPCWSTR lpValueName,
__reserved LPDWORD lpReserved,
__out_opt LPDWORD lpType,
__out_bcount_opt(*lpcbData) LPBYTE lpData,
__inout_opt LPDWORD lpcbData
);
#ifdef UNICODE
#define RegQueryValueEx RegQueryValueExW
#else
#define RegQueryValueEx RegQueryValueExA
#endif // !UNICODE
hKey是主键
lpValueName是键值名称
lpType是类型
lpData是读出来数据保存地方
lpcbData是读取数据多少
17.FindResourceA
函数功能:该函数确定指定模块中指定类型和名称的资源所在位置
函数原型:HRSRC FindResource(HMODULE hModule,LPCTSTR lpName,LPCTSTR lpType)
参数:
hModule:处理包含资源的可执行文件的模块。NULL值则指定模块句柄指向操作系统通常情况下创建最近过程的相关位图文件。
lpName:指定资源名称。若想了解更多的信息,请参见注意部分。
lpType:指定资源类型。若想了解更多的信息,请参见注意部分。作为标准资源类型。这个参数的含义同EnumResLangProc/lpType。
返回值:如果函数运行成功,那么返回值为指向被指定资源信息块的句柄。为了获得这些资源,将这个句柄传递给LoadResource函数。如果函数运行失败,则返回值为NULL。
hMoule=FindResourceA( NULL, 2058, "XIA")
SizeofResource( NULL, hMoule ) //Returns the size, in bytes, of the specified resource.
handle=LoadResource( NULL, hMoule) //Returns a handle to be used to obtain a pointer to the first byte of the resource in memory.
LockResource( handle ) // 锁定资源并得到资源在内存中的第一个字节的指针
18.SetFilePointer
函数功能:在一个文件中设置新的读取位置
DWORD SetFilePointer(
HANDLE hFile, // 文件句柄
LONG lDistanceToMove, // 偏移量(低位)
PLONG lpDistanceToMoveHigh, // 偏移量(高位)
DWORD dwMoveMethod // 基准位置FILE_BEGIN:文件开始位置 FILE_CURRENT:文件当前位置 FILE_END:文件结束位置
说明:移动一个打开文件的指针
19.CreateFile
函数功能:这个函数的功能是创建或者打开一个文件或者I/O设备,通常使用的I/O形式有文件、文件流、目录、物理磁盘、卷、终端流等。如执行成功,则返回文件句柄。 INVALID_HANDLE_VALUE 表示出错,会设置 GetLastError 。
HANDLE WINAPI CreateFile(
_In_ LPCTSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);
20.SetFileTime
函数功能:设置指定文件或目录的创建,上次访问或上次修改的日期和时间。
BOOL SetFileTime(
HANDLE hFile,
const FILETIME *lpCreationTime,
const FILETIME *lpLastAccessTime,
const FILETIME *lpLastWriteTime
);
21.CreateProcess
函数功能:CreateProcess是Windows下用于创建进程的API函数,用来创建一个新的进程和它的主线程,这个新进程运行指定的可执行文件。
函数原型:
BOOL CreateProcess
(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes。
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
22.WaitForSingleObject
函数功能:等待一个内核对象变为已通知状态
DWORD WaitForSingleObject(
HANDLE hObject, //指明一个内核对象的句柄
DWORD dwMilliseconds); //等待时间
该函数需要传递一个内核对象句柄,该句柄标识一个内核对象,如果该内核对象处于未通知状态,则该函数导致线程进入阻塞状态;如果该内核对象处于已通知状态,则该函数立即返回WAIT_OBJECT_0。第二个参数指明了需要等待的时间(毫秒),可以传递INFINITE指明要无限期等待下去,如果第二个参数为0,那么函数就测试同步对象的状态并立即返回。如果等待超时,该函数返回WAIT_TIMEOUT。如果该函数失败,返回WAIT_FAILED。
23.TerminateProcess
函数功能:终止|杀死其它进程
函数原型:
BOOL TerminateProcess(HANDLE hProcess,UINT uExitCode)
参数说明:
1、hProcess:要终止(杀死)进程的句柄,需要有PROCESS_TERMINATE权限。
2、uExitCode:设置进程的退出值。可通过GetExitCodeProcess函数得到一个进程的退出值。
返回值:
如果失败将返回FALSE(0),而成功将返回一个非零值。
注:不要用if(ret==TRUE)去检测函数是否调用成功,因为函数调用成功会返回一个非零值,但不一定是TRUE(1)
24.GetExitCodeProcess
函数功能:获取指定进程的退出码
hProcess Long,想获取退出代码的一个进程的句柄
lpExitCode Long,用于装载进程退出代码的一个长整数变量。如进程尚未中止,则设为常数STILL_ACTIVE
25.CloseHandle
函数功能:函数用于关闭一个内核对象
BOOL CloseHandle(HANDLE hObject);
参数
hObject :代表一个已打开对象handle。
返回值
TRUE:执行成功;
FALSE:执行失败,可以调用GetLastError()获知失败原因。
26.GlobalAlloc
函数功能:调用GlobalAlloc函数分配一块内存,该函数会返回分配的内存句柄。简称全局堆分配
HGLOBAL GlobalAlloc(
UINT uFlags, // 分配属性(方式)
DWORD dwBytes // 分配的字节数
);
27.GetModuleHandle
函数功能:功能是获取一个应用程序或动态链接库的模块句柄。只有在当前进程的场景中,这个句柄才会有效。
HMODULE WINAPI GetModuleHandle(
_In_opt_LPCTSTR lpModuleName
);
28.GetProcessHeap
函数功能:它返回调用进程的默认堆句柄。
参数
函数无参数
返回值
如果函数成功,返回调用进程的默认内存堆句柄。
如果函数失败,返回 Null。若想,可以调用GetLastError获得更多错误信息。
之后是病毒的加密模块t.wnry
网友评论