加入收藏 设为首页
博客 友圈 商城
留言 搜索 投搞
首页 | 网络动态 | 技术文章 | 下载中心 | 设计 | 摄影 | 精彩Flash | 摄影作品 | 顶客排行 | 悠乐论坛
>首页 -> 网络动态 -> 软件动态

TOP

机器狗0625技术剖析(驱动读写磁盘扇区)
[ 录入者:riqukiqpl | 时间:2008-08-03 01:57:38 | 作者: | 来源: | 浏览:181次 ]
简介:编辑语:倒是没听说机器狗作者被抓 由于对磁盘结构不是很熟,仅仅从逆向分析的角度,通过对这个驱动的分析过程,学习了向SCSI端口向扇区写数据的方法。如有错误或纰漏之处,请大家指出。 摘要这是一个6月23号捕..

编辑语:倒是没听说机器狗作者被抓

由于对磁盘结构不是很熟,仅仅从逆向分析的角度,通过对这个驱动的分析过程,学习了向SCSI端口向扇区写数据的方法。如有错误或纰漏之处,请大家指出。

摘要
这是一个6月23号捕获的机器狗变种。跟之前不同的地方,主要是将以前ring3下写磁盘替换文件的操作使用驱动实现了,本文的主要部分也是对该部分的解析。可以看出,机器狗的作者对内核驱动以及磁盘结构都很熟悉。

三、驱动分析
1、基本信息
文件名:%windir%\system32\drivers\obj2.sys
DeviceName:\\Device\\DogBaby
SymbolicLinkName:\\DosDevices\\DogBaby

2、摘除ntfs的AttachedDevice

3、恢复disk的Hook
1)、摘除disk的AttachedDevice
2)、根据_DRIVER_OBJECT.DriverSection获得_LDR_DATA_TABLE_ENTRY的链表,枚举该链表查找名为CLASSPNP.SYS的模块,找到则获取该模块的基址Base和EntryPoint,如果EntryPoint - Base等于0AE8Fh(入口的RVA),则取g_pClassInternalIoControl = Base + 4FC3h。然后注册驱动disk的派遣例程为_DRIVER_OBJECT.IrpInternalDeviceControlDispatch = pClassInternalIoControl。

4、摘除FtDisk的AttachedDevice

5、摘除Atapi的AttachedDevice

6、恢复Atapi的Hook
1)、读取atapi.sys文件,并定位pe头、及块表。
2)、定位到init块,从init块中使用模糊匹配特征C7 ?? 30 ?? ?? ?? ?? C7 ?? 34。
3)、找到的地址为24968h,记为Atapi_24968h。
4)、取Atapi_24968h + 11h的位置,并换算成RVA,得到fnRVA_IdePortDispatch。
5)、取Atapi_24968h + 18h的位置,并换算成RVA,得到fnRVA_IdePortDispatchDeviceControl。
6)、如果上面的方法获取失败,则强制转换:
fnRVA_IdePortDispatch = 67B4h,
fnRVA_IdePortDispatchDeviceControl = 0A592h。
7)、获取这两个函数的SVA
fnIdePortDispatch = fnRVA_IdePortDispatch + DriverStart,
fnIdePortDispatchDeviceControl = fnRVA_IdePortDispatchDeviceControl + DriverStart。
8)、注册Atapi的派遣例程
_DRIVER_OBJECT.IrpDeviceControlDispatch =
fnIdePortDispatchDeviceControl,
_DRIVER_OBJECT.IrpInternalDeviceControlDispatch = fnIdePortDispatch。

7、定位被覆盖文件的逻辑簇号
1)、打开文件conime.exe,失败则打开userinit.bat;并获取FileObject。
2)、根据FileObject获取_FILE_OBJECT.Vpb,再获取_VPB.DeviceObject。
3)、创建一个IRP。设MainFunction为IRP_MJ_FILE_SYSTEM_CONTROL。
4)、设定_IO_STACK_LOCATION.FsControlCode =
FSCTL_GET_RETRIEVAL_POINTERS
//其它IRP初始化略过,详细请看idb文件。
5)、调用自己实现的IOCallDriver,获取指定文件的RETRIEVAL_POINTERS_BUFFER信息。
结构查阅msdn如下:
typedef struct RETRIEVAL_POINTERS_BUFFER {
   DWORD ExtentCount;
   LARGE_INTEGER StartingVcn;
   struct {
     LARGE_INTEGER NextVcn;
     LARGE_INTEGER Lcn;
   } Extents[1];
} RETRIEVAL_POINTERS_BUFFER,
*PRETRIEVAL_POINTERS_BUFFER;
要注意的是按8个字节对齐的,所以DWORD ExtentCount后、LARGE_INTEGER StartingVcn前有4个字节的空间。
ExtentCount记录了该文件分成了n块存储。
StartingVcn记录了起始虚拟簇号。
Extents[i].NextVcn记录了下一个块的起始虚拟簇号,所以Extents[i].NextVcn - Extents[i - 1].NextVcn表示i块所占的簇数。
Extents[i].Lcn记录了第i + 1块的起始逻辑簇号。注意i是从0开始的。
本机器狗使用了不严格的计算方法,仅用到了该结构中的Extents[0].Lcn,即文件的起始逻辑簇号,并没有考虑分多块存储的情况。因此,理论上是存在破坏系统文件的风险的。这也是病毒不负责任的地方。

8、MyRead_WriteSector
从这个函数名可以看出这是一个读写扇区的例程。前缀My表示该函数是由我标识的,用来跟ida自动识别的函数进行区别,以便查找。这个函数很重要,整个驱动中会调用三次,所以先解析。
MyRead_WriteSector(DeviceObj, IRP_ID, pBuffer, dwSectorLowPos, SecNum);
DeviceObj:操作设备对象,这里为Disk。
IRP_ID:3表示读取,4表示写入。
pBuffer:写入设备的数据地址,或读取数据的内存辞职。
dwSectorLowPos:读取或写入的扇区号。其实扇区号为LARGE_INTEGER类型的大数,但这里仅使用了低32位,没有使用高32位。
SecNum:读取或写入的扇区数。
下面为函数的实现介绍。
1)、通过使用MyIoCallDriver(病毒自己实现的IoCallDriver)来向Disk设备发送服务号为IRP_MJ_INTERNAL_DEVICE_CONTRO,通过向SCSI端口读写数据实现的。
2)、填充IRP
mov      [edi+_IRP.MdlAddress], eax; eax为pBuffer对应的Mdl
......     ;省略部分代码
mov      [ebp+UserIosb], ebx   ; ebx == 0
lea      eax, [ebp+UserIosb]
mov      [edi+_IRP.UserIosb], eax
lea      eax, [ebp+Event]
mov      [edi+_IRP.UserEvent], eax
mov      [edi+1Ch], ebx   ; _IRP.Information = 0
mov      [edi+18h], ebx   ; _IRP._IO_STATUS_BLOCK = 0
or       [edi+_IRP.Flags], 5 ; IRP_SYNCHRONOUS_API | IRP_NOCACHE
mov      [edi+0Ch], ebx   ; _IRP.IrpCount = 0
mov      [edi+24h], bl    ; _IRP.Cancel = 0
mov      [edi+38h], ebx   ; _IRP.CancelRoutine = 0
mov      [edi+20h], bl    ; _IRP.RequestorMode = 0
call     KeGetCurrentThread
mov      [edi+50h], eax   ; _IRP.Thread
mov      eax, [edi+60h]   ; _IRP.CurrentStackLocation
sub      eax, 24h         ; IoGetNextStackLocation
mov      byte ptr [eax], IRP_MJ_INTERNAL_DEVICE_CONTROL
mov      [eax+4], esi     ; _IO_STACK_LOCATION.scsi._SCSI_REQUEST_BLOCK
mov      ecx, [ebp+DiskDeviceObj]
mov      [eax+14h], ecx   ; _IO_STACK_LOCATION.DeviceObject = DiskDeviceObj
mov      eax, [edi+60h]
sub      eax, 24h
mov      dword ptr [eax+1Ch], offset fnCompletionRoutine ; _IO_STACK_LOCATION.CompletionRoutine
mov      [eax+20h], esi   ; _IO_STACK_LOCATION.Context
mov      [eax+_IO_STACK_LOCATION.Control], 0E0h
从填充的IRP可以看到一个重要的结构_SCSI_REQUEST_BLOCK。
3)、_SCSI_REQUEST_BLOCK
关于结构的定义可以找相关资料。用来存放向SCSI端口写数据的一些信息。这里看一下对这个srb的填充。
mov      word ptr [esi], 40h ; _SCSI_REQUEST_BLOCK.Length = 40h
mov      [esi+2], bl      ; _SCSI_REQUEST_BLOCK.Function = 0
mov      eax, [ebp+pBuffer]   ;pBuffer指向传入的参数,即存放数据的地址。
mov      [esi+18h], eax   ; _SCSI_REQUEST_BLOCK.DataBuffer = pBUffer
movzx    eax, [ebp+SecNum]
shl      eax, 9           ; eax = SecNum * 512
mov      [esi+10h], eax   ; _SCSI_REQUEST_BLOCK.DataTransferLength = SecNum * 512 //即写入数据的大小
mov      byte ptr [esi+9], 20h ; _SCSI_REQUEST_BLOCK.QueueAction = 20h
mov      [esi+3], bl      ; _SCSI_REQUEST_BLOCK.SrbStatus = 0
mov      [esi+4], bl      ; _SCSI_REQUEST_BLOCK.ScsiStatus = 0
mov      [esi+20h], ebx   ; _SCSI_REQUEST_BLOCK.NextSrb = NULL
mov      [esi+1Ch], ecx   ; _SCSI_REQUEST_BLOCK.SenseInfoBuffer = pSenseInfoBuffer
mov      byte ptr [esi+0Bh], 12h ; _SCSI_REQUEST_BLOCK.SenseInfoBufferLength = 12h
xor      eax, eax
cmp      [ebp+IRP_ID], IRP_MJ_READ ; if(IRP_ID != IRP_MJ_READ)
setnz    al               ; {al = 1;}
dec      eax              ; eax --;
and      eax, 0FFFFFFC0h ; eax &= 0x0ffffffc0;
add      eax, 80h         ; eax += 80h;
; //if IRP_ID == IRP_MJ_READ eax == 40h SRB_FLAGS_DATA_IN
; //else eax == 80h SRB_FLAGS_DATA_OUT
mov      [esi+0Ch], eax   ; _SCSI_REQUEST_BLOCK.SrbFlags = 80h
cmp      [ebp+IRP_ID], IRP_MJ_READ
jnz      short loc_11997
or       eax, 200h SRB_FLAGS_ADAPTER_CACHE_ENABLE
mov      [esi+0Ch], eax   ; _SCSI_REQUEST_BLOCK.SrbFlags = 240h
loc_11997:
or       dword ptr [esi+0Ch], 20h ; _SCSI_REQUEST_BLOCK.SrbFlags = A0h(If Read 260h)
; 20h == SRB_FLAGS_DISABLE_AUTOSENSE
mov      eax, [esi+10h]   ; eax = _SCSI_REQUEST_BLOCK.DataTransferLength
shr      eax, 0Ah
inc      eax
mov      [esi+14h], eax   ; _SCSI_REQUEST_BLOCK.TimeOutValue = _SCSI_REQUEST_BLOCK.DataTransferLength >> 0Ah + 1
mov      eax, [ebp+dwSectorLowPos]
mov      [esi+2Ch], eax   ; _SCSI_REQUEST_BLOCK.QueueSortKey = dwSectorLowPos
mov      byte ptr [esi+0Ah], 0Ah ; _SCSI_REQUEST_BLOCK.CdbLength = 0Ah
填充这个结构读数据和写数据的SrbFlags分别设为SRB_FLAGS_DATA_IN | SRB_FLAGS_ADAPTER_CACHE_ENABLE |
SRB_FLAGS_DISABLE_AUTOSENSE和SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_AUTOSENSE。
4)、_CDB
在_SCSI_REQUEST_BLOCK结构最后是UCHAR Cdb[16]; 但是Cdb不一定是16个字节,长度由_SCSI_REQUEST_BLOCK.CdbLength决定,从上面可以看出这里是0Ah个字节。该结构是一个联合体,这里使用的是_CDB10结构。

 

1
<< < 1 2 > >> 1/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】 【关闭】 【返回顶部
[上一篇]QQ for Linux新体验 [下一篇]新推出的360杀毒误杀QQ游戏中心

评论

称  呼:
验 证 码:
内  容:

相关栏目

最新文章

热门文章

推荐文章

相关文章

广告位