本文为看雪论坛优秀文章
看雪论坛作者ID:wx_全都怪我
一、 情报收集
测试环境为winXP-32虚拟机。
分析攻击:IDA、OD、PEID等。
二、 具体行为分析
2.1 主要行为
(1) 这个程序秘密的启动了svchost孤儿进程;
(2) 这个程序通过将资源数据替换到svchost进程中;
(3) 资源数据需要XOR解码。
键盘数据记录:
这里先将ecx(003B0000)压入栈中,然后将之前栈中存放003B0000的地址中的值置为0,跳转到地址401016处,将eax置为0然后与6000比较,如果不是6000那么就跳转到40101E处,将003B0000存入edx中。
同时将003b0000加上栈中ebp-4处的值此时为0000000,把edx003B0000地址中存的第一个第一个字节0C放到al中即eax的低4位,然后将al即0C与ebp+10的值即最开始存的41即‘A‘进行异或,异或后的值4D存入al中,然后将栈中ebp+8地址处的值003B0000存入ecx中,将ecx与ebp-4处的值此时为0000000相加,然后将异或完成的al的值4D存入ecx的地址中,最后跳转指40100D处。
我们将查看这个函数的伪代码,这里其实是将003B0000处的6000h个字节与41进行异或 完成后跳出循环 a2即为6000,a1为地址003B0000 ,a3为41。
平衡堆栈,上个异或函数的返回值是资源的大小6000存在eax中,这里继续将eax的值存入堆栈中然后与0比较是否有资源,如果有资源则将资源的第一个字节的指针句柄00406048压入栈中,作为freeresource的参数,释放加载的资源。
Eax为0释放成功。
至此sub_40132c执行完毕返回值为分配内存区域的基地址003B0000存于eax中,然后平衡堆栈,将eax压入栈中与0比较看是否执行成功,继续将栈中的003B0000存入edx中,将edx的值压入栈中,将之前存入栈中的拼接好的目录c:\windows\system32\svchost.exe的地址存入eax中,然后将eax压入栈中,做为函数sub_4010EA的两个参数。
将参数中的003B0000存入eax中,再将eax存入局部变量中,然后将局部变量中的值003B0000又存入eax中,清空edx的值,将ecx地址中的值即003B0000中的值取2个字节4D5A放入dx中,然后将dx与0x4D5A比较看是否相等 相等则不跳转。
接着继续局部变量中的值003B0000存入eax中,然后将参数中的值003B0000存入ecx中,将eax地址偏移3C位置处的地址传入ecx中,将ecx的值传入栈中,然后又将栈中的数据转移到edx中,比较003B00E0地址的4个字节的值与0X4550比较 ,其实这两步仍然在验证该内存异或后是否为PE文件。
接下来传入了3个参数中到memset函数中分别为44h,0,地址12DB0C,目的是将地址12DB0C处的44h字节的数据置为0。
可以看到函数执行完后地址的44h大小的数据都为0。
同上 这里又传入3个参数将12FB50地址处10h大小的数据置为0。
传入的第一个参数12FB50是一个processinformation结构体里面保存了创建进程的句柄34主线程句柄44后面会用到。
接下来又传入4个参数来给分配内存空间由于传入的address值为0所以分配的区域由系统决定,分配2CC大小,我这边分配的地址是3E0000。
将eax的值传入栈中 然后栈又传给edx中,将0x10007存入edx地址的前4个字节,即003E0000地址的前4个字节的数据。然后继续将栈中的该地址传入eax中 然后再压入栈,将栈中的局部变量44放入ecx中然后压入栈,作为getthreadcontext的两个参数。
线程上下文获取成功。
清空栈中连续3块地址的数据压入函数readprocessmemory所需的参数,eax+0A4h是context._ebx再加8指向的是进程的基地址7FFDE008,这里是context结构体的偏移。
压入两个字符串到栈中其中ntdll.dll字符串做为函数getmodulehandA的参数 获取该模块的句柄。
然后将获得的模块。柄压入栈中和之前压入的字符串ntunmapviewofsection一起做为参数传入getprocaddress,即获取该dll中该函数的地址。
将eax中的函数地址传入栈中ebp-64,比较ebp-64是否有值,将栈中之前缓冲区的地址的值(存放着readprocessmemory函数读取到的值)存入eax中 将eax入栈,将挂起的进程句柄地址传入ecx中 eax入栈 ZwUnmapViewOfSection函数来取消映射目标进程的内存。
传入了5个参数 其中有挂起进程的句柄00000034,由于通过ZwUnmapViewOfSection函数取消了目标进程的内存映射,这时我们为这个挂起的进程申请分配模块的基地址提供给挂起的进程使用分配的空间为7000。这里的ecx+34是PE头的偏移34指向的imagebase进程基地址,ecx+50是pe头偏移50指向的是imagesize。
执行成功,我们通过zwunmapviewofsection函数进程卸载内存镜像后得到了一个“干净“的空间,然后我们通过virtualallocEx往该进程填其他程序的内存镜像。
传入5个参数到wirteprocessmemory函数中,通过句柄00000034找到目标进程由于目标进程的基地址就是我们要填充的进程基地址即为恶意程序的基地址0x400000,填充的数据为003B0000里面的数据,填充1000h大小,这里的ecx+54h是指pe头偏移54指向的是sizeofheaders。
将ebp-70地址的值置为0,然后将eax寄存器置为1同时传入栈中做为变量,将PE头的地址003B00E0传入eac中同时通过偏移+0x6找到节的数量这个PE文件有3个节区,然后将值3赋给dx中,dx与ebp-70的值比较做为循环的条件。
+3c指向的是PE标志位再+f8指向的image_section_header区段名称的地址,由于image_section_heade的大小为40个字节即28h,imul edx,28h就是在读取这个每个区段的数据结构。
开始通过writeprocessmemory函数向挂起的进程写入1000h大小接下来继续把.Text的内容写入该进程中。
循环遍历剩下2个节.rdata,.data。
修改该进程的基地址为400000:
设置该挂起进程的线程上下文。
减少挂起计数恢复线程进程运行,返回值是1。有使用ntunmapviewofsection 函数,常见的恶意手法会以挂起模式创建一个新进程通过调用CreateProcess并将流程创建标志设置为CREATE_SUSPENDED(0x00000004)完成。
新进程的主线程被创建为挂起状态,直到ResumeThread函数被调用才会运行。接下来,恶意软件需要用恶意的有效载荷来替换合法文件的内容。这可以通过调用ZwUnmapViewOfSection或NtUnmapViewOfSection来取消映射目标进程的内存。这两个API释放目标的内存。
内存被取消映射后,通过执行VirtualAllocEx为恶意软件分配新内存,并使用WriteProcessMemory将每个恶意软件的部分写入目标进程空间。恶意软件调用SetThreadContext将entrypoint指向已编写的新代码段。
将存文件路径的内存数据置为0。
释放开始分配的内存空间。
2.3 恶意代码分析-资源文件
提取资源文件:
用winhex异或解密出资源中的PE文件。
用PEid查看未加壳,导入表。
分配一个新的控制台。
在所有窗口中查找类名称为ConsoleWindowsClass的窗口。
函数返回值存到eax中然后放到ebp+hwnd局部变量里面,然后比较返回值是否为空为空就跳转到00401035处,不为0继续执行00401029处 将找到的窗口通过showwindow函数设为隐藏的窗口。
将405350 大小为400h的数据全置为1。
获取当前进程的句柄:
安装监视低级键盘输入事件的钩子程序,钩子过程为fn指针。
获取当前线程的任何窗口可用的消息。
一直获取消息,获取成功后调用unhookwindowshoookex来取消挂钩。
钩子过程分析:
传入了3个参数 先比较code参数是否有值,然后比较wParam参数是否是104或者是100,执行4010A1处的函数,函数的参数为lparam。
1个参数3个局部变量,创建一个文件名为practicalmalwareanalysis.log的普通属性文件保存一直打开的状态并且开启请求读写的权限。
设置文件移动的指针来控制。
获取当前前景窗口的指针句柄。
将当前窗口标题栏的文本复制到缓冲区中,返回复制的字符串长度。
比较两处字符串是否相等 相等返回0进行。
向创建的文件中写入\r\n[window:
计算缓冲区字符串的长度:
缓冲区的数据写入\r\n[window:的后面。
继续在后面写入]\r\n字符串。
将缓冲区字符串复制到405350处。
根据输入的字符进行比较判断来输入对应的数据到文件中最后关闭文件句柄。
将挂钩信息传递到当前挂钩链中的下一个挂钩过程。
看雪ID:wx_全都怪我
https://bbs.pediy.com/user-home-888591.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!