• 动态获取API函数地址对抗win7 aslr安全机制(转)


        本人近期在研究缓冲区溢出,在学习中发现,win7下系统关键函数的地址随机化了(每次重启后地址有变),为了解决地址定位问题,在偌大的互联网上找了好久,贴来分享下,以作备用。

    ------------------------------------------------------------------------------------------------------------------

    cvc论坛里好久没人写基础文章了,我就大胆地来个大家写个有关API函数地址获取的文章,希望对初学病毒的你有所帮助
    要想动态地获得一个API函数的地址,我们通常都是调用系统的LoadLibraryA()函数和GetProcAddress()函数来动态地获得。LoadLibraryA()用来加载API函数对应的动态链接库

    (dll),GetProcAddress()函数用来获得API函数对应的入口地址。
    那如何获得LoadLibraryA()和GetProcAddress()的地址呢?
    我们知道LoadLibraryA()和GetProcAddress()都是由kernel32.dll引出的函数,所以LoadLibraryA()的地址也可以由GetProcAddress()得到。那么GetProcAddress()的地址如何得

    到呢?
    上面说过GetProcAddress()的地址在kernel32.dll中,具体地说是在kernel32的引出表中,所以我们的问题变为如何找到kernel32的引出表地址,或者说如何找到kernel32的基地

    址。
    找kernel32基地址的方法一般有三种:暴力搜索法、异常处理链表搜索法、PEB法。
    暴力搜索法是最早的动态查找kernel32基地址的方法。它的原理是几乎所有的win32可执行文件(pe格式文件)运行的时候都加载kernel32.dll,可执行文件进入入口点执行后esp

    存放的一般是Kernel32.DLL 中的某个地址,所以沿着这个地址向上查找就可以找到kernel32的基地址。
    那么如何知道我们找到的地址是kernel32的基地址呢?
    因为kernel32.dll也是标准的pe结构文件,pe结构文件的开始是IMAGE_DOS_HEADER结构,IMAGE_DOS_HEADER结构的第一个字段是e_magic,它的值为’MZ’用于证明这是DOS兼容的

    文件类型,所以如果我们找到的地址所指向的字符串为’MZ’,那么我们可以确信这是kernel32的基地址,具体代码如下:
    Find_kernel32:
        mov eax,[esp]
        and eax,00000fffh;因为kernel32的基地址在内存中页的开始处,这代码的作用是对齐
        ;页的开始
    compare:
        cmp eax,40000h;比较是否低于应用程序的边界
        jb find_kernel32_fail;低于则查找失败
        cmp word ptr[eax],’ZM’;比较e_magic,因为Intel CPU是小端模式所以是’ZM’不是’MZ’
        je kernel32_found
        sub eax.100h;以页为单位加速查找
        jmp compare
    kernel32_found:
    补充:and eax,00000ffffh也可以换成or eax,00000fffh xor eax,00000fffh 我更喜欢后一种:)

    暴力搜索是有缺陷的,如果搜索的内存为不可读时,电脑就会蓝屏,所以毒客们发明了更安全的异常处理链表搜索法。所谓异常处理链表就是系统提供的处理异常的机制,当系统

    遇到一个不知道如何处理的异常时就会查找异常处理链表,找到对应的异常处理程序,把保存的处理程序地址赋给eip,并执行处理程序,避免系统崩溃,异常处理链表的最后一项

    是默认异常处理函数UnhandledExceptionFilter,因为UnhandledExceptionFilter在kernel32中,所以从UNhandledExceptionFilter地址向上搜索即可找到kernel32的基地址,具体

    代码如下:
    Load_SEH:
        xor edx, edx;edx=0
        push dword ptr fs:{edx];注册SEH异常处理函数
        mov fs:[edx],esp
        mov eax,[esp+(12*4)];指向SEH末尾
        xor ax,ax; 对齐于64k边界开始
    find_kernel32:
        cmp eax,40000h;比较是否低于应用程序的边界
        jb find_kernel32_fail;低于则查找失败
        cmp word ptr[eax],’ZM’;比较MZ标志
        je kernel32_found
        sub eax,65536;以64k为边界加速查找
        jmp find_kernel32
    kernel32_found:
    补充:找UnhandledExceptionFilter函数地址还可以用如下方法UnhandledExceptionFilter指针是在异常链表的最后,它的上一个值是指向下一个处理点的地址,因为后面没有异常

    处理点了,所以是0xffffffff,所以获得UnhandledExceptionFilter地址的代码为:
    GetExceptionFilter:
        cmp [eax],0xffffffff
        je GetedExceptionFilter
        mov eax,[eax]
    jmp GetExceptionFilter
    GetedExceptionFilter:
        mov eax,[eax+4]

    随着黑客技术的发展,后来出现了更为简单的方法:PEB法。(结构示意图来自 http://bbs.nyasama.com/forum.php?mod=viewthread&tid=585 )

    -----------------------------------------PEB--结构--------------------------------------------------------------

    View Code
      1 01.typedef struct _PEB
    2
    3 02.{
    4
    5 03. UCHAR InheritedAddressSpace; // 00h
    6
    7 04. UCHAR ReadImageFileExecOptions; // 01h
    8
    9 05. UCHAR BeingDebugged; // 02h
    10
    11 06. UCHAR Spare; // 03h
    12
    13 07. PVOID Mutant; // 04h
    14
    15 08. PVOID ImageBaseAddress; // 08h
    16
    17 09. PPEB_LDR_DATA Ldr; // 0Ch
    18
    19 10. PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
    20
    21 11. PVOID SubSystemData; // 14h
    22
    23 12. PVOID ProcessHeap; // 18h
    24
    25 13. PVOID FastPebLock; // 1Ch
    26
    27 14. PPEBLOCKROUTINE FastPebLockRoutine; // 20h
    28
    29 15. PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
    30
    31 16. ULONG EnvironmentUpdateCount; // 28h
    32
    33 17. PVOID* KernelCallbackTable; // 2Ch
    34
    35 18. PVOID EventLogSection; // 30h
    36
    37 19. PVOID EventLog; // 34h
    38
    39 20. PPEB_FREE_BLOCK FreeList; // 38h
    40
    41 21. ULONG TlsExpansionCounter; // 3Ch
    42
    43 22. PVOID TlsBitmap; // 40h
    44
    45 23. ULONG TlsBitmapBits[0x2]; // 44h
    46
    47 24. PVOID ReadOnlySharedMemoryBase; // 4Ch
    48
    49 25. PVOID ReadOnlySharedMemoryHeap; // 50h
    50
    51 26. PVOID* ReadOnlyStaticServerData; // 54h
    52
    53 27. PVOID AnsiCodePageData; // 58h
    54
    55 28. PVOID OemCodePageData; // 5Ch
    56
    57 29. PVOID UnicodeCaseTableData; // 60h
    58
    59 30. ULONG NumberOfProcessors; // 64h
    60
    61 31. ULONG NtGlobalFlag; // 68h
    62
    63 32. UCHAR Spare2[0x4]; // 6Ch
    64
    65 33. LARGE_INTEGER CriticalSectionTimeout; // 70h
    66
    67 34. ULONG HeapSegmentReserve; // 78h
    68
    69 35. ULONG HeapSegmentCommit; // 7Ch
    70
    71 36. ULONG HeapDeCommitTotalFreeThreshold; // 80h
    72
    73 37. ULONG HeapDeCommitFreeBlockThreshold; // 84h
    74
    75 38. ULONG NumberOfHeaps; // 88h
    76
    77 39. ULONG MaximumNumberOfHeaps; // 8Ch
    78
    79 40. PVOID** ProcessHeaps; // 90h
    80
    81 41. PVOID GdiSharedHandleTable; // 94h
    82
    83 42. PVOID ProcessStarterHelper; // 98h
    84
    85 43. PVOID GdiDCAttributeList; // 9Ch
    86
    87 44. PVOID LoaderLock; // A0h
    88
    89 45. ULONG OSMajorVersion; // A4h
    90
    91 46. ULONG OSMinorVersion; // A8h
    92
    93 47. ULONG OSBuildNumber; // ACh
    94
    95 48. ULONG OSPlatformId; // B0h
    96
    97 49. ULONG ImageSubSystem; // B4h
    98
    99 50. ULONG ImageSubSystemMajorVersion; // B8h
    100
    101 51. ULONG ImageSubSystemMinorVersion; // C0h
    102
    103 52. ULONG GdiHandleBuffer[0x22]; // C4h
    104
    105 53. PVOID ProcessWindowStation; // ???
    106
    107 54.} PEB, *PPEB;

    -----------------------------------------------------------------------------------------------------------------

    原理如下:在NT内核系统中fs寄存器指向TEB结构,TEB+0x30处指向PEB结构,PEB+0x0c处指向PEB_LDR_DATA结构,

    PEB_LDR_DATA+0x1c处存放一些动态链接库地址,第一个指向ntdl.dll,第二个就是kernel32.dll的基地址了。对于非NT内核的9x系统也有类似的链接结构,但是我没有找到相关的

    文档,无法做出解释,记下来就好了,就像考前临时抱佛脚那样:) PEB法的代码如下:
    find_kernel32:
    push esi
    xor eax, eax
    assume fs:nothing
    mov eax, fs:[eax+030h]; eax指向PEB结构
    test eax, eax;是否为9x
    js find_kernel32_9x
    find_kernel32_nt:
    mov eax, [eax + 0ch];eax指向PEB_LDR_DATA结构
    mov esi, [eax + 01ch];
    lodsd
    mov eax, [eax + 08h];eax中为kernel基地址
    jmp find_kernel32_finished
    find_kernel32_9x:
    mov eax, [eax + 034h]
    lea eax, [eax + 07ch]
    mov eax, [eax + 03ch]
    kernel32_found:
    pop esi
    补充:只用cmp word ptr[eax],’ZM’来验证kernel32的基地址有点太单薄,我们可以加点东西,在IMAGE_DOS_HEADER+0x3ch处是e_lfanew存放IMAGE_NT_HEAGER结构的文件地址,

    IMAGE_NT_HEAGER结构的第一个字段是signature值为’PE’用于证明是PE文件头,我们还可以检查文件的重分配表的文件地址值是否为40h,子系统是否为win32系统,是否是DLL文

    件,具体代码为:
    assume esi :ptr IMAGE_DOS_HEADER
    cmp [esi].e_magic,IMAGE_DOS_SIGNATURE 
    jne find_kernel32
    cmp [esi]. e_lfarlc,040h;比较重分配表的文件地址
    jne find_kernel32
    add esi,[esi].e_lfanew ;此时edx指向PE文件头
    assume esi:ptr IMAGE_NT_HEADERS
    cmp [esi].Signatur,IMAGE_NT_SIGNATURE ;是PE文件吗?
    jne find_kernel32
    cmp word ptr [esi].OptionalHeader.Subsystem,2比较子系统
    jne find_kernel32
    cmp word ptr [esi]. OptionalHeader.DllCharacterstics,00100000b;比较DLL状态

    得到了kernel32的基地址,下一步就是定位到引出表找到GetProcAddress的地址。PE头+120处就是引出表的文件地址,引出表中有三个我们需要的字段:
    AddressOfFunction字段:指向模块中所有函数地址的数组
    AddressOfNames字段:指向模块中所有函数名称的数组
    AddressOfNameOrdinals字段:指向AddressOfNames数组中函数对应序数的数组
    我们可以这样差找函数地址,用函数的名称在AddressOfName指向的数组中找对应的序号,序号乘以2后在AddressOfNameOrdinals指向的数组中找对应的序数,序数乘以4后在

    AddressOfFunction指向的数组中找对应的函数地址,具体代码如下:
    输入;
    esi=要查找的函数名
    eax=函数所在动态链接库地址
    输出:
    eax=函数的地址
    GetFunctionAddress PROC
    mov ebx, [eax + 3Ch];指向PE头
    add ebx, eax
    add ebx, 120
    mov ebx, [ebx]
    add ebx, eax ;ebx=引出表地址
    xor edx, edx
    mov ecx, [ebx + 32];AddressOfNames
    add ecx, eax
    push     esi
    push edx
    CompareNext: 在函数名数组中查找下一个函数名
    pop edx
    pop esi
    inc edx
    mov edi, [ecx]
    add edi, eax
    add ecx, 4
    push     esi
    push     edx
    CompareName:   ;比较要查找的函数名是否于函数名数组中的一致
    mov dl, [edi]
    mov dh, [esi]
    cmp dl, dh
    jne CompareNext
    inc edi
    inc esi
    cmp byte ptr [esi], 0
    je GetAddress
    jmp CompareName
    GetAddress:
    pop edx
    pop esi
    dec edx
    shl edx, 1;乘2
    mov ecx, [ebx + 36];AddressOfNameOrdinals
    add ecx, eax
    add ecx, edx
    xor edx, edx
    mov dx, [ecx]
    shl edx, 2;乘4
    mov ecx, [ebx + 28];AddressOfFunction
    add ecx, eax
    add ecx, edx
    add eax, [ecx]
    ret
    GetFunctionAddress ENDP

    通过以上讨论GetProcAddress地址可以用如下代码获得
    mov eax ,[ebp+Kernel32]
    lea esi,[ebp+API_GetProcAddress]
    call GetFuntionAddress
    mov [ebp+ADDR_GetProcAddress],eax
    对于kernel32引出的函数都可以用如上的方法获得地址
    对于非kernel32引出的函数如MessageBoxA()可以这样获取:
    Lea ebx,[ebp+offset_DLL_User32]
    push ebx
    call [ebp+ADDR_LoadLibraryA];装载 user32.dll  
    push eax ;eax = user32的基地址
    lea esi,[ebp+API_MessageBoxA]
    push esi
    call [ebp+ADDR_GetProcAddressA]
    mov [ebp+ADDR_MessageBoxA],eax
    补充:现在的杀毒软件都对GetProcAddress函数挂钩,会对调用GetProcAddress函数的程序进行检查,所以为了绕过这保护机制我们不用GetProcAddress函数,像获取kernel32引

    出函数那样,直接在动态链接库中找相应的函数地址,还是以MessageBoxA为例,代码如下:
    lea esi, [ebp + API_LoadLibraryA]
    call GetFunctionAddress
    lea ebx, [ebp + offset_DLL_User32]
    push     ebx
    call eax ; 装载 user32.dll   eax = user32的基地址
    lea esi, [ebp + API_MessageBoxA]
    call GetFunctionAddress
    mov [ebp + ADDR_MessageBoxA], eax
    就写这么多了有错误还希望大家指出来,下一次写有关简单病毒优化的文章希望大家支持:)

  • 相关阅读:
    mysql 数据操作 单表查询 group by 注意
    mysql 数据操作 单表查询 group by 聚合函数
    mysql 数据操作 单表查询 group by 聚合函数 没有group by情况下
    mysql 数据操作 单表查询 group by 介绍
    【洛谷P3586】LOG
    【YbtOJ#20078】路径之和
    【ABC181】F
    【洛谷P7045】金牌
    【AT2165】Median Pyramid Hard
    【洛谷P3708】koishi的数学题
  • 原文地址:https://www.cnblogs.com/witty/p/2413890.html
Copyright © 2020-2023  润新知