MEMZ 这个上古毒物我两三年前就下载了它的样本,然后把它扔到硬盘的某个角落。今天重新给它翻出来,看看它的源代码到底是怎么样的。
首先 ,样本: MEMZ_virus.zip (密码:MEMZ!virus
工具:IDA Pro v7.0 IDA7.0.zip
(Index不想写w
解压,发现目录里有两个文件,一个.bat,另一个.exe

很明显.exe就是病毒样本,那么.bat什么用的?
打开

可以看到,为了逃避检测,它还特地把执行内容写到js里
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//获取组件 f=new ActiveXObject("Scripting.FileSystemObject"); //打开文件x i=f.getFile("x").openAsTextStream(); //创建base64解密对象 x=new ActiveXObject("MSXml2.DOMDocument").createElement("Base64Data"); x.dataType="bin.base64"; //读取x中所有数据,并解密 x.text=i.readAll(); o=new ActiveXObject("ADODB.Stream"); o.type=1; o.open(); o.write(x.nodeTypedValue); //将解密内容写入z.zip z=f.getAbsolutePathName("z.zip"); o.saveToFile(z); s=new ActiveXObject("Shell.Application"); //解压z.zip得到MEMZ.zip s.namespace(26).copyHere(s.namespace(z).items()); o.close(); i.close(); |
很明显这个bat是用来生成exe病毒文件的。
就一个base64加密竟然直接绕过了360????
直接用IDA打开MEMZ.exe,分析源代码
程序出口点为start函数,跟进

然后直接一个 F5 下去





观察一下函数的流程。

首先获取了程序的启动参数,类似于int main(int argc,char**argv),只不过用Windows API GetCommandLine()的方式获取。
然后再判断参数是否存在,存在的话再判断是不是为/watchdog,是的话创建一个新线程执行sub_40114A()

创建完新线程后,调用了RegisterClassEx注册窗口和CreateWindowEx新建窗口。
但是这里的参数有点奇怪![]()
RegisterClassEx的定义
ATOM WINAPI RegisterClassEx(
_In_ const WNDCLASSEX *lpwcx
);
参数应该为WNDCLASSEX*而不是SHELLEXECUTEINFO*
有可能是IDA反编译出错,也有可能是作者的混淆(可能性更大
)
总之,到MSDN上找这两的定义


注意程序中的调用为RegisterClassExA((const WNDCLASSEXA *)&pExecInfo.lpVerb);
所以SHELLEXECUTEINFO.lpVerb及以下与WNDCLASSEX.cbSize及以下的参数一一对应。(项数也刚好相同,大小也相等sizeof(*void)=sizeof(int)=sizeof(DWORD)=4)
分析一下参数就可以发现,它创建了一个类名为hax的窗口,且此窗口的回调函数为sub_4010000,其余什么也没有。
跟进此函数

在winuser.h里面可以看到关于Message系列常数定义的值

WM_CLOSE对应窗口关闭请求,WM_ENDSESSION对应关机时关闭窗口请求
且如果传给当前窗口的消息不为其中任一时,窗口信息转为DefWindowProc处理。
否则调用函数sub_401021
跟进此函数

函数一开始就创建了 20 个线程执行同样这个函数,这样会变成无限创建线程,如果没有后续处理系统会卡死。
上面这个是我一开始的想法。其实它是错的。
这20个线程执行的不是这个函数的开头,而是一段IDA没能F5出来的代码,在这里

(意义不明,下图看得出堆栈也是平衡的,不知道为什么IDA错误

没有办法,我们直接对着汇编分析。
首先push esi,意义不明。
然后调用GetCurrentThreadId获取当前线程ID,保存在eax中。
接着SetWindowHookEx(idHook=5,lpfn=offset_fn,hmod=0,dwId);
查阅MSDN,
即所有的窗口事件将会被传送到fn所指向的函数处理,跟进

再次查询MSDN,code=3即创建窗口事件
跟进函数sub_401A55

发现这是个生成随机数的函数。
观察原函数,生成的随机数都返回给了v4 (LPARAM),所以这段代码实现了随机设置此句柄上的窗口位置。
之后调用了MessageBox(hWnd=0,lpText,Caption="MEMZ",0x1010=MB_OK|MB_ICONERROR|MB_SYSTEMMODAL)来显示对话框。
最后使用UnhookWindowHookEx卸载之前的钩子。
MessageBox中的内容lpText则是随机以下内容



回到原函数,创建对话框后

首先获取(LoadLibrary)ntdll.dll中的RtlAdjustPrivilege和NtRaiseHardError函数,如果这两个函数都能成功获取的话,就先调用v4(RtlAdjustPrivilege),再调用v6(NtRaiseHardError)
但是如果有一获取不成功,就是用原始的方法提升至SeShutdownPrivilege权限,最后调用ExitWindowsEx强制关机。
RtlAdjustPrivilege是一个MSDN未公开的函数,因为它可以做到一行提权,完美替代以前使用传统的OpenProcessToken -> LookupPrivilegeValue -> AdjustTokenPrivileges麻烦方法。
此函数定义NTSTATUS RtlAdjustPrivilege
(
ULONG Privilege, //Privilege [In] Privilege index to change.
BOOLEAN Enable, //Enable [In] If TRUE, then enable the privilege otherwise disable.
BOOLEAN CurrentThread, //CurrentThread [In] If TRUE, then enable in calling thread, otherwise process.
PBOOLEAN Enabled //
Enabled [Out] Whether privilege was previously enabled or disabled.
)
19 = 0x13 = SE_SHUTDOWN_PRIVILEGE即关机权限
同样,NtRaiseHardError也为ntdll.dll中MSDN未公开的函数,它的功能是引发一次蓝屏(与普通蓝屏不同,这个蓝屏一般由硬件错误引起,区别如图
)。
函数定义NTSYSAPI NTSTATUS NTAPI NtRaiseHardError
(
IN NTSTATUS ErrorStatus,
IN ULONG NumberOfParameters,
IN PUNICODE_STRING UnicodeStringParameterMask OPTIONAL,
IN PVOID *Parameters,
IN HARDERROR_RESPONSE_OPTION ResponseOption,
OUT PHARDERROR_RESPONSE Response
);
使用例子:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
typedef /*__success(return >= 0)*/ LONG NTSTATUS; typedef NTSTATUS *PNTSTATUS; #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) typedef struct _LSA_UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING; typedef enum _HARDERROR_RESPONSE_OPTION { OptionAbortRetryIgnore, OptionOk, OptionOkCancel, OptionRetryCancel, OptionYesNo, OptionYesNoCancel, OptionShutdownSystem } HARDERROR_RESPONSE_OPTION, *PHARDERROR_RESPONSE_OPTION; typedef enum _HARDERROR_RESPONSE { ResponseReturnToCaller, ResponseNotHandled, ResponseAbort, ResponseCancel, ResponseIgnore, ResponseNo, ResponseOk, ResponseRetry, ResponseYes } HARDERROR_RESPONSE, *PHARDERROR_RESPONSE; //获取函数地址....... int nEn = 0; RtlAdjustPrivilege(0x13, TRUE, FALSE, &nEn); HARDERROR_RESPONSE reResponse; NtRaiseHardError(0xC000021A,0,0,0,OptionShutdownSystem,&reResponse); |
(也可以参考:https://blog.csdn.net/AcceZn/article/details/54670776
即可引起一次0xC000021A蓝屏。(注:代码中NtRaiseHardError的第一个参数为负数是因为signed int的溢出)
别忘了,在创建窗口前还创建了一个线程执行函数sub_40114A()
继续跟进

这个函数流程是一目了然。
首先用GetProcessImageFileName获取当前运行文件名,然后利用tlhelp32.h里的CreateToolhelp32Snapshot结合Process32First和Process32Next遍历所有进程,也获取这些进程的文件名,并寻找与当前进程名相等的数量。如果数量v4比之前的数量v7还少(说明有进程被杀死),直接调用函数sub_401021,即之前的蓝屏/关机函数。
总结一下目前为止的流程:从程序启动开始,如果参数带有/watchdog,则创建一个线程判断是否有当前病毒程序被杀死,有的话直接蓝屏/关机。之后创建一个窗口,如果检测到窗口被关闭或系统将要关闭,手动创建多个线程蓝屏/关机。
回到主程序,继续往下看流程。(注:此时还在有参数存在的条件内,即有参数但不为/watchdog,因为在之前创建窗口后使用while死循环获取并处理窗口消息。

连续调用CreateFile和WriteFile尝试向\\.\PhysicalDrive0即0号磁盘设备写入扇区内容。中间那一大段就是获取写入内容,这一段就是覆盖系统MBR的过程。

首先打开当前磁盘目录下的note.txt,写入以下内容,完成后调用notepad将其打开,即显示了这段信息。

之后循环创建线程执行函数sub_401A2B
然后进入while() Sleep永久阻塞状态
中间这一段有点难分析,我们先看接下来的程序。(注:此时为无参数内

一堆警告信息。
都确认之后

首先通过ShellExecuteEx创建5个带/watchdog参数的程序(作用如上分析),再执行一个带/main参数的程序,还SetPriorityClass (0x80u=HIGH_PRIORITY_CLASS)设置最高优先级后本进程退出。
现在整个程序的流程都大概清楚了:先是用户点击,此时无参数,显示两个警告,都确认后创建5个/watch进程和一个/main进程并退出。/watchdog进程只是检测是否有自己的进程被杀死或者要关机了,那个时候直接蓝屏/关机。/main进程才是执行主要功能的进程,先是覆盖磁盘MBR,然后开始搞事搞事(好像确实是真的搞事
)。
回到刚刚参数/main(现在已经知道是它了)执行的地方,现在来详细分析这段调用(循环创建线程执行函数sub_401A2B)的功能。

跟进sub_401A2B()

发现这个函数只是简单地调用了以参数lpThreadParameter传进来的函数,并且只要函数调用成功的话这个函数的两个参数都自加,10sec后又重新执行一遍。
所以问题的所在不是这个函数,而是参数v9
回到原函数,分析一下流程:首先v8=0; v9=(DWORD *)&off_405130
然后Sleep() v9指向的空间的第二个DWORD字节的数据大小。
接着CreateThread() v9指向的空间第一个DWORD指向的函数。
最后v9越过两个DWORD字节,并且阻塞10msec后继续。
看得出来,最重要的就是在off_405130处的数据了。

函数都直接硬编码了在里面,我们一个一个分析。
来到sub_4014FC(函数只有一个参数,我也不知道为什么

先是sub_401A55()随机数到v2,然后ShellExecute (&lpFile)[v2 % 0x2E],即随机打开以下文件


来到sub_40156D

先是GetCursorPos获取指针位置,然后疯狂在一定范围内随机,最后SetCursorPos,实现了鼠标不断抖动跟喝了脉动一样的效果。
来到sub_4017A5

也很明显,先是随机了一定范围内的数作为SendInput的参数(1=INPUT_KEYBOARD),且这个随机数作为可视ASCII被模拟发送至键盘。('0'=48 '0'+42=90='Z')
来到sub_4016A0

随机了一个数作为PlaySound的参数(&pszSound)[v1 % 3],即随机播放下列声音

来到sub_4015D4

先用GetDesktopWindow()获取了顶层桌面的句柄,再用GetWindowDC()获取了桌面的窗口设备上下文(DC),接着用GetWindowRect获取桌面的大小,最后使用BitBlt()绘制图形并ReleaseDC()关闭写入。
BitBlt()参数中的0x330008=NOTSRCCOPY即“
Copies the inverted source rectangle to the destination.”(MSDN),就是实现了将整个桌面进行反色显示的功能。(一个F5刷新即可复原)
来到sub_40162A

发现创建了一个新线程执行sub_401994,跟进

功能与之前分析的类似。
来到sub_401866

首先使用GetSystemMetrics()获取ICO支持大小,如下


然后同样的获取桌面DC,鼠标位置。之后用LoadIcon()读取ICO图片,在鼠标位置上调用DrawIcon绘制ICO图片后,再随机生成位置绘制ICO图片。(同样F5刷新可去除)
注:0x7F01=32513=IDI_ERROR 0x7F03=32515=IDI_EXCLAMATION
来到sub_401688

使用了EnumChildWindows()获取桌面上所有子窗口,并且用EnumFunc()函数回调,跟进。

使用GlobalAlloc()分配堆空间,作为SendMessageTimeout()的一参数,同时设置Msg,超时时间为0x64=100msec

Msg=0xD,开始先获取窗口中所有文本,存到v2,然后将v2代入sub_401AA0,最后Msg=0xC重新设置窗口的文本为处理后的v2。跟进此函数。

对不起根本不想看
,自己去试过之后知道这个函数作用是颠倒字符串,同STL中reverse()一样。
这个函数功能就是颠倒桌面子窗口中所有文本框里的字符串。
(注:这里的桌面不仅仅是explorer.exe,还有其子进程,就是所有通过双击打开的程序。
来到sub_4017E9

StretchBlt()和BitBlt()的区别就是前者会根据Dst放缩图像,后者只是单纯拷贝。
StretchBlt()中参数0xCC0020=SRCCOPY,即将整个桌面缩小50,50,100,100
实现了无限循环缩小屏幕的效果。(同样,F5刷新可复原)
来到sub_4016CD

生成一堆随机数,然后再一定范围内随机复制图像,造成混乱的效果(同样F5可去除)
至此所有的功能都分析完了。
总结: 双击打开病毒文件时,首先弹出俩警告框,点取消直接退出,点确定则创建五个参数/watchdog和一个参数/main的进程。/watchdog进程检测是否有病毒进程被关闭或者将要关机,有的话弹出20个提示框,然后蓝屏/关机。/main进程首先修改磁盘MBR,然后用记事本弹出信息,最后循环执行打开程序/播放声音/反色屏幕/放图片/不断缩放屏幕/随意复制屏幕内容等操作并堵塞。
感觉除了改MBR其它好像都不会怎么样
所以帮它把改MBR去掉就安全了

源代码,要去掉CreateFile并且不触发ExitProcess
首先把底下的jnz改成jmp

然后。。。。。。。。。。。

再来到这里

同样把jnz换成jmp

再然后。。。。。。。。。。。。

然后很完美

前面删的太爽一不小心把这个给删了

弄回去就好了

还有这个也要弄回去(手打就成这样了,不过不影响

然后写入

一份无毒无害安全的MEMZ.exe就弄好了
。。。。。。虽然运行不正常
别了别了别这样换,重新替换一次,这次只修改jmp以及call以及push调用
然后就成功的去掉了写MBR的功能。
因为它实在是太安全了,以至于360都报毒了
修改版(去除写MBR,安全无害,可以在真机上运行):memz_edit.zip(密码:memz!funny
(PS:从上面的源代码分析看得出来,其实只要taskkill /f /im MEMZ.exe就好了,什么都不会发生
(可以直接写一个检测按键的程序,xx键按下while(1)if(GetAsyncKeyState(...))直接全部关闭MEMZ.exe,是完美安全的做法。
总感觉把写MBR去了这程序乐趣就少了一半
所以我特地把MBR及NC代码提出来:dump.bin(如果只有mbr.bin是不够的
直接用WinHex等工具覆盖写到虚拟机的磁盘偏移量0就可以了。
为了方便写入,我还特地写了个小工具 WriteMBR.exe(源代码:WriteMBR.zip
直接把.bin文件拖进去就可以了。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。本来弄到这里很开心
很快乐
直到我看到了这个 https://github.com/Leurak/MEMZ
MMP*******************************************************************************************************************************



MD有源代码我还分析个**
。。。。。。。等等,其实这波不亏啊。
因为在Windows上配置编译环境比TM直接改汇编还难
好了,源代码有是有,但是我也不会改
只好自己琢磨这也弄个骚东西出来。
推荐下载地址: av_full.rar (无打包,多文件
(注:Win7以上可能无法运行! av_packed_full.rar (打包版本,单文件
(注:无压缩 av_packed_full.exe
(注:精简警告:可能运行不正常!!! av_packed.exe av_packed.rar
注:安全无毒警告! http://r.virscan.org/language/zh-cn/report/58c20ab6372e192bf370238cb13bbbdb
至于360sd………………………….大家都知道的。(QVM和KVM存在意义不明????)
声明:本程序并不会篡改任何系统文件(重启后就跟没运行过一样),可以放心的在真机上食用。
源代码: av_source.zip (build with VC6 on win7x64
特别鸣谢:6332812(虽然是自己拿来用的w
顺带BUG:XP上自行关掉静音,谢谢合作
还有我在Win7上写的,界面是这样的:
如果拿到XP上运行界面变成这样知道一下意思一下就行哈

可不可以补一下无害版链接
NB
好文章,很清晰!
大佬我有个疑问,如何把jnz修改成jmp
qwq
不错不错!可惜无毒版MEMZ的链接好像没了,原本想玩一下
上次丢数据的时候给弄没了,只存了 av_source.zip 的备份。如果想玩的话,可以自己编译一份 MEMZ,或者闲着无聊也可以拿 IDA 啥的改一改ww
如何编译啊
qwq
用 VC++ 之类的编译源代码,剩下的资源文件根据说明生成打包就可以了。不过实际上可能会有点麻烦,自己弄编译环境的话可能不会比 IDA 里 NOP 几下快(
Ohhhhhhhhhh!
感谢您的用心,我是一只路过猫