文件MD5: 6735d02b856b0e7fffefdfc940843d74
IDA载入样本 发现所有函数都是白的 没有发现什么可疑信息
运行 用行为检测工具 发现样本创建了一个子进程 所有行为都是在子进程中完成
下面来看样本的行为
CreateProcess函数创建子进程
子进程是以挂起形式创建的(在挂起状态下 OD是无法附加进程的)
在当前内存申请一段空间
获取进程上下文
从子进程中读内存到当刚才申请的空间
利用ZwUnmapViewOfSection取消子进程的内存映射
第二参数为0x400000 是取消0x400000处的内存映射
在子进程0x400000处开辟0x13000字节大小的空间(这个数据在下面会用到)
利用多次WriteProcessMemory向子进程0x400000后的内存写数据,实际是在每次循环将一个section写入
设置进程上下文
跟到context结构体中看 EP为0xBO处的数据 0x408665
恢复进程
到此为止 外壳程序的任务已经完成 由子进程完成恶意代码操作
其实外壳程序完成了一种代码注入操作 只是和常见的注入方式不同
来总结一下外壳程序到底做了什么
1. 通过给CreateProcess传递CREATE_SUSPENDED参数来创建一个处于挂起状态的进程。(这里称新进程为进程B)
2. 通过调用GetThreadContext来获取被挂起进程(进程B)的CONTEXT。
3. 通过调用ZwUnmapViewOfSection来取消进程B的映像映射。
4. 通过VirtualAllocEx在进程B中分配足够的空间。
5. 通过WriteProcessMemory将恶意代码写到进程B刚分配的空间中
6. 通过调用SetThreadContext来设置线程的上下文。
7. 通过调用ResumeThread来执行被挂起的进程。
所以到现在为止,是没有办法直接对子进程进行调试的,因为OD无法附加挂起的进程,所以只能选择dump的方式,在对子进程的Write操作结束之后,进程恢复之前,将子进程dump下来
用LoadPE选择对应的PID
选择部分dump
地址和大小是刚才VirtuallAllocEx的参数
因为此时dump下来的文件是解压的 所以要修改对齐粒度和偏移 并且要把EP修改成刚才SetThreadContext设置的
并将所有区段的文件偏移修改为和内存偏移相同的值 并保存
保存 然后可以直接对该文件进行分析来分析实际的恶意行为
下面是实际的恶意行为分析
使用FindFirstFile和FindNextFile对硬盘进行遍历
根据dwFileAttributes
判断是文件还是文件夹
附上文件属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#define FILE_SHARE_READ 0x00000001 #define FILE_SHARE_WRITE 0x00000002 #define FILE_SHARE_DELETE 0x00000004 #define FILE_ATTRIBUTE_READONLY 0x00000001 #define FILE_ATTRIBUTE_HIDDEN 0x00000002 #define FILE_ATTRIBUTE_SYSTEM 0x00000004 #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_ARCHIVE 0x00000020 #define FILE_ATTRIBUTE_DEVICE 0x00000040 #define FILE_ATTRIBUTE_NORMAL 0x00000080 #define FILE_ATTRIBUTE_TEMPORARY 0x00000100 #define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 #define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 #define FILE_ATTRIBUTE_COMPRESSED 0x00000800 #define FILE_ATTRIBUTE_OFFLINE 0x00001000 #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 #define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 #define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000 #define FILE_ATTRIBUTE_VIRTUAL 0x00010000 #define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000 |
如果为0x10则为文件夹
这里有个问题 为什么要用&而不用==
我在网上找到了答案
但是当程序运行起来的时候却发现有一些文件夹是会忽略掉的,并没有被当作文件夹处理。而且在Debug的时候通过跟踪dwFileAttributes可以看到它的值会在16与32之间出现,所以使用“==”来判断一个目标是否为文档或目录是出现问题。
为文件夹时继续递归该函数0x407B98继续搜索
如果不是文件夹 而且属性不是删除属性 继续向下判断
在0x407872函数中判断文件类型
在0x406622函数中将所有文件类型解压出来
在这里比较文件类型
所有加密类型大体如下
对于符合加密类型的文件在0x407984函数中进行加密操作
利用CreateFile打开文件
将文件内容映射到内存
执行加密操作的地方 每次加密0x10个字节
具体的加密函数
具体加密过程我就不分析了……..密码学是真渣
取消映射并回写
将加密后的文件用MoveFile改名
在目录下所有文件都被加密之后 调用两次0x4077D8处的函数 其中一个写key 另一个写勒索信息
所有key相关的在0x406622函数中
总结一下,其实写这个样本的分析是因为这种自身代码注入的行为很有意思,并且对于调试也很有针对性,算是见过的比较有趣的一个样本,所以记录下来方便以后遇到相同的问题时来解决。