【恶意代码分析实战】加密的反向连接木马分析
前言
使用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动态代码分析:查看运行过程中寄存器/栈/内存