Run x64-shellcode In dotNET (在 dotNET 中运行 x64 的 shellcode)

    本文将在 x64 的 dotNET 环境中嵌入一段汇编 shellcode,它的功能主要是“原子变量自增 / InterlockedIncrement32”的汇编实现,用于阐述如何在 x64 环境下运行你的 shellcode 代码。

    动态运行 shellcode 代码,在 Win32 运行一块代码,则即指向代码的内存必须具有 PAGE_EXECUTE_READ 的权限,否则视作缓冲区,在 dotNET ,你可以通过“VirtualProtectEx、VirtualProtect”两个函数修改其内存的属性值。

    同时一个重要点是,运行 x64 的 shellcode 要求一点是,你必须包含“汇编函数”的栈布局、平衡栈等过程,这与VC中直接内链汇编不同,只需要编写 body 部分(x64 的环境下VC处理内链接汇编也比较麻烦,但相对好得多)

         mov         dword ptr [rsp+8],ecx  
         push        rbp  
         push        rdi  
         sub         rsp,0C8h  
         mov         rbp,rsp  
         mov         rdi,rsp  
         mov         ecx,32h  
         mov         eax,0CCCCCCCCh  
         rep stos    dword ptr [rdi]  
         mov         ecx,dword ptr [rbp+00000000000000E0h]  
         mov         eax,1  
         lock xadd   dword ptr[ecx],eax 
         inc         eax   
         lea         rsp,[rbp+00000000000000C8h]  
         pop         rdi  
         pop         rbp  
         ret  

    以上是一串“原子自增”的汇编代码,它可以在 x64 的环境下正确的执行,其用户代码区域(body 部分)在下面的代码中被标识出来,它其实很简单仅仅只是做了“四句话”把 rbp+00000000000000E0h 的地址拷贝到 ecx 中,然后把 1 拷贝到 eax,然后在利用 x86/x64 处理器指令集提供的 lock xadd 指令将其相加,最后为 eax 寄存器加 1。

         mov         ecx,dword ptr [rbp+00000000000000E0h]  
         mov         eax,1  
         lock xadd   dword ptr[ecx],eax 
         inc         eax   

   说明一点在 x64 环境下是兼容,x86 环境的,这也是为什么 x86 应用可以运行在 x64 的处理器平台上面,但有一个比较重要的小点或许需要单独说明一点,在 x64 处理器中当机器代码操作类似如 eax (32位元)寄存器时,其与之相对应的 rax (64位元)寄存器的值,也将发生改变,唯一的区别就是可以存储的二进制位宽 是不同的,一个32,一个64。这就相当于,mov eax,1 那么 rax = 1,但 rax 寄存器设置超出 32bit 的数值时,eax 只可以反应其低 32bit 的值。


    dotNET 的 GC 是一个很强大的东西,但有时候它会替你增加很多的麻烦,一个形象的例子是当我们创建一段连续的 buffer 我们把它传入到一个非托管的应用中,但是在一段时候后 GC 将这块内存移走了,哎呀麻烦了!非托管的代码因为继续访问了这块内存而造成了“内存无法访问”问题,故因此崩溃,而这个问题在dotNET中运行shellcode也是存在的,所以我们需要告诉GC不要去移动这块内存,不然这很有可能在你的代码运行过程突然爆一个类似如“外部组件内部错误”之类的问题。

 “x64汇编代码”转换成“x64机器代码”是一个比较麻烦的问题,好在我们通过一些工具来解决这些问题,例如“A2BX64”,此工具你可以从“https://pan.baidu.com/s/1hyslxMuX1MULBY6OvQpEWA”这里获取。

    另一个比较麻烦的事情是,”A2BX64“   转换的”机器代码“只支持 vb 与 C 语言的格式,所以你还需要把它转换成例如 C#的数组的值,当你可以修改下面的 C 代码简单用用。

int main()
{
#define SHELLCODECOUNT 60

	char shellcode[60] =
		"\x89\x4C\x24\x08"             //3
		"\x55"                         //4
		"\x57"                         //5
		"\x48\x81\xEC\xC8\x00\x00\x00" //12
		"\x48\x8B\xEC"                 //15
		"\x48\x8B\xFC"                 //18
		"\xB9\x32\x00\x00\x00"         //23
		"\xB8\xCC\xCC\xCC\xCC"         //28
		"\xF3\xAB"                     //30
		"\x8B\x8D\xE0\x00\x00\x00"     //36
		"\xB8\x01\x00\x00\x00"         //41
		"\xF0\x67\x0F\xC1\x01"         //46
		"\xFF\xC0"                     //48
		"\x48\x8D\xA5\xC8\x00\x00\x00" //55
		"\x5F"                         //56
		"\x5D"                         //57
		"\xC3";                        //58

	for (int i = 0; i < SHELLCODECOUNT; i++) {
		if (i <= 0) {
			printf("{");
		}
		printf("%d", (int)(unsigned char)shellcode[i]);
		if (i + 1 >= SHELLCODECOUNT) {
			printf("}");
		}
		else {
			printf(",");
		}
	}
	return getchar();
}

    以下提供 dotNET at C# 语言的实现,但要运行此代码之前你必须要指定你的 C# 项目的目标平台为”x64“才可以,否则代码是无法被正确运行的。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

class Shell64
{
    [DllImport("kernel32.dll")]
    private static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int __InterlockedIncrement32(ref int n);
    /* 
         mov         dword ptr [rsp+8],ecx  
         push        rbp  
         push        rdi  
         sub         rsp,0C8h  
         mov         rbp,rsp  
         mov         rdi,rsp  
         mov         ecx,32h  
         mov         eax,0CCCCCCCCh  
         rep stos    dword ptr [rdi]  
         mov         ecx,dword ptr [rbp+00000000000000E0h]  
         mov         eax,1  
         lock xadd   dword ptr[ecx],eax 
         inc         eax   
         lea         rsp,[rbp+00000000000000C8h]  
         pop         rdi  
         pop         rbp  
         ret  
    */
    private static byte[] shellcode =
    {
        137,76,36,8,85,87,72,129,236,200,0,0,0,72,139,236,72,139,252,185,50,0,0,0,184,204,204,204,204,
        243,171,139,141,224,0,0,0,184,1,0,0,0,240,103,15,193,1,255,192,72,141,165,200,0,0,0,95,93,195,0
    };

    private static __InterlockedIncrement32 InterlockedIncrement32;

    static void Main()
    {
        IntPtr address = GCHandle.Alloc(shellcode, GCHandleType.Pinned).AddrOfPinnedObject();
        VirtualProtectEx(Process.GetCurrentProcess().Handle, address, (uint)shellcode.Length, 0x40, out uint lpflOldProtect);
        InterlockedIncrement32 = (__InterlockedIncrement32)Marshal.GetDelegateForFunctionPointer(address, typeof(__InterlockedIncrement32));
        {
            ISet<int> set = new HashSet<int>();
            int n1 = 0;
            for (int i = 0; i < 10; i++)
            {
                Thread t = new Thread(() =>
                {
                    Thread current = Thread.CurrentThread;
                    for (int c = 0; c < 100; c++)
                    {
                        int quantity = InterlockedIncrement32(ref n1);
                        set.Add(quantity);
                        Console.WriteLine("Thread: {0}, Output: {1}", current.Name, quantity);
                    }
                });
                t.Name = i.ToString();
                t.Start();
            }
            Console.ReadKey(false);
            if (set.Count == (10 * 100))
            {
                Console.WriteLine("Atomic operation results meet established expectations");
            }
            else
            {
                Console.WriteLine("No guarantee to the uniqueness of the atom, the result of the operation is not consistent with the expected");
            }
        }
        Console.ReadKey(false);

    }
}


猜你喜欢

转载自blog.csdn.net/liulilittle/article/details/80897851