吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 10481|回复: 30
收起左侧

[PC样本分析] [原创]一款木马释放器的简单分析及IDA的骚操作

  [复制链接]
SSH山水画 发表于 2020-11-5 13:25
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子!
病毒分析分区附件样本、网址谨慎下载点击,可能对计算机产生破坏,仅供安全人员在法律允许范围内研究,禁止非法用途!
禁止求非法渗透测试、非法网络攻击、获取隐私等违法内容,即使对方是非法内容,也应向警方求助!

前言

这是一款比较简单的木马,通过分析此样本可以训练分析思路、分析逻辑,并且加强IDA的使用.本人安全公司实习生,刚入圈几个月,所以文章中会有说的不对的地方,欢迎指出.择优吸收.

样本信息:

File: virus.exe
SHA1:   71b7322291b5a89d227b5cfe82106fce51036da2
SHA256: b48ebc54b9717bbe3a9de3fd5744c8ad1fdd3a26c7b47f6170b98f8abde9a744
LsHashS:    1155293a7b9091923b1d1511a8d9c17589bb1b55b4613bd55313ba7b3594b899
MD5:        00877507f0b812599868a647330d0630
分析环境   Windows7_Service Pack 1
分析工具   OD IDA 火绒剑 PEID

0x1.初步观察了解行为

拖入PEID查一下有没有壳子

image-20201030162643091

yoda's Protector v1.02 (.dll,.ocx) -> Ashkbiz Danehkar (h) [Overlay] *

不了解,只知道有个加密壳

右键查看文件属性

image-20201030162957493

属性为空,至少确定了不是个正规公司开发的程序,直接上火绒剑吧

过滤FILE_open   REG_openkey   REG_getval FILE_read  行为(个人习惯,因为这几个行为基本没卵用)

image-20201030163049022

不得不说,火绒剑真是方便,程序行为十分清晰了

image-20201030163205846

样本启动了svchost.exe后直接结束了

而下方的行为显然不是svchost.exe应该有的,目测会有一段注入ShellCode的代码

到这里,这个程序基本上可以报毒了 : 静默运行+启动svchost.exe执行敏感代码+无任何文件描述信息

我们接下来要对这个样本进行稍微详细的分析,看看他具体干了什么

0x2.第一层代码分析

将样本拖入IDA32,分别查看字符串+导入表

image-20201030163843540

image-20201030163917123

发现字符串有很多URL还有请求头信息,

我们随便选一个:http://morphed.ru/static.php

去VT查一下

image-20201030164054405

很好,非常不正常!我们又可以为他扣掉几分(所以到现在还是在做黑白鉴定的工作 --_-- )

该步入正题了,上面查看文件属性,文件大小不是很大,字符串和导入表信息也不多,因此我们决定从入口点开始进行分析,在IDA中找到入口点按下F5分析伪源码 (个人比较喜欢F5,但是这是不好的,遇到强度高的样本,F5反而会增加你的工作量)

image-20201030164457491

可以看到上来就开始开辟内存,拷贝0x00401000处的数据,我们去看看401000存了些啥

image-20201030164604022

一段函数代码+一堆未知数据,暂时记住这个地方,后面可能会用到

简单起个名后,继续分析,我们看到memcpy下面的sub_407AC4紧接着就对这块内存进行了操作,跟进去看看函数内部情况

image-20201030164941108

没有再调用函数,反而进行了很多数据的运算,而一般这种情况不是加密就是解密,结合上面的行为分析结果,加上调用407AC4这个函数后并没有接收这个函数的返回值,推测此处对ShellCode进行了解密一类的操作

回到入口函数,观察下一行代码sub_407B02(pAlloc_, 0x490, LoadLibraryA, GetProcAddress);

参数有很明显的特点:

  • 一段ShellCode的首地址
  • 一个未知的常量
  • 两个经典远线程注入需要用到的函数地址

进入函数内部,为参数命名后,看一下代码

image-20201030165842727

哦吼,又是没有函数调用,全部在对数据进行操作,两层循环嵌套,循环内调用了GetProcAddress和LoadLibrary,根据传入的参数401000  490我们去看看401490都存了些啥

定位到401490D让其以字节显示,观察附近的数据,有了很明显的发现

image-20201030170634441

下面是一个个函数名字符串,我们按A Alt+A把这些数据以字符串进行展示,观察结果

image-20201030171047096

image-20201030171054696

根据401490附近的字符串,我们最终可以推测出sub_407B02处的函数用于加载函数,模拟导入表功能,将其命名FixImport

回到主函数继续分析:return ((int (__stdcall *)(_DWORD))(pAlloc_0 + 0xC0))(sub_407992);

可以看到这行代码用于执行4010C0出的函数,传入参数为407992,一个函数地址

我们之前记得401000向下是由一段数据+一段函数代码组成

而这行代码以函数指针的形式调用了4010C0的代码,却不是直接写成函数

由此我们验证了上文对401000处数据为ShellCode的猜想,接下来我们则需要知道4010C0干了什么

0x3.第二层代码提取

进入4010C0,在4010C0处按P,使IDA识别为函数

按F5,解析为伪源码,发现失败,提示call的地址找不到?这是怎么回事?

还记得上文中有两个函数分别对401000附近的数据进行了解密+修复导入函数一类的操作吗?

没错,这段代码只有在401000段被正确修复后才能执行,至此,我们需要使用OD进行动态调试来dump出401000处的数据

打开OD,拖入样本,在地址0040798E处下断点 (4010C0处为函数头部,在40798E处调用了这个函数)

观察OD各个窗口的值

image-20201030172633316

我们上文一直按照401000分析的,因为IDA静态分析我们并不知道VirtualAlloc分配到了哪里,所以我们只能忽略入口函数的那句memcpy,但是现在我们动态调试,就可以查看分配的那块内存了:eax = 0x200c0

反汇编窗口跳转到0x200C0,数据窗口跳转到0x20000 (4010C0之前有一段未知数据,我们要弄清楚那段数据)

image-20201030172946769

可以看到,call处的地址都变了,而且OD已经正确识别出了各个函数的名字,这也是为什么IDA无法F5的原因,这里的call的地址都是通过解密函数+地址修复函数进行处理过的

仔细观察,call后面的地址都是小于200C0的,正好对应上了我们20000~200C0这一段未知数据

数据窗口右键->长型->地址

image-20201030173215918

一切都变得清晰了,从20000~200C0是样本自身维护的一张导入表,以供ShellCode使用,400C0~42000包含了一些可执行的代码

点击M右键20000内存段->数据->备份->保存数据到文件

这样,ShellCode这段代码我们就dump下来了,接下来我们要对这段ShellCode进行分析

将dump下来的数据(后文称之为1.mem)拖入IDA32

点击Edit->Segments->Rebase Program进行基地址的修改

image-20201030173739806

由于OD中内存申请地址为0x20000我们也要在IDA中设置为0x20000以保证IDA能正确解析汇编代码

点击OK,观察代码变化

image-20201030173855390

不再是一堆堆的字节数据了,变成了可读性较高的汇编代码,但是还有一处瑕疵

20000~200C0处应该有一些函数的,IDA这里没有为我们解析,这会让我们分析工作量增加,我们接下来要使用IDA的脚本执行功能:

0x4.IDA脚本批量为Dump中的函数命名

点击File->Produce FIle->Dump database to IDC file...->保存

此时,我们dump文件同目录下会生成一个与dump文件同名的idc文件:1.idc

用notepad++将其打开

static main(void)
{
  // set 'loading idc file' mode
  set_inf_attr(INF_GENFLAGS, INFFL_LOADIDC|get_inf_attr(INF_GENFLAGS));
  GenInfo();            // various settings
  Segments();           // segmentation
  Enums();              // enumerations
  Structures();         // structure types
  ApplyStrucTInfos();   // structure type infos
  Patches();            // manual patches
  SegRegs();            // segment register values
  Bytes();              // individual bytes (code,data)
  Functions();          // function definitions
  // clear 'loading idc file' mode
  set_inf_attr(INF_GENFLAGS, ~INFFL_LOADIDC&get_inf_attr(INF_GENFLAGS));
}

我们会看到这样一段代码,把函数内容全部删除,只留下Bytes();              // individual bytes (code,data)

static main(void)
{
  Bytes();              // individual bytes (code,data)
}

接下来Ctrl+F搜索Bytes,我们看看这个Bytes函数干了什么

static Bytes(void) {
    Bytes_0();
        end_type_updating(UTP_STRUCT);
}

可以看到Bytes调用了Bytes_0,我们再搜索Bytes_0去看看他的内容

static Bytes_0(void) {
        auto x;
#define id x

    update_extra_cmt        (0X20000,   E_PREV + 0, "; File Name   : G://1.mem");
    update_extra_cmt        (0X20000,   E_PREV + 1, "; Format      : Binary file");
    update_extra_cmt        (0X20000,   E_PREV + 2, "; Base Address: 0000h Range: 0000h - 2000h Loaded length: 2000h");
    create_dword    (0X20000);
    create_dword    (0X20004);
    create_dword    (0X20008);
    create_dword    (0X2000C);
    create_dword    (0X20010);
    create_dword    (0X20014);
    create_dword    (0X20018);
    create_dword    (0X2001C);
    create_dword    (0X20020);
    create_dword    (0X20024);
    create_dword    (0X20028);
    create_dword    (0X2002C);
    create_word (0X20036);
    create_word (0X2003A);
    create_dword    (0X2003C);
    make_array  (0X2003C,   0X9);
    create_strlit   (0X20060,   0X20068);
    set_name    (0X20060,   "a753");
    create_strlit   (0X20068,   0X20094);
    set_name    (0X20068,   "aSystem32Wuaucl");
    create_strlit   (0X20094,   0X200C0);
    set_name    (0X20094,   "aSyswow64Svchos");
    create_insn (0X200C0);
    create_insn (x=0X200C3);
    op_hex      (x, 1);
    create_insn (0X2014F);
    create_insn (0X203C9);
    set_cmt (0X2041D,   "Trap to Debugger", 0);
    create_insn (x=0X2041D);
    op_hex      (x, 0);
    create_insn (0X2041E);
    create_insn (0X20424);
    create_insn (0X2042A);
    create_insn (0X20430);
    create_insn (0X20436);
    create_insn (0X2043C);
    create_insn (0X20442);
    create_insn (0X20448);
    create_insn (0X2044E);
    create_insn (0X20454);
    create_insn (0X2045A);
    create_insn (0X20460);
    create_insn (0X20466);
    create_insn (0X2046C);
    create_insn (0X20472);
    create_insn (0X20478);
    create_insn (0X2047E);
    create_insn (0X20484);
    create_insn (0X2048A);
    create_dword    (x=0X20490);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    create_dword    (0X20494);
    make_array  (0X20494,   0X2);
    create_byte (0X2049D);
    make_array  (0X2049D,   0X3);
    create_dword    (x=0X204A0);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    create_dword    (x=0X204A4);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    make_array  (0X204A8,   0X8);
    make_array  (0X204B2,   0X2);
    create_dword    (0X204B4);
    make_array  (0X204B4,   0X6);
    create_dword    (x=0X204CC);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    create_dword    (x=0X204D0);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    create_byte (0X204D5);
    make_array  (0X204D5,   0X3);
    make_array  (0X204DA,   0X2);
    make_array  (0X204DE,   0X2);
    make_array  (0X204E2,   0X2);
    create_dword    (x=0X204E4);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    create_dword    (x=0X204E8);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    make_array  (0X204EE,   0X2);
    make_array  (0X204F2,   0X2);
    make_array  (0X204F6,   0X2);
    make_array  (0X204FA,   0X6);
    create_dword    (x=0X20500);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    create_dword    (x=0X20504);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    create_byte (0X20509);
    make_array  (0X20509,   0X3);
    create_dword    (x=0X2050C);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    make_array  (0X20512,   0X2);
    create_byte (0X20515);
    make_array  (0X20515,   0X3);
    make_array  (0X20519,   0X7);
    create_strlit   (0X20522,   0X20533);
    set_name    (0X20522,   "aNtdelayexecuti");
    create_strlit   (0X20536,   0X2053E);
    set_name    (0X20536,   "aZwclose");
    create_byte (0X2053F);
    create_strlit   (0X20541,   0X20550);
    set_name    (0X20541,   "aWcreatesection");
    create_strlit   (0X20552,   0X20565);
    set_name    (0X20552,   "aZwmapviewofsec");
    create_strlit   (0X20569,   0X20582);
    set_name    (0X20569,   "aWqueryinformat");
    create_word (0X20582);
    create_strlit   (0X20585,   0X20593);
    set_name    (0X20585,   "aWresumethread");
    create_strlit   (0X20597,   0X205AB);
    set_name    (0X20597,   "aWunmapviewofse");
    create_strlit   (0X205AC,   0X205B6);
    set_name    (0X205AC,   "aNtdllDll");
    create_strlit   (0X205B8,   0X205C4);
    set_name    (0X205B8,   "aClosehandle");
    create_strlit   (0X205C7,   0X205D2);
    set_name    (0X205C7,   "aReatefilew");
    create_strlit   (0X205D5,   0X205E3);
    set_name    (0X205D5,   "aReateprocessw");
    create_strlit   (0X205E7,   0X205F2);
    set_name    (0X205E7,   "aXitprocess");
    create_word (0X205F2);
    create_strlit   (0X205F4,   0X20607);
    set_name    (0X205F4,   "aGetmodulefilen");
    create_byte (0X20609);
    make_array  (0X20609,   0X3);
    create_strlit   (0X2060C,   0X2061B);
    set_name    (0X2060C,   "aTmodulehandlew");
    create_strlit   (0X2061E,   0X2062F);
    set_name    (0X2061E,   "aGetthreadconte");
    create_strlit   (0X20633,   0X20647);
    set_name    (0X20633,   "aEtwindowsdirec");
    create_strlit   (0X2064B,   0X20662);
    set_name    (0X2064B,   "aEtenvironmentv");
    create_strlit   (0X20664,   0X20671);
    set_name    (0X20664,   "aVirtualalloc");
    create_byte (0X20673);
    create_strlit   (0X20674,   0X20680);
    set_name    (0X20674,   "aVirtualfree");
    create_strlit   (0X20682,   0X2068B);
    set_name    (0X20682,   "aLstrcatw");
    create_strlit   (0X2068C,   0X20699);
    set_name    (0X2068C,   "aKernel32Dll");
    make_array  (0X20699,   0X3);
    create_dword    (0X2069C);
    make_array  (0X2069C,   0X25A);
    create_dword    (x=0X21004);
    op_plain_offset (x, 0,  0);
    op_plain_offset (x, 128,    0);
    create_word (0X2100A);
    create_dword    (0X2100C);
    make_array  (0X2100C,   0X3FD);
}

可以看到一大堆函数代码,不要管,全部删除

static Bytes_0(void) {

}

接下来我们要用到一个函数 set_name (地址,"名字");这是IDA提供的接口,用于为地址命名

我们需要将20000~200C0处的函数全部命名

回到OD,数据窗口定位到20000,复制全部函数数据

00020000  7DD7186E  kernel32.VirtualFree
00020004  7DD71856  kernel32.VirtualAlloc
00020008  7DD789F1  kernel32.SetEnvironmentVariableW
0002000C  7DD743E2  kernel32.GetWindowsDirectoryW
00020010  7DD979D4  kernel32.Wow64GetThreadContext
00020014  7DD734B0  kernel32.GetModuleHandleW
00020018  7DD74950  kernel32.GetModuleFileNameW
0002001C  7DD77A10  kernel32.ExitProcess
00020020  7DD7103D  kernel32.CreateProcessW
00020024  7DD73F5C  kernel32.CreateFileW
00020028  7DD71410  kernel32.CloseHandle
0002002C  7DD9828E  kernel32.lstrcatW
00020030  00000000
00020034  7DE90058  ntdll_12.ZwResumeThread
00020038  7DE8FAC8  ntdll_12.ZwQueryInformationProcess
0002003C  7DE8FC40  ntdll_12.ZwMapViewOfSection
00020040  7DE8FF94  ASCII "窑"
00020044  7DE8F9D0  ntdll_12.ZwClose
00020048  7DE8FC70  ntdll_12.ZwUnmapViewOfSection
0002004C  7DE8FD6C  ntdll_12.ZwDelayExecution
00020050  00000000
00020054  00000000
00020058  00000000
0002005C  00000000

将上面这段数据以set_name (xxx,"xxx");的格式进行替换,具体替换方法可以手动替换,也可以正则替换,看各位喜好

替换完的结果:

set_name  (0x00020000, "VirtualFree");
set_name  (0x00020004, "VirtualAlloc");
set_name  (0x00020008, "SetEnvironmentVariableW");
set_name  (0x0002000C, "GetWindowsDirectoryW");
set_name  (0x00020010, "Wow64GetThreadContext");
set_name  (0x00020014, "GetModuleHandleW");
set_name  (0x00020018, "GetModuleFileNameW");
set_name  (0x0002001C, "ExitProcess");
set_name  (0x00020020, "CreateProcessW");
set_name  (0x00020024, "CreateFileW");
set_name  (0x00020028, "CloseHandle");
set_name  (0x0002002C, "lstrcatW");
set_name  (0x00020034, "ZwResumeThread");
set_name  (0x00020038, "ZwQueryInformationProcess");
set_name  (0x0002003C, "ZwMapViewOfSection");
set_name  (0x00020044, "ZwClose");
set_name  (0x00020048, "ZwUnmapViewOfSection");
set_name  (0x0002004C, "ZwDelayExecution");

//记得别忘了后面的;和地址的0x  否则IDA执行脚本时会报错

将这段数据复制到Bytes_0函数体中,结果如下

static Bytes_0(void) {
    set_name  (0x00020000, "VirtualFree");
set_name  (0x00020004, "VirtualAlloc");
set_name  (0x00020008, "SetEnvironmentVariableW");
set_name  (0x0002000C, "GetWindowsDirectoryW");
set_name  (0x00020010, "Wow64GetThreadContext");
set_name  (0x00020014, "GetModuleHandleW");
set_name  (0x00020018, "GetModuleFileNameW");
set_name  (0x0002001C, "ExitProcess");
set_name  (0x00020020, "CreateProcessW");
set_name  (0x00020024, "CreateFileW");
set_name  (0x00020028, "CloseHandle");
set_name  (0x0002002C, "lstrcatW");
set_name  (0x00020034, "ZwResumeThread");
set_name  (0x00020038, "ZwQueryInformationProcess");
set_name  (0x0002003C, "ZwMapViewOfSection");
set_name  (0x00020044, "ZwClose");
set_name  (0x00020048, "ZwUnmapViewOfSection");
set_name  (0x0002004C, "ZwDelayExecution");    
}

保存,退出,回到IDA,按Alt+F7弹出脚本选择框

image-20201030175109800

选择1.idc,点打开

image-20201030175135806

可以神奇的发现,20000开始的地址已经被IDA正确解析,在200C0处按P,让IDA将其解释为一个函数头部

image-20201030175250337

这样就是我们最终弄好的结果,IDA可以正确解析这段代码,我们看的也会特别清晰

0x5.第二层代码分析

按下F5观察伪源码

image-20201030175557342

这段代码就不详细说明了,大体逻辑就是判断系统位数,取出对应的系统文件,注入恶意代码,运行系统文件

我们的重点在注入到系统文件中的那部分代码是什么,这里我们不分析第二层代码,采用一个更加便捷的方式

0x6.第三层代码提取

我们此处依然采取dump 的方式进行提取,直接运行样本,不做任何阻拦,样本会创建一个被注入了恶意代码的svchost.exe进程,我们火绒剑选择svchost.exe,点击下方内存列表,右击0x20000这块内存,选择内存转储

PS:若火绒剑无法保存,可以尝试使用其他工具进行保存,需要注意的是svchost为64位程序,OD是用不了的,此处推荐PCHunter64

拷贝出来的dump文件才用相同方式拖入IDA,更改基址,批量命名,此处省略

0x7.第三层代码分析

如果操作没有错误,IDA解析后的代码应该如下:

image-20201030180540335

image-20201030180612608

image-20201030180627837

image-20201030180646252

到了这里样本的最终行为已经毫无遮拦了,我们甚至不用进入F5分析逻辑,只需简单的观察字符串即可分析出这个样本的大致行为逻辑

  • image-20201030180742937

image-20201030180753289

注册表操作API+启动项目录字符串 = 添加开机自启

  • image-20201030180849816

网络操作相关API用于发送请求,可能是下载一个恶意程序亦或是上传一段用户隐私数据

总结

整篇文章对于代码的分析很少,各位如果想要细致分析可以自行抠代码,没有了保护,所有写法已经是透明的了,因此继续逆向只是个体力活,我这里就不演示了

主要是给初学的小白演示下这种多层代码释放嵌套的样本怎么处理,已经IDA的强大功能

择优吸收

免费评分

参与人数 9威望 +1 吾爱币 +27 热心值 +9 收起 理由
金钱一层 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lyslxx + 1 + 1 我很赞同!
wanfon + 1 + 1 谢谢@Thanks!
fengbolee + 1 + 1 用心讨论,共获提升!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lemon__star + 1 + 1 我很赞同!
chobits110 + 1 我很赞同!
火绒 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
__不说 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

刀大喵 发表于 2020-11-5 13:50
小白的我还是一脸蒙逼的来了  学这个要从哪下手呀
花好s月圆 发表于 2020-11-5 15:43
 楼主| SSH山水画 发表于 2020-11-5 13:55
刀大喵 发表于 2020-11-5 13:50
小白的我还是一脸蒙逼的来了  学这个要从哪下手呀

从科锐或者15派下手
刀大喵 发表于 2020-11-5 14:03
SSH山水画 发表于 2020-11-5 13:55
从科锐或者15派下手

培训机构简单直接
__不说 发表于 2020-11-5 14:26
大佬能放个样本上来嘛?想学着分析一下
 楼主| SSH山水画 发表于 2020-11-5 15:05
__不说 发表于 2020-11-5 14:26
大佬能放个样本上来嘛?想学着分析一下

拿着hash去找啊- -公司不让传文件
email123 发表于 2020-11-5 15:12
欢心进来,懵逼出去
kingty_x 发表于 2020-11-5 15:27
emmmmm...爱萝莉的图挂了?
ghostsang 发表于 2020-11-5 16:16
本帖最后由 ghostsang 于 2020-11-5 16:33 编辑

火绒剑里有监测到网络行为. 可以Wireshark看样本和morphed.ru域名进行了什么网络行为. 对域名进行威胁情报查询也可以得到这个域名的家族是什么.还有相关的类似家族样本.样本行为的信息
image.png
image.png
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 09:47

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表