对于一个经常写程序的人来说,写驱动不是一件困难的事情。因为网络上有很多现成的
代码,要实现某个功能,直接 Ctrl+C和Ctrl+V 就能解决问题。但是写出来了驱动能不能加
载进入内核就是另外一回事了,准确的说是能不能存在于别人的硬盘上就是另外一回事了。
因为很多杀毒软件(特别像360这种没技术含量的)见到后缀名为sys的文件就直接删除,
甚至连调用NtLoadDriver的机会都没有。对于一般的软件来说,给出一个声明说明一下解
决方法就算了。但是对于恶意程序,是不能给出声明的。于是很多恶意软件的作者另辟蹊径,
利用大公司写好的而且有数字签名的驱动来做坏事。
有人说,大公司做好的驱动怎么可能被用来做坏事呢?其实,这是很容易理解的事情。
很多安全类或者系统优化类的软件,甚至系统毫不相关的软件(比如:迅雷)都附带有驱动。
这些驱动都带有一定的通用性。q_lai_a_qu网友在其博客里说:“ComputerZ.sys……没事
逆了逆是鲁大师的驱动,发现这个驱动功能齐全,而且没有调用者验证!既可以读、写Msr
寄存器,也可以用in、out指令读写端口,而且char/short/long数据长度齐全!”。这个是
个人之言,可信度请自行揣度。下面说个可信度比较高的例子:曾经有病毒利用了360的
AntiRK.dll来删除杀毒软件的文件(请自行用谷歌搜索“360 antirk.dll”,会有惊喜发现。
AntiRK.dll虽然不是驱动,但也是被非法利用了)。破坏杀毒软件的病毒已经算是小儿科了,
其实利用某些驱动还能破坏硬件!我最近在笔记本上折腾硬件,“本友会”上的网友给我推
荐了几款软件:SetFSB、ThrottleStop、NvFlash、WinFlash。它们分别是修改CPU外频、设
置CPU倍频(可以调节CPU电压)、读写显卡BIOS和读写主板BIOS的软件。一言概括他们的特性,
就是它们都支持NT x86/x64,它们的驱动都有正规数字签名(特别是最后两个,分别带的是 NVIDIA和ASUS的数字签名)。

最为重要的是,他们的驱动没有加花加壳,没有校验调用者,
如果利用这几个驱动,加上一丁点的逆向知识,就能做出破坏性的病毒(以下摘自我在紫水
晶编程论坛的帖子):
1.SetFSB能调节处理器的外频,如果直接把外频调到600MHz,电脑会瞬间黑屏,可能
会损坏 CPU或主板;
2.ThrottleStop能调节 CPU的倍频(如果CPU没有锁倍频),如果直接把倍频调到 31,

电脑会瞬间黑屏,可能会损坏CPU 或主板;ThrottleStop还能调节CPU的核心电压,如果
把CPU的核心电压调到3V,能直接烧毁CPU 甚至主板;
3.NvFlash、WinFlash等软件能直接读写BIOS(显卡BIOS 和主板BIOS),我们可以把
BIOS全部写零;
4.如果做病毒的话,先写坏显卡BIOS 和主板BIOS,然后通过调节电压烧掉显卡和CPU
(有可能会连同主板一起损坏);

解决方案

由此可见,没有验证调用者的驱动实在是有着巨大的危害。我最近受学院委托,做一个
需要驱动的软件(那个驱动会被加上数字签名)。为了防止上述悲剧发生,我决定在正式写
驱动之前,先解决如何防止自己的驱动被恶意利用。以前我曾经在紫水晶编程论坛上问过这
个问题,网友的回答五花八门,不过大概是可以分成三类:第一类是信息验证,比如应用程
序发个信息给驱动来验证一下是“自己人”;第二类是加壳保护,比如给驱动和应用程序加
上极强难脱的壳,利用VMP加密通信部分(类似XueTr 的做法);还有人提出混合应用,综
合第一类和第二类的做法。
这三种想法看似都不错,但是我认为不妥。第一种:别人只要把驱动全部逆向完毕就行
了;第二种:虽然VMP保护和加保护壳使得破解不容易,但是不是使破解变得不可能。而且
VMP 和保护壳能使程序执行的效率降低,我不太喜欢。最可恶的是,杀毒软件对加了壳(甚
至包括 UPX)和 VMP的程序一律报毒,得不偿失。于是我想出了第三种思路:校验调用者的
特征。如果符合,就执行功能语句,否则不予执行。如何校验调用者的特征码呢?不少人想
到的是使用CRC32 或者 MD5。使用它们不是不可以,不过我还有自己的想法。我的想法是自
己设计一套验证算法,它的规则如下:
1.获得调用者的EPROCESS
2.通过调用者的EPROCESS获得调用者的文件路径
3.获取调用者的文件全部内容,放到字节数组buff里
4.把 buff里所有的元素依次相加减(fb1 + fb2 - fb3...),得到y1

5.把 buff里所有的元素依次异或(0 XOR fb1 XOR fb2 XOR fb3...),得到y2
把 y1和 y2与已经计算出来的数值对比,如果都相同则执行功能代码,如果不相同则不
执行功能代码
获得调用者的EPROCESS直接用 PsGetCurrentProcess()就行了,获得调用者的文件路
径比较麻烦,大家可以使用我以前向高手购买的代码(已经封装为函数,方便调用):  
//依据 EPROCESS得到进程全路径
VOID GetFullPathByEprocess( ULONG eprocess, PCHAR ProcessImageName )
{
    ULONG object;
    PFILE_OBJECT FileObject;
    UNICODE_STRING FilePath;
    UNICODE_STRING DosName;
    STRING AnsiString;
    FileObject = NULL;
    FilePath.Buffer = NULL;
    FilePath.Length = 0;   
   *ProcessImageName = 0; 
    //Eprocess->sectionobject(offset_SectionObject)
if(MmIsAddressValid((PULONG)(eprocess+offset_SectionObject)))   
{
        object=(*(PULONG)(eprocess+offset_SectionObject));
        //KdPrint(("[GetProcessFileName] sectionobject :0x%x\n",object));
        if(MmIsAddressValid((PULONG)((ULONG)object+0x014)))
        {
            object=*(PULONG)((ULONG)object+0x014);
            //KdPrint(("[GetProcessFileName] Segment :0x%x\n",object));
            if(MmIsAddressValid((PULONG)((ULONG)object+0x0)))
            {
                object=*(PULONG)((ULONG_PTR)object+0x0);
                //KdPrint(("[GetProcessFileName]
ControlAera :0x%x\n",object));
                if(MmIsAddressValid((PULONG)((ULONG)object+0x024)))
                {
                    object=*(PULONG)((ULONG)object+0x024);  
                    if (NtBuildNumber >= 6000) object=((ULONG)object &
0xfffffff8);
                    //KdPrint(("[GetProcessFileName]
FilePointer :0x%x\n",object));
                }
                else
                    return ;
            }
            else
                return ;
        }
        else
            return ;
    }
    else
        return ;

FileObject=(PFILE_OBJECT)object;
    FilePath.Buffer = ExAllocatePool(PagedPool,0x200);
    FilePath.MaximumLength = 0x200;
    //KdPrint(("[GetProcessFileName]
FilePointer :%wZ\n",&FilePointer->FileName));
    ObReferenceObjectByPointer((PVOID)FileObject,0,NULL,KernelMode);
RtlVolumeDeviceToDosName(FileObject-> DeviceObject, &DosName);
  RtlCopyUnicodeString(&FilePath, &DosName);
    RtlAppendUnicodeStringToString(&FilePath, &FileObject->FileName); 
   ObDereferenceObject(FileObject);
    RtlUnicodeStringToAnsiString(&AnsiString, &FilePath, TRUE);
    if ( AnsiString.Length >= 216 )
    {
        memcpy(ProcessImageName, AnsiString.Buffer, 0x100u);
        *(ProcessImageName + 215) = 0;
    }
    else
    {
        memcpy(ProcessImageName, AnsiString.Buffer, AnsiString.Length);
        ProcessImageName[AnsiString.Length] = 0;
    }
    RtlFreeAnsiString(&AnsiString);
    ExFreePool(DosName.Buffer);
    ExFreePool(FilePath.Buffer);
}

以上代码需要三个硬编码,分别是NtBuildNumber(系统版本号)、EPROCESS中的
SectionObject项和UniqueProcessId项的偏移。我测试的操作系统是Windows 2003。所以
我在代码里如下定义:
#define offset_SectionObject 0x124
#define offset_UniqueProcessId 0x94
ULONG NtBuildNumber=3790;

获得进程路径后就校验特征码。由于流程已经说清楚了,所以直接给出代码:

VOID CalcChar(PUNICODE_STRING logFileUnicodeString, LONG *XorChar, LONG
*AnSChar)
{
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK iostatus;
    HANDLE hfile;
    NTSTATUS ntStatus;
    FILE_STANDARD_INFORMATION fsi;
    PUCHAR pBuffer;
    ULONG i=0,y1=0,y2=0;
    //初始化 objectAttributes
    InitializeObjectAttributes(&objectAttributes,
                            logFileUnicodeString,
                            OBJ_CASE_INSENSITIVE,//对大小写敏感
                            NULL,
                            NULL);
//创建文件
    ntStatus = ZwCreateFile(&hfile,
      GENERIC_READ,
                            &objectAttributes,
                            &iostatus,
                            NULL,
                            FILE_ATTRIBUTE_NORMAL,
                            FILE_SHARE_READ,
                            FILE_OPEN,//即使存在该文件,也创建
                            FILE_SYNCHRONOUS_IO_NONALERT,
                            NULL,
                            0 );
    if (!NT_SUCCESS(ntStatus))
    {
        dprintf("The file is not exist!\n");
        return;
    }
    //读取文件长度
    ntStatus = ZwQueryInformationFile(hfile,
                                    &iostatus,
                                    &fsi,
                                    sizeof(FILE_STANDARD_INFORMATION),
                                    FileStandardInformation);
    dprintf("The program want to read %d bytes\n",fsi.EndOfFile.QuadPart);
    //为读取的文件分配缓冲区
     pBuffer = (PUCHAR)ExAllocatePool(PagedPool,
(LONG)fsi.EndOfFile.QuadPart);
    //读取文件
    ZwReadFile(hfile,NULL,
                NULL,NULL,
                &iostatus,
                pBuffer,
                (LONG)fsi.EndOfFile.QuadPart,
                NULL,NULL);
    dprintf("The program really read %d bytes\n",iostatus.Information);
    //异或计算
    for(i=0;i<iostatus.Information;i++)
        y1=y1^(LONG)(*(pBuffer+i));
    *XorChar=y1;
    //加减计算
    for(i=0;i<iostatus.Information;i++)
    {
        if(i%2==0)
  y2=y2+(LONG)(*(pBuffer+i));
        else 
          y2=y2-(LONG)(*(pBuffer+i));
    }
    *AnSChar=y2;
    //关闭文件句柄
    ZwClose(hfile);
    //释放缓冲区
    ExFreePool(pBuffer);
}

接下来就要调用了。我们需要编写一个函数VerifyCaller,在此函数里有两个值需要
固化在驱动里,就是合法调用者的两个特征值。为了方便计算这两个特征值,我特地写了个
应用程序,核心代码如下:
Option Explicit
Private Function ReadFile(ByVal strFileName As String, Optional ByVal
lngStartPos As Long = 1, Optional ByVallngFileSize As Long = -1) As Byte()
    Dim FilNum As Long
    FilNum = FreeFile
    Open strFileName For Binary As #FilNum
    If lngFileSize = -1 Then
        ReDim ReadFile(LOF(FilNum) - lngStartPos)
    Else
        ReDim ReadFile(lngFileSize - 1)
    End If
    Get #FilNum, lngStartPos, ReadFile
    Close #FilNum
End Function
Private Function WriteFile(ByVal strFileName As String, bytData() As Byte,
Optional ByVal lngStartPos As Long = -1,Optional ByVal OverWrite As Boolean =
True)
    On Error GoTo erx
    Dim FilNum As Long
    FilNum = FreeFile   
    If OverWrite = True And Dir(strFileName) <> "" Then
        Kill strFileName
End If
Open strFileName For Binary As #FilNum
    If lngStartPos = -1 Then
        Put #FilNum, LOF(FilNum) + 1, bytData
    Else
        Put #FilNum, lngStartPos, bytData
    End If
  Close #FilNum
erx:
  End Function
Private Sub Command1_Click()
    Dim buff() As Byte, i As Long, y As Long, ub As Long
    'text1.text is the file name
    buff = ReadFile(Text1.Text, 1, -1)
    ub = UBound(buff)
    'calc xor char
    y = 0
    For i = 0 To ub
        y = y Xor buff(i)
    Next
    Text2.Text = CLng(y)
    DoEvents
    'calc add/sub char
    y = 0
    For i = 0 To ub
        If i Mod 2 = 0 Then
            y = y + CLng(buff(i))
        Else
            y = y - CLng(buff(i))
        End If
    Next
    Text3.Text = CLng(y)
End Sub

Private Sub Form_Load()
    Me.Icon = LoadPicture("")
End Sub

驱动里的 VerifyCaller代码如下: 
LONG VerifyCaller(void)
{
    PEPROCESS cur_ep;
    char cur_pp[260];
    char *nt_cur_pp;
    ANSI_STRING asCur_pp;
    UNICODE_STRING usCur_pp;
    LONG xorc, ansc;
    cur_ep=PsGetCurrentProcess();
GetFullPathByEprocess((ULONG)cur_ep, cur_pp);
//在文件名前面加上\??\
    nt_cur_pp=cs("\\??\\",cur_pp);
    DbgPrint("%s",nt_cur_pp);
    RtlInitAnsiString(&asCur_pp, nt_cur_pp);
RtlAnsiStringToUnicodeString(&usCur_pp, &asCur_pp, TRUE);
    DbgPrint("%wZ",&usCur_pp);
    CalcChar(&usCur_pp, &xorc, &ansc);
    DbgPrint("XorChar: %ld; AnSChar: %ld",xorc,ansc);
    //这个就是事先算好的合法程序的特征码,【必须】固化在驱动里!
    if(xorc==186 && ansc==136176)
        return 1;

else
        return 0;
}

在 DispatchIoctl函数的每个功能执行之前,都调用VerifyCaller()校验一下调用者:   
switch(uIoControlCode)
{
    case IOCTL_VERIFY:
    {
        DbgPrint("[MyDriver] DispatchIoctl - IOCTL_VERIFY");
        if(VerifyCaller()==1)
            DbgPrint("[MyDriver] {IOCTL_VERIFY} Function code run now!");
        else
            DbgPrint("[MyDriver] {IOCTL_VERIFY} You're illegal caller!");
        status = STATUS_SUCCESS;
        break;
    }
    //下面省略
}

运行测试
 
3.首先把合法的调用者,非法的调用者(用eXeScope随便把合法的调用者Patch一下,
比如删掉程序的版本信息)和驱动复制到虚拟机
4.用合法的调用者来加载驱动并执行
5.用非法的调用者来加载驱动并执行
6.对比以上两者在DbgView的输出

调用者合法时:

调用者非法时:

写在最后
 
写完这篇文章,我必须再次重申:只有当驱动程序携带正式数字签名时,验证调用者的
代码才有使用价值。为什么这么说呢?因为别人无法patch 带有正式数字签名的驱动(一旦
驱动被 patch,签名就失效了,就像被破处的女人,不值钱了。这个比喻虽然粗俗,但是很
恰当)。而没有加上签名的驱动,本来就没有使用价值。即使别人要使用,直接把驱动扔到
IDA 里,什么代码都出来了。

转载地址 http://www.jybase.net/ruanjianpojie/20111119635.html

转载于:https://blog.51cto.com/2817071/719487

如何防止驱动被恶意利用相关推荐

  1. Linux中基于eBPF的恶意利用与检测机制(rootkit、驱动)

    目录 前言 现状分析 海外资料 国内资料 eBPF技术恶意利用的攻击原理 网络层恶意利用 Linux系统运行时恶意利用 综述 检测防御 运行前 运行时 运行后 防御 工程实现 系统兼容性 CO-RE ...

  2. 微信微博聊天记录可作为证据 如何防止聊天记录被恶意利用?可以使用BTchat链语阅后即焚功能

    依据12月26日公布的 <最高人民法院关于修改<关于民事诉讼证据的若干规定>的决定>,今后微信微博聊天记录可正式作为打官司的证据. TVB中,警察逮捕嫌疑犯的时候一般都会说的那 ...

  3. 中国开发者无条件退款被人恶意利用 跪求同胞放过

    BLUE SKY是一名独立游戏开发者,在Steam上目前共有三款他制作的游戏,游戏一般都和"R18""黄油"挂钩,这些打着擦边球的游戏售价都不高,都是11元,游 ...

  4. 云计算里的安全:警惕云服务被恶意利用

    云计算拥有庞大的计算能力与丰富的计算资源,恶意攻击者正在越来越多的利用合法的云计算实施恶意攻击. 今年四月份的索尼被黑客攻击事件,就是恶意攻击者利用亚马逊弹性云计算服务对索尼PlayStation N ...

  5. 良性代码,恶意利用:浅谈 Return-Oriented 攻击

    众多的安全漏洞中,栈溢出(stack-based buffer overflows)算是非常常见的了.一方面因为程序员的疏忽,使用了 strcpy.sprintf 等不安全的函数,增加了栈溢出漏洞的可 ...

  6. 各位同意转载博文的善意,是否被恶意利用?文章被转载了,该不该收钱?

    我在CSDN,承蒙管理员和各位朋友的帮助,目前写的文章点击量稍可,所以经常会有朋友来问文章能否被转载?在一开始,我在高兴之余就直接同意了,因为在我眼里,转载是个双赢的事情,而且出于相互尊重,转载者应该 ...

  7. 成也萧何,败也萧何?加密技术被恶意利用成为2019年最恶劣的攻击软件之一!...

    来源 | Bitcoinmagazine 编译 | 火火酱 责编 | Maozz.Carol 出品 | 区块链大本营(blockchain_camp) 老祖宗给我们留下过很多智慧结晶,比如一些流传甚广 ...

  8. Linux驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数

    1. 前言 很多朋友在调试驱动的时候,都会遇到这样一个场景:修改一个参数,然后调用某个内核中的函数. 比如将某个gpio的值拉高/拉低,修改某个寄存器的值等等. 如果每一个参数都通过字符设备的ioct ...

  9. WordPress插件WP Cost Estimation因漏洞被恶意利用

    过去几个月,商业WordPress插件WP Cost Estimation一直受到***的***.***们利用这些插件中的旧漏洞闯入网站并植入后门.这些持续的***首先被Defiant发现,他是Wor ...

  10. python页面驱动mxd_如何利用python 批量导出mxd至jpg

    展开全部 你好,arcpy.mapping提供了如下的函32313133353236313431303231363533e78988e69d8331333335313835数:arcpy.mappin ...

最新文章

  1. RDKit | 基于主成分分析可视化(DrugBank)类药性的化学空间
  2. GT Transceiver中的重要时钟及其关系(5)QPLL的工作原理介绍
  3. modbus从站模拟软件_这些操作软件都不知道?趁早别当电气人了
  4. SpringBoot+Vue+Echarts实现双柱体柱状图
  5. build-blocker-plugin
  6. SignalR Self Host+MVC等多端消息推送服务(4)
  7. sql server常用函数积累
  8. VC++2012编程演练数据结构《12》二叉排序树
  9. 好久没更新了,更新一篇,关于ZEC的吧
  10. Blue Coat推移动设备安全(MDS)服务
  11. PAT1003 我要通过! (20 分)(C语言)
  12. (Emitted value instead of an instance of Error) postcss-viewport-units:
  13. 【转】SD Card - UHS-I UHS Speed Class 1
  14. 家用宽带优化-光猫桥接,路由器拨号
  15. 二十六、 对偶问题(※※※)
  16. 全局基址 一级基址 二级基址 三级基址的关系
  17. 适合BS模式项目的录入页面
  18. 【物联网专题】1.1_物联网基本概念_什么是物联网(IoT)?
  19. 我手写了个SLAM算法!
  20. Excel使用VBA代码,每隔m行插入n行(可以是n行空白行,也可以是n行经复制的固定内容行)

热门文章

  1. 国内8款热门协作软件综合比较
  2. 鸿蒙断更术辰东,唐家三少说出《圣墟》断更真相,这次不怪辰东,网文将大范围断更...
  3. 【Cython】Cython 基本用法
  4. E.03.08. Scrapped Plans for London Concert Hall Sour Mood for U.K. Musicians
  5. 徒手撸一个记账本(附源码)
  6. 立创EDA——PCB的布局(四)
  7. 聚名师之力,扬信息之帆,逐教育现代化浪潮——韶关市教育信息化蓝凌名教师工作室挂牌仪式
  8. Phalcon PHP 中文,Phalcon 入门
  9. Uncaught SyntaxError The requested module ‘node_modules.vitevue.jsv=bd1817bb‘ does not provide
  10. Mac 如何彻底删除/卸载程序