summer14

【恶意代码分析实战】加密的反向连接木马分析

前言

使用IDA+Ollydbg分析一个加密的反向连接的木马程序

教程:《恶意代码分析实战》第九章实验Lab9-1

恶意代码样本:https://github.com/mikesiko/PracticalMalwareAnalysis-Labs

 

实验环境与工具(虚拟环境)

Windows XP:Ollydbg 1.1
Windows 10:IDA pro 7.7

 

定位调用main函数地址

    IDA函数窗口找到_main,进入函数实现的地方

    

   Ctrl+x列出交叉引用的地址,跳转过去

    

   00403945便是调用main函数的地址

   Ollydbg启动程序,F8单步执行到00403945,在此处下个断点,方便重新运行时快速定位到这里。

     

 

初步分析main函数

   Ollydbg F7步入main函数

     IDA中查看main函数的实现,首先判断程序执行时参数个数是否是1,如果是1个,调用sub_401000;不为1,继续执行main函数(注:程序执行时如果不带任何参数,那么argc=1)

    

   分析sub_401000,RegOpenKeyExA打开注册表项"SOFTWARE\\Microsoft \\XPS",如果打开成功,RegQueryValueExA获取"Configuration"对应的值;如果注册表打开失败,则退出sub_401000函数

    

  回到main函数,继续分析。由于执行程序时没有指定参数,注册表项也不存在,程序就会先调用sub_402410,接着跳转到loc_402d76,退出main函数

    

   分析sub_402410

     Ollydbg 先F8来到sub_402410,F7步入sub_402410

     IDA静态分析sub_402410,函数调用GetModuleFileNameA和GetShortPathNameA获取当前执行路径,然后ShellExecute执行一个cmd命令

    

 

    

 

     具体执行的命令是什么,在IDA难以分析,但是可以知道lpParameters是执行的cmd.exe的参数,在运行过程中存放在eax寄存器。Ollydbg F8执行到004024EE,查看寄存器的内容

     

    这样一来,sub_402410的作用就很清楚了,就是删除自身

  sub_402410分析结束

 

整理到此为止程序的功能:执行程序带上参数,程序继续执行;执行程序不带任何参数,程序会去查找注册表项,查找成功,继续执行;查找失败,退出程序。

换句话说,要想程序继续执行,方法一是执行程序时带上参数,方法二是添加注册表项。很明显,方法一更稳妥。

 

让main函数继续执行

  Ollydbg选项卡选择Debug->Arguments,添加任意参数

     

  Debug->Restart,重新运行。main函数成功继续执行

 

加密功能

  继续分析main函数。下面这段的含义是:取最后一个参数,调用sub_402510(var_4),如果返回值为1,继续执行main函数;否则调用sub_402410,前面已经分析过了,这个函数删除自身然后退出程序。

  

  分析sub_402510

    这个函数看上去像使用一个令人费解、硬编码的算法进行输入的完整性检查(教材原话)

    Ollydbg可以修改代码,使sub_402510只返回1

    绕过加密

    MOV EAX,1的机器码是B8 01000000
    RETN的机器码是C3
    在函数入口处右键->Binary->Edit把原来的55改成B801000000C3

        

     成功替换

      

       保存为新的可执行文件Lab09-01-Passwed-password.exe:文件右键->Copy to Executable->All modifications

          Ollydbg运行修改后的文件,带上任意的密码

           

   成功绕过!!

 

检查参数是否正确

   继续分析main函数,这一段检查输入的参数"-xxx"是否等于offset byte_40C170,不相等则跳去检查其他参数。

    

  40C170存放的数据是"-in"

    

   把参数改成"-in",Debug->restart重新运行。

    

 

获取文件路径

  main函数调用sub_4025B0

  

     sub_4025B0调用GetModuleFileNameA获取当前执行目录,__splitpath分解路径,指出完整路径path,驱动器号drive,目录路径dir,文件扩展名ext

  

 

继续执行main函数,来到这一段,执行sub_402600(ServiceName)

  

 Ollydbg执行到此处查看到ServiceName是"Lab09-01-Passwed-password"

  

 

程序用 -in做了什么(分析sub_402600

    Ollydbg F7步入到sub_402600

   执行sub_4025B0(var1404),前面已经分析了该函数获取工作目录

    

  连接本地服务控制管理器
  执行OpenSCManagerA(0,0,0F003Fh),连接本地计算机的服务控制管理器,拥有所有的访问控制权限

    

  返回值为hSCManager
  如果连接成功,则返回值不为0,继续执行
  

  打开具体服务
  执行OpenServiceA(hSCManager,lpServiceName,0F01FFh),打开某个服务,其中0F01FFh表示拥有这个服务的所有访问控制权限

    

  lpServiceName表示打开的服务名,保存在eax寄存器。在OD执行到此处,查看eax的值
    

  打开的服务就是自己
  返回值保存为hService
  如果返回值为0,意味着打开服务失败,跳转到0040277D
  如果成功打开服务,则继续执行

  假设成功打开服务
    执行ChangeServiceConfigA(hService,dwService...)[以下省略9个参数]
      

  创建服务

  而我们是第一次把程序执行到此处,此前并没有创建服务,所以服务打开失败,跳转到0040277D
    执行CreateServiceA(hSCManager,lpServiceName,lpDisplayName...)[以下省略十个参数]

      

    值得注意的参数是lpBinaryPathName,对应寄存器是EAX。Ollydbg运行到此处查看寄存器:  

      

    所以程序在这里创建了一个服务,对应的可执行文件就是自己
    如果返回值不为0,表示创建成功

  创建失败:
    执行CloseServiceHandle(hSCManager),关闭服务管理器的句柄
    跳转到loc_4028F5,退出sub_402600函数

      

  创建成功:
    执行CloseServiceHandle(hService),关闭服务的句柄
    执行CloseServiceHandle(hSCManager),关闭服务管理器的句柄

    继续执行

     

  查看系统服务,确实创建了服务!!!

    

  添加扩展环境变量
  执行ExpandEnvironmentStringsA(lpSrc,lpDst,nSize)

    

  lpSrc,lpDst保存在寄存器edx,ecx。OD执行到此处,查看对应寄存器

    

  因此ExpandEnvironmentStringsA函数的作用是添加扩展环境变量,使得可以通过命令行启动程序


  执行GetModuleFileNameA(),老朋友了,获取当前执行路径
    

 

  复制文件

  CopyFileA(lpExistingFileName,lpNewFileName,bFailfExists)

    

  其中lpExistingFileName,lpNewFileName保存在寄存器EDX,ECX
  OD执行到此处,查看寄存器

    

  因此函数的作用是把自己复制到system32目录下
  到对应目录下查看,果然存在

  执行sub_4015B0(BinaryPathName)

    

  其中BinaryPathName保存在eax,OD运行到这里查看对应的值
     

  分析sub_004015B0

  步入sub_004015B0看看函数做了什么

    执行GetSystemDirectory(lpBuffer,uSize)

      

    lpBuffer存在eax,OD运行到这里查看对应的值

      

    GetSystemDirectory查询系统目录,查询结果保存缓冲区中,lpBuffer指针指向这个缓冲区。
    查看此时内存中0012CF28存放的数据。在内存窗口右键->go to->Expression输入0012CF28

      

    因此程序使用GetSystemDirectory查询到了系统目录为C:\WINDOWS\system32

 

    执行sub_4014E0(LPCSTR,lpFileName)

      

    其中LPCSTR,lpFileName存放于ecx、eax,OD运行到此处查看寄存器

      

 

      分析sub_4014E0

      步入004014E0函数

      执行CreateFileA(lpFileName...),创建kernel32.dll文件

        

      执行CreateFileA(arg_0),创建Lab09-01-Passed-password.exe文件

        

    sub_4014E0分析结束

   sub_004015B0分析结束

  执行sub_401070("ups","http://www.practicalmalwareanalysis.com","80","60")

    

  分析sub_401070  

    步入sub_401070
    创建注册表项
    执行RegCreateKeyExA(hKey,"SOFTWARE\\Microsoft \\XPS",Reserved...)[省略6个参数]

      

    其中hKey为80000002h,Ollydbg查看此处栈的数据

      

    因此RegCreateKeyExA创建了注册表项HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \XPS

    执行RegSetValueExA(hKey,"Configuration",Reserved,dwType,lpData,cdData)

      

    其中lpData是注册表项的值,保存在edx,OD运行到此处查看
      

    由此可知RegSetValueExA在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \XPS注册表项添加了键"Configuration"和值"ups"
  sub_401070分析结束

00402600分析结束

 

终于回到main函数!!

至此,可以知道当程序以"-in"参数运行时,把自身安装为服务,创建注册表项并设置键值,并添加环境变量。

 

按照分析“-in”参数的思路,可以分析出程序所有参数及其作用如下:

-in      把自身安装为服务,创建注册表项并设置键值,并添加环境变量。
-re      删除服务
-c       创建注册表,设置键值
-cc      读取注册表的内容

 

无参执行 

    分析"-in"参数之前,提到还有另一种情况可以让main函数继续执行,那就是注册表项已经存在的情况。

   用"-in"走完程序流程后,可以看到程序已经添加了注册表项

    

  此时再来研究如果不带参数,执行程序会发生什么

  程序执行sub_401000,前面已经分析该函数检查注册表项是否存在。此时注册表项存在,返回值不为0,调用sub_402360

    

   分析sub_402360

    步入sub_402360

    执行sub_401280(var_C00,name,String,var_400),打开注册表项。执行完毕跳转到loc_4023BD

       

     其中String的值为“80”

        

    

     将_atoi("80")执行的返回值放入栈中,调用sub_402020(name,80)

       

    其中name的值为"http://www.practicalmalwareanalysis.com"

       

     分析sub_402020

     步入sub_402020 

       首先执行sub_401E60,步入

     程序执行sub_401AF0(name,hostshor,int,Str,80)

       

       

      步入sub_401AF0,程序执行sub_401640

      再步入sub_401640,发现一些网络连接函数

     创建网络连接

      分析sub_401640

      执行WSAStartup(202h,lpWSAData),通过进程启动Winsock dll

        

      执行Gethostbyname("http://www.practicalmalwareanalysis.com"),获取该域名的主机信息

        

        

      执行socket(2,1,6),创建一个绑定到特定传输服务提供者的套接字。

         

        执行connect(),建立与指定套接字的连接

         

    sub_401640分析结束      

    回到sub_401AF0

    执行send(),发送数据

        

    执行recv(),接受数据

        

  sub_402360分析结束

 

无参执行程序时,程序主动向外建立网络连接,具备反向连接后门程序的特性。      

 

总结

样本分析用到的Windows系统库函数

OpenSCManagerA:建立与服务控制管理器的连接,并打开指定的服务控制管理器数据库
OpenServiceA: 打开一个已存在的服务
ChangeServiceConfigA:更改服务的配置参数
CloseServiceHandle:关闭指向服务控制管理对象的句柄,也可能是指向服务对象的句柄
CreateServiceA:创建服务对象并将其添加到指定的服务控制管理器数据库
RegDeleteValueA:删除注册表中的键值
RegCreateKeyExA:创建指定的注册表项。如果键已经存在,函数将打开它
RegSetValueExA:设置注册表键值的数据和类型
RegOpenKeyExA:打开指定的注册表项
RegQueryValueExA:检索与开放注册表键关联的指定值名称的类型和数据。与RegOpenKeyExA功能类似
DeleteService:从服务控制管理器数据库中删除的指定服务
WSAStartup: 函数通过进程启动 Winsock DLL 的使用
Gethostbyname:获取域名的主机信息
socket:创建一个绑定到特定传输服务提供者的套接字。

Windows API文档:https://learn.microsoft.com/en-us/windows/win32/api/_shell/

 

程序自定义函数需要步入进去了解其功能
系统函数不需要步入,找到参数对应的值即可分析清楚

 

IDA静态代码分析:分析函数基本结构

Ollydbg动态代码分析:查看运行过程中寄存器/栈/内存 

    

posted on 2023-04-12 16:00  summer14  阅读(368)  评论(0编辑  收藏  举报

导航