CobaltStrike 4.9.1-客户端破解(一)


背景

我们知道 CobaltStrike 在渗透测试中扮演着重要的角色。因此知道他的工作原理对于免杀来说至关重要。之前也有很多文章写CobaltStrike我个人认为大佬们对于新手来说写的不太详细导致有些java编程基础薄弱的可能看不太懂。大佬们觉得这些很简单但是对于新手来说是不可逾越的鸿沟。我通过互联网搜索关于CobaltStrike 4.9.1 破解文章较少。有的是付费的对于这个破解我想说收费也对毕竟要吃饭不知道付费的讲解如何。我站在我自己的角度会一步一步学习如何破解CobaltStrike 4.9.1 从0开始。前提是你必须有有一定的java编程基础否则你可能看不懂。
我们后续会对CobaltStrike进行进一步的剖析。我讲的在部分东西上有误或者有瑕疵希望大佬们轻喷。接下来开始吧。我们将CobaltStrike简称cs。

下载原版cs并且解压

我们从https://www.ddosi.org/cobalt-strike-4-9-1/下载cs原版4.9.1 解压密码为: www.ddosi.org
cs原版本下载
我们把cobaltstrike-4.9.1.-original.jar 解压如下图
如下图
其中cobaltstrike-client.jar 就是我们需要破解的客户端

反编译源代码

由于原版本没有进行混淆所以我们直接使用idea自带反编译器对jar包进行反编译我们创建一个cssource文件夹。把java-decompiler.jar 从idea里面拷贝出来放到和cssource同一目录
反编译jar
我们来进行反编译。

"ieda安装目录\jbr\bin\java.exe" -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true cobaltstrike-client.jar cssource/

在反编译的开始会有停顿属于正常现象等着就行如下图:
反编译遇到停顿
出现下图就算反编译结束了
反编译结束

把反编译的源码和lib放到idea中

打开idea 点击File-New-project创建项目csproject
创建项目
在项目中分别创建lib和DecompileSrc。lib copy到 cobaltstrike-client.jar。DecompileSrc cpoy到cobaltstrike-client.jar反编译后的.java文件
反编译后的文件
在idea项目中放置如下图:
idea布局结构

部署运行环境

1:给项目增加依赖

点击-文件-项目结构-模块-依赖

如图
然后在点击那个加号选择点击加号 jar或者是目录。选择lib包里面的cobaltstrike-client.jar
如图
勾选然后点击应用(apply)然后点击ok
如图

2:从反编译源码中复制主类类名添加依赖模块
我们从下图图片中得到主类为 aggressor.Aggressor
得到主类
选中Aggressor 按照如下图进行操作
复制
注意要复制到src里面
注意路径

3:配置jar包输出目录
来到文件-项目结构-控件-加号-jar-来自模块依赖 如下图:
如图
然后选择主类点击确定就行了。另外一个没截图默认就行了。
如图
我们在主类中加入

JOptionPane.showMessageDialog(null,"test!!!");

测试类
如图
我们看主类的MANIFEST.MF缺少东西我们把反编译源码里面的MANIFEST.MF内容复制过来
如图
复制过程就不截图了

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.15
Created-By: 1.7.0_80-b15 (Oracle Corporation)
Main-Class: aggressor.Aggressor
Add-Exports: java.desktop/sun.swing java.desktop/sun.swing.table java.de
 sktop/sun.swing.plaf.synth java.desktop/com.sun.java.swing.plaf.windows
  java.desktop/sun.awt.shell java.desktop/com.sun.awt java.base/sun.secu
 rity.action
Add-Opens: java.desktop/javax.swing.plaf.synth java.desktop/javax.swing.
 plaf.basic java.desktop/javax.swing java.desktop/javax.swing.tree java.
 desktop/java.awt.event
Synthetica-Version: 2.30.0 Build 16
Multi-Release: true

来到build到build artifart 。build成功会生成jar文件
如图
如图生成了jar文件。
如图
运行测试一下。点击run-编辑配置
配置
点击加号-然后选择jar应用按照这么配置
空格 -XX:+AggressiveHeap -XX:+UseParallelGC 注意jdk要选择idea内置jdk
如图
然后点击我们配置好的项目直接运行就行了
如图
出现下图这种说明环境没有问题程序运行了
如图

分析并且破解cs客户端

我们从代码看Requirements.checkGUI();挨个函数进行分析
如图
我们来看代码我把代码贴上来了

public static void checkGUI() {
       recommended();  //空函数
       String var0 = A(true);  //这个应该是判断环境的
       if (var0 != null) {
           JOptionPane.showMessageDialog((Component)null, var0, (String)null, 0);
           CommonUtils.print_error(var0);
           System.exit(0);
       }

       String var1 = System.getenv("XDG_SESSION_TYPE"); //获取环境配置
       if ("wayland".equals(var1)) {
           CommonUtils.print_warn("You are using a Wayland desktop and not X11. Graphical Java applications run on Wayland are known to crash. You should use X11. See: https://www.cobaltstrike.com/help-wayland");
           JOptionPane.showInputDialog((Component)null, "The Wayland desktop is not supported with Cobalt Strike.\nMore information:", (String)null, 2, (Icon)null, (Object[])null, "https://www.cobaltstrike.com/help-wayland");
       }

       Requirements var2 = new Requirements(); 
       var2.initializeStarter(var2.getClass());
   }
public static void recommended() { //空函数
   }

    private static String A(boolean var0) {
       if (var0) {
           if ("1.6".equals(System.getProperty("java.specification.version"))) {
               return "Java 1.6 is not supported. Please upgrade to Java 1.7 or later.";
           }

           if (var0 && "1.8".equals(System.getProperty("java.specification.version")) && CommonUtils.isin("OpenJDK", System.getProperty("java.runtime.name"))) {
               return "OpenJDK 1.8 is not supported. Use Oracle Java 8 or OpenJDK 11 (or later.)";
           }

           Set var1 = A();
           if (!var1.contains("-XX:+AggressiveHeap")) {
               return "Java -XX:+AggressiveHeap option not set. Use the Cobalt Strike launcher. Don't click the .jar file!";
           }

           if (!var1.contains("-XX:+UseParallelGC")) {
               return "Java -XX:+UseParallelGC option not set. Use the Cobalt Strike launcher. Don't click the .jar file!";
           }
       }

       return null;
   }

private static Set A() {
       RuntimeMXBean var0 = ManagementFactory.getRuntimeMXBean();
       List var1 = var0.getInputArguments();
       HashSet var2 = new HashSet(var1);
       return var2;
   }

上面分析没有发现有校验函数所以我们看这个Requirements类调用了一个initializeStarter函数并且传入class对象

Requirements var2 = new Requirements(); 
 var2.initializeStarter(var2.getClass());

我们重点看这个initializeStarter函数

  protected final void initializeStarter(Class var1) {
        if (!this.A(var1)) { //调用A函数进行校验操作
            System.exit(0);
        }

    }
 private final boolean A(Class var1) {
        boolean var2 = true;
        Class var3 = null;
        String var4 = null;
        long var5 = 0L;
        var3 = AuthCrypto.class;
        var4 = "common/AuthCrypto.class";
        var5 = 895661977L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        --var5;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var4 = "resources/authkey.pub";
        var5 = 1661186542L;
        if (!Initializer.isFileOK(var1, var4, var5, true)) {
            var2 = false;
        }

        --var5;
        if (Initializer.isFileOK(var1, var4, var5, true)) {
            var2 = false;
        }

        var3 = License.class;
        var4 = "common/License.class";
        var5 = 1747926151L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        --var5;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var3 = Authorization.class;
        var4 = "common/Authorization.class";
        var5 = 1655604415L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        --var5;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var3 = LicenseStatus.class;
        var4 = "common/LicenseStatus.class";
        var5 = 3059950611L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        --var5;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var3 = SleevedResource.class;
        var4 = "common/SleevedResource.class";
        var5 = 3881376138L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        --var5;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var3 = AggressorInfo.class;
        var4 = "common/AggressorInfo.class";
        var5 = 1814321826L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        --var5;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var3 = SleeveSecurity.class;
        var4 = "dns/SleeveSecurity.class";
        var5 = 3962922538L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var5 -= 159L;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var3 = BaseArtifactUtils.class;
        var4 = "common/BaseArtifactUtils.class";
        var5 = 1905161680L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        ++var5;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var3 = BaseResourceUtils.class;
        var4 = "common/BaseResourceUtils.class";
        var5 = 510216517L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var5 /= 2L;
        if (Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        var3 = PayloadGeneratorDialog.class;
        var4 = "aggressor/dialogs/PayloadGeneratorDialog.class";
        var5 = 3766937686L;
        if (!Initializer.isOK(var1, var3, var4, var5, true)) {
            var2 = false;
        }

        return var2;
    }
调用isOk函数进行校验
\csproject\lib\cobaltstrike-client.jar!\common\Initializer.class
public static final boolean isOK(Class var0, Class var1, String var2, long var3, boolean var5) {
        long[] var6 = new long[]{var3};
        return isOK(var0, var1, var2, var6, var5);
    }

继续分析
\csproject\lib\cobaltstrike-client.jar!\common\Initializer.class
public static final boolean isOK(Class var0, Class var1, String var2, long[] var3, boolean var4) {
        return A(var0, var1, var2, var3, var4);
    }

\csproject\lib\cobaltstrike-client.jar!\common\Initializer.class
 private static final boolean A(Class var0, Class var1, String var2, long[] var3, boolean var4) {
        ZipFile var5 = null;

        boolean var6;  //这里默认就是false
        try {
            var5 = A(var0, var1); //校验一函数
            if (var5 == null) {
                var6 = !var4;
                return var6;
            }

            var6 = A(var5, var2, var3); //正正校验二函数
        } finally {
            try {
                if (var5 != null) {
                    var5.close();
                }
            } catch (Throwable var14) {
            }

        }

        return var6;
    }
\csproject\lib\cobaltstrike-client.jar!\common\Initializer.class  获取路径应该是
     private static final ZipFile A(Class var0, Class var1) {
        try {
            String var2 = var0.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
            String var3 = var1.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
            return var2.equals(var3) ? new ZipFile(var2) : null;
        } catch (Throwable var4) {
            return null;
        }
    }

\csproject\lib\cobaltstrike-client.jar!\common\Initializer.class  这个函数是真正的校验函数
   private static final boolean A(ZipFile var0, String var1, long[] var2) {
        try {
            ZipEntry var3 = var0.getEntry(var1);
            if (var3 == null) {
                return false;
            } else {
                return A(var3.getCrc(), var2); 
            }
        } catch (Throwable var4) {
            return true;
        }
    }
\csproject\lib\cobaltstrike-client.jar!\common\Initializer.class 使用crc进行校验
     private static final boolean A(long var0, long[] var2) {
        for(int var3 = 0; var3 < var2.length; ++var3) {
            if (var0 == var2[var3]) {
                return true;
            }
        }

        return false;
    }

回顾上面的操作说明了你只要修改 common/AuthCrypto.class common/Authorization.class 等类就会触发校验退出系统下图函数里面的类
如图


protected final void initializeStarter(Class var1) {
      if (!this.A(var1)) { //调用A函数进行校验操作
          System.exit(0);
      }

  }

所以我们要对initializeStarter函数进行修改它在\csproject\lib\cobaltstrike-client.jar!\common\Starter.class 。所以我们要来到反编译源码里面复制common\Starter.java(\csproject\DecompileSrc\common\Starter.java)到src里面和复制主类名一样的操作我就不截图了
我们直接修改代码


private final boolean A(Class var1) {
    System.out.println("bypass initializeStarter class checked 2");
    boolean var2 = true;

  return var2;
}

修改截图如下。直接把它的核心校验函数修改掉
修改核心校验函数
继续走我们 走到如下图的函数

License.checkLicenseGUI(new Authorization());

我们首先看checkLicenseGUI函数。我们可以看到校验核心是 Authorization 类

public static void checkLicenseGUI(Authorization var0) {
       if (!var0.isValid()) {
           CommonUtils.print_error("Your authorization file is not valid: " + var0.getError());
           JOptionPane.showMessageDialog((Component)null, "Your authorization file is not valid.\n" + var0.getError(), (String)null, 0);
           System.exit(0);
       }

       if (!var0.isPerpetual()) {
           if (var0.isExpired()) {
               CommonUtils.print_error("Your Cobalt Strike license is expired. Please contact cobalt.custops@helpsystems.com to renew. If you did renew, run the update program to refresh your authorization file.");
               JOptionPane.showMessageDialog((Component)null, "Your Cobalt Strike license is expired.\nPlease contact cobalt.custops@helpsystems.com to renew\n\nIf you did renew, run the update program to refresh your\nauthorization file.", (String)null, 0);
               System.exit(0);
           }

           if (var0.isAlmostExpired()) {
               CommonUtils.print_warn("Your Cobalt Strike license expires " + var0.whenExpires() + ". Email cobalt.custops@helpsystems.com to renew. If you did renew, run the update program to refresh your authorization file.");
               JOptionPane.showMessageDialog((Component)null, "Your Cobalt Strike license expires " + var0.whenExpires() + "\nEmail cobalt.custops@helpsystems.com to renew\n\nIf you did renew, run the update program to refresh your\nauthorization file.", (String)null, 1);
           }

       }
   }

我们去Authorization类看看。此类为核心校验类校验是否是正版还是试用版本。通过Authorization构造函数进行校验。


\csproject\lib\cobaltstrike-client.jar!\common\Authorization.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package common;

import java.io.File;
import java.util.Arrays;
import java.util.Calendar;

public class Authorization {
    protected String validto = "";
    protected String error = null;
    protected boolean valid = false;

    public Authorization() {
        String var1 = CommonUtils.canonicalize("cobaltstrike.auth");
        if (!(new File(var1)).exists()) {
            try {
                File var2 = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
                if (var2.getName().toLowerCase().endsWith(".jar")) {
                    var2 = var2.getParentFile();
                }

                var1 = (new File(var2, "cobaltstrike.auth")).getAbsolutePath();
            } catch (Exception var11) {
                MudgeSanity.logException("trouble locating auth file", var11, false);
            }
        }

        byte[] var12 = CommonUtils.readFile(var1);
        if (var12.length == 0) {
            this.error = "Could not read " + var1;
        } else if (var12.length == 512) {
            AuthCrypto var3 = new AuthCrypto();
            byte[] var4 = var3.decrypt(Arrays.copyOfRange(var12, 0, 256));
            if (var4.length != 0) {
                try {
                    DataParser var5 = new DataParser(var4);
                    var5.big();
                    byte[] var6 = var5.readBytes(var5.readByte());
                    var5.consume(var5.readByte());
                    byte var7 = var5.readByte();
                    int var8 = var5.readInt();
                    int var9 = var5.readInt();
                    if (var7 != 49) {
                        return;
                    }

                    if (29999999 == var8) {
                        this.validto = "forever";
                        MudgeSanity.systemDetail("valid to", "perpetual");
                    } else {
                        if (!this.A(var8)) {
                            this.error = "Valid to date (" + var8 + ") is invalid";
                            return;
                        }

                        this.validto = "20" + var8;
                        MudgeSanity.systemDetail("valid to", CommonUtils.formatDateAny("MMMMM d, YYYY", this.getExpirationDate()));
                    }

                    this.valid = true;
                    MudgeSanity.systemDetail("id", var9 + "");
                    SleevedResource.Setup(var6);
                } catch (Exception var10) {
                    MudgeSanity.logException("auth file parsing", var10, false);
                }

            }
        }
    }

    private final boolean A(int var1) {
        if (var1 > 999999) {
            return false;
        } else {
            int var2 = 2000 + var1 / 10000;
            int var3 = var1 % 10000 / 100;
            int var4 = var1 % 100;
            byte var5 = 10;
            int var6 = Calendar.getInstance().get(1) + var5;
            if (var2 > var6) {
                return false;
            } else {
                Calendar var7 = Calendar.getInstance();
                var7.clear();
                var7.setLenient(false);

                try {
                    var7.set(var2, var3 - 1, var4);
                    var7.getTime();
                    return true;
                } catch (Throwable var9) {
                    return false;
                }
            }
        }
    }

    public boolean isPerpetual() {
        return "forever".equals(this.validto);
    }

    public boolean isValid() {
        return this.valid;
    }

    public String getError() {
        return this.error != null ? this.error : "Authorization file is not for Cobalt Strike 4.9";
    }

    public long getExpirationDate() {
        return CommonUtils.parseDate(this.validto, "yyyyMMdd");
    }

    public boolean isExpired() {
        return System.currentTimeMillis() > this.getExpirationDate() + B(1);
    }

    public String whenExpires() {
        long var1 = (this.getExpirationDate() + B(1) - System.currentTimeMillis()) / B(1);
        if (var1 == 1L) {
            return "in 1 day (" + CommonUtils.formatDateAny("MMMMM d, YYYY", this.getExpirationDate()) + ")";
        } else {
            return var1 <= 0L ? "TODAY (" + CommonUtils.formatDateAny("MMMMM d, YYYY", this.getExpirationDate()) + ")" : "in " + var1 + " days (" + CommonUtils.formatDateAny("MMMMM d, YYYY", this.getExpirationDate()) + ")";
        }
    }

    public boolean isAlmostExpired() {
        long var1 = System.currentTimeMillis() + B(45);
        return var1 > this.getExpirationDate();
    }

    private static final long B(int var0) {
        return 86400000L * (long)var0;
    }
}

我们把要修改的类拷贝到src目录里面csproject\src\common\Authorization.java
我们看他的属性一个一个分析填入值 protected String validto = “”;我们看这个为forever我们修改一下属性为forever
如图
前天两个属性同样的方式改成
protected String error = null; //这个不用动
protected boolean valid = true;

所以我们改成了

protected String validto = "forever";
   protected String error = null;
   protected boolean valid = true;

继续看代码 SleevedResource.Setup(var6);
如图
这个代码是干啥的我们来分析一下

\csproject\lib\cobaltstrike-client.jar!\common\SleevedResource.class
 protected static void Setup(byte[] var0) {
        B = new SleevedResource(var0);
    }

SleeveSecurity A = new SleeveSecurity(); //初始化 SleeveSecurity 并且调用 registerKey
        private SleevedResource(byte[] var1) {
        this.A.registerKey(var1);
    }

csproject\lib\cobaltstrike-client.jar!\dns\SleeveSecurity.class

  public SleeveSecurity() {
        try {
            byte[] var1 = "abcdefghijklmnop".getBytes();
            this.B = new IvParameterSpec(var1);
            this.A = Cipher.getInstance("AES/CBC/NoPadding");
            this.C = Cipher.getInstance("AES/CBC/NoPadding");
            this.F = Mac.getInstance("HmacSHA256");
        } catch (Exception var2) {
            throw new RuntimeException(var2);
        }
    }

我们看 SleevedResource.Setup(var6); var6是哪里来的我就不截图了。看这个架势是从.auth文件里面读取的。这搞到这里就没办法了似乎这个var6值搞不定了。并且从代码里面看貌似是AES\CBC的秘钥。我们翻看github不难发现有个 4.9从正版提取出来的var6我们看看 https://github.com/kyxiaxiang/CrackSleeve4.9

byte[] var12 = CommonUtils.readFile(var1);
    if (var12.length == 0) {
       this.error = "Could not read " + var1;
    } else if (var12.length == 512) {
       AuthCrypto var3 = new AuthCrypto();
       byte[] var4 = var3.decrypt(Arrays.copyOfRange(var12, 0, 256));
       if (var4.length != 0) {
          try {
             DataParser var5 = new DataParser(var4);
             var5.big();
             byte[] var6 = var5.readBytes(var5.readByte());

我们发现秘钥为
private static byte[] OriginKey = {-1, 12, -6, 65, 7, -47, 91, 48, 17, 61, 29, 43, -99, -23, 21, 109}; 所以我们定义var6为
所以我们新的文件代码为

public class Authorization {
   protected String validto = "forever"; //修改部分
   protected String error = null;  //修改部分
   protected boolean valid = true;  //修改部分

   public Authorization() {  //修改部分
          byte[] var6 = {-1, 12, -6, 65, 7, -47, 91, 48, 17, 61, 29, 43, -99, -23, 21, 109};
      System.out.println("bypass Authorization");
      SleevedResource.Setup(var6);
   }
    private final boolean A(int var1) {
      if (var1 > 999999) {
         return false;
      } else {
         int var2 = 2000 + var1 / 10000;
         int var3 = var1 % 10000 / 100;
         int var4 = var1 % 100;
         byte var5 = 10;
         int var6 = Calendar.getInstance().get(1) + var5;
         if (var2 > var6) {
            return false;
         } else {
            Calendar var7 = Calendar.getInstance();
            var7.clear();
            var7.setLenient(false);

            try {
               var7.set(var2, var3 - 1, var4);
               var7.getTime();
               return true;
            } catch (Throwable var9) {
               return false;
            }
         }
      }
   }

   public boolean isPerpetual() {
      return "forever".equals(this.validto);
   }

   public boolean isValid() {
      return this.valid;
   }

   public String getError() {
      return this.error != null ? this.error : "Authorization file is not for Cobalt Strike 4.9";
   }

   public long getExpirationDate() {
      return CommonUtils.parseDate(this.validto, "yyyyMMdd");
   }

   public boolean isExpired() {
      return System.currentTimeMillis() > this.getExpirationDate() + B(1);
   }

   public String whenExpires() {
      long var1 = (this.getExpirationDate() + B(1) - System.currentTimeMillis()) / B(1);
      if (var1 == 1L) {
         return "in 1 day (" + CommonUtils.formatDateAny("MMMMM d, YYYY", this.getExpirationDate()) + ")";
      } else {
         return var1 <= 0L ? "TODAY (" + CommonUtils.formatDateAny("MMMMM d, YYYY", this.getExpirationDate()) + ")" : "in " + var1 + " days (" + CommonUtils.formatDateAny("MMMMM d, YYYY", this.getExpirationDate()) + ")";
      }
   }

   public boolean isAlmostExpired() {
      long var1 = System.currentTimeMillis() + B(45);
      return var1 > this.getExpirationDate();
   }

   private static final long B(int var0) {
      return 86400000L * (long)var0;
   }

如下图 注意每次改完要重新进行build 然后在运行
如图
我们运行一下看看。发现还是不行我们推测是在这里 A = new MultiFrame(); 有暗桩
如图
我们来分析一下这个代码

csproject\lib\cobaltstrike-client.jar!\aggressor\MultiFrame.class
protected MultiFrame() {
        super("");
        CommonUtils.checkAuthFile();
        this.getContentPane().setLayout(new BorderLayout());
        this.toolbar = new JToolBar();
        this.content = new JPanel();
        this.cards = new CardLayout();
        this.content.setLayout(this.cards);
        this.getContentPane().add(this.content, "Center");
        this.buttons = new LinkedList();
        this.setDefaultCloseOperation(3);
        this.setSize(800, 600);
        this.setExtendedState(6);
        this.setIconImage(DialogUtils.getImage("resources/armitage-icon.gif"));
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
        LookAndFeelManager.adjustments(MultiFrame.class);
    }

public static final boolean checkAuthFile() {
        Authorization var0 = new Authorization(); //由于这个验证已经被干掉了所以应不存在验证了看情况但是还是退出了
        if (!var0.isValid()) {
            print_error("License is invalid");
            System.exit(1);
        }

        return true;
    }

我们这时候看看这个类调用了。哪些不是系统类

package aggressor;

import aggressor.ui.UseLookAndFeel.LookAndFeelManager;
import common.CommonUtils;
import dialog.DialogUtils;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;

发现是这个三个

import aggressor.ui.UseLookAndFeel.LookAndFeelManager;
import common.CommonUtils;
import dialog.DialogUtils;

我一个一个看首先我们看这个并且在里面调用为 LookAndFeelManager.adjustments(MultiFrame.class);

csproject\lib\cobaltstrike-client.jar!\aggressor\ui\UseLookAndFeel.class
 public static final boolean adjustments(Class var0) {
            if (!A(var0)) {
                System.exit(1);
            }

            return true;
        }

private static final boolean A(Class var0) {
            boolean var1 = true;
            Method var2 = null;
            String var3 = "Y29tbW9uLkNvbW1vblV0aWxz"; //common.CommonUtils
            String var4 = "amF2YS5sYW5nLkNsYXNz"; //java.lang.Class
            String var5 = "dmFsaWRDbGFzc0ludGVncml0eQ=="; //validClassIntegrity
            Class var6 = null;

            try {
                var6 = Class.forName(A(var3, ""));
                Class var7 = Class.forName(A(var4, ""));
                var2 = var6.getDeclaredMethod(A(var5, ""), var7, var7, Boolean.TYPE, Long.TYPE);//common.CommonUtils.validClassIntegrity
            } catch (Throwable var10) {
                System.exit(1);
            }

            var1 = var1 && A(var2, var0, false, "Y29tbW9uLkNvbW1vblV0aWxz", 395936014L); //common.CommonUtils
            var1 = var1 && A(var2, var0, false, "Y29tbW9uLkF1dGhvcml6YXRpb24=", 1655604415L); //common.Authorization
            long var11 = 895661977L;
            String var9 = "Y29tbW9uLkF1dGhDcnlwdG8="; //common.AuthCrypto
            var1 = var1 && A(var2, var0, false, var9, var11);
            var1 = var1 && !A(var2, var0, false, var9, var11 + 1L);
            var1 = var1 && A(var2, var0, false, "Y29tbW9uLkRhdGFQYXJzZXI=", 2488344227L); //common.DataParser
            var1 = var1 && A(var2, var0, false, "Y29tbW9uLlNsZWV2ZWRSZXNvdXJjZQ==", 3881376138L); //common.SleevedResource
            var1 = var1 && A(var2, var0, false, "ZG5zLlNsZWV2ZVNlY3VyaXR5", 3962922538L); //dns.SleeveSecurity
            var1 = var1 && A(var2, var0, false, "YWdncmVzc29yLkFnZ3Jlc3Nvcg==", 105727981L); //aggressor.Aggressor
            var1 = var1 && A(var2, var0, false, "Y29tbW9uLkFnZ3Jlc3NvckluZm8=", 1814321826L); //common.AggressorInfo
            var1 = var1 && A(var2, var0, false, "Y29tbW9uLlN0YXJ0ZXI=", 541000743L); //common.Starter
            return var1;
        }

          private static final String A(String var0, String var1) { //base64编码
         return new String(Base64.decode(var0));
      }
``` java

我们推测这个应该就是检测点我们还是老样子去反编译源码中复制一波
变成了csproject\src\aggressor\ui\UseLookAndFeel.java 修改一波代码。重新编译看看
``` java 
      public static final boolean adjustments(Class var0) {
         if (!A(var0)) {
            System.out.println("check adjustments!!!");
            System.exit(1);
         }

         return true;
      }

说明是这个点
如图
var2 = var6.getDeclaredMethod(A(var5, “”), var7, var7, Boolean.TYPE, Long.TYPE);//common.CommonUtils.validClassIntegrity
使用反射利用 validClassIntegrity 校验类是否被改


public static final boolean validClassIntegrity(Class var0, Class var1, boolean var2, long var3) {
    ZipFile var5 = null;

    boolean var6;
    try {
       var5 = openZip(var0, var1);
       if (var5 != null) {
          long var18 = getClassCRC(var5, var1);
          boolean var8 = var3 == var18;
          return var8;
       }

       var6 = var2;
    } finally {
       if (var5 != null) {
          try {
             var5.close();
          } catch (Throwable var16) {
          }
       }

    }

    return var6;
 }

    public static final long getClassCRC(ZipFile var0, Class var1) {
    try {
       String var2 = convertClassToFileName(var1.getName());
       ZipEntry var3 = var0.getEntry(var2);
       return var3 != null ? var3.getCrc() : -1L;
    } catch (Throwable var4) {
       return -1L;
    }
 }

我们之计copy common.CommonUtils 在validClassIntegrity函数加上 System.out.println(“crc checking class “+var0.getName()+” | “+var1.getName());
如图
验证了我猜想使用 common.CommonUtils 的在validClassIntegrity函数 验证 aggressor.MultiFrame 的完整性所以直接注释掉


private static final boolean A(Class var0) {
         boolean var1 = true;
         /*
         Method var2 = null;
         String var3 = "Y29tbW9uLkNvbW1vblV0aWxz";
         String var4 = "amF2YS5sYW5nLkNsYXNz";
         String var5 = "dmFsaWRDbGFzc0ludGVncml0eQ==";
         Class var6 = null;

         try {
            var6 = Class.forName(A(var3, ""));
            Class var7 = Class.forName(A(var4, ""));
            var2 = var6.getDeclaredMethod(A(var5, ""), var7, var7, Boolean.TYPE, Long.TYPE);
         } catch (Throwable var10) {
            System.exit(1);
         }

         var1 = var1 && A(var2, var0, false, "Y29tbW9uLkNvbW1vblV0aWxz", 395936014L);
         var1 = var1 && A(var2, var0, false, "Y29tbW9uLkF1dGhvcml6YXRpb24=", 1655604415L);
         long var11 = 895661977L;
         String var9 = "Y29tbW9uLkF1dGhDcnlwdG8=";
         var1 = var1 && A(var2, var0, false, var9, var11);
         var1 = var1 && !A(var2, var0, false, var9, var11 + 1L);
         var1 = var1 && A(var2, var0, false, "Y29tbW9uLkRhdGFQYXJzZXI=", 2488344227L);
         var1 = var1 && A(var2, var0, false, "Y29tbW9uLlNsZWV2ZWRSZXNvdXJjZQ==", 3881376138L);
         var1 = var1 && A(var2, var0, false, "ZG5zLlNsZWV2ZVNlY3VyaXR5", 3962922538L);
         var1 = var1 && A(var2, var0, false, "YWdncmVzc29yLkFnZ3Jlc3Nvcg==", 105727981L);
         var1 = var1 && A(var2, var0, false, "Y29tbW9uLkFnZ3Jlc3NvckluZm8=", 1814321826L);
         var1 = var1 && A(var2, var0, false, "Y29tbW9uLlN0YXJ0ZXI=", 541000743L);
         */
          
         return var1;
      }

编译运行我们发现又退出了如下图。所以那就是 (new ConnectDialog(A)).show(); 代码有验证
如图

\csproject\lib\cobaltstrike-client.jar!\aggressor\dialogs\ConnectDialog.class
  public ConnectDialog(MultiFrame var1) {
        this.window = var1;
        this.useAliasName = Prefs.getPreferences().isSet("connection.view.alias.boolean", false);
        super.initialize(this.getClass());
    }

我们看到了super就是他的父类存在这个函数 public class ConnectDialog extends Starter2 
那就是 Starter2 
\csproject\lib\cobaltstrike-client.jar!\common\Starter2.class

 protected final void initialize(Class var1) {
        if (!this.A(var1)) {
            System.exit(0);
        }

    }

 private final boolean A(Class var1) {
      boolean var2 = true;
      Class var3 = null;
      var3 = Starter.class;
      String var4 = "common/Starter.class";
      long[] var5 = this.A((Object)var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }

      var3 = Initializer.class;
      var4 = "common/Initializer.class";
      var5 = this.A((Object)var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }

      var3 = Aggressor.class;
      var4 = "aggressor/Aggressor.class";
      var5 = this.A((Object)var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }

      var3 = AggressorInfo.class;
      var4 = "common/AggressorInfo.class";
      var5 = this.A((Object)var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }

老规矩复制一波 注释掉验证函数

private final boolean A(Class var1) { //修改一波
      boolean var2 = true;
      /*
      Class var3 = null;
      var3 = Starter.class;
      String var4 = "common/Starter.class";
      long[] var5 = this.A((Object)var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }

      var3 = Initializer.class;
      var4 = "common/Initializer.class";
      var5 = this.A((Object)var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }

      var3 = Aggressor.class;
      var4 = "aggressor/Aggressor.class";
      var5 = this.A((Object)var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }

      var3 = AggressorInfo.class;
      var4 = "common/AggressorInfo.class";
      var5 = this.A((Object)var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }
*/
      return var2;
   }

按照他这个套路在反编译源码中我们搜索一下 common/
\csproject\src\common\Helper.java

看图

public final boolean startHelper(Class var1) {
      boolean var2 = true;
      /*
      Class var3 = null;
      var3 = Starter2.class;
      String var4 = "common/Starter2.class";
      long[] var5 = this.getHelpID(var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }

      var3 = ConnectDialog.class;
      var4 = "aggressor/dialogs/ConnectDialog.class";
      var5 = this.getHelpID(var3);
      if (!B(var1, var3, var4, var5, true)) {
         var2 = false;
      }
      */
      
      return var2;
   }

我们再次搜索 Y29tbW9uLkNvbW1vblV0aWxz
搜索
csproject\src\console\StatusBar.java

private static final boolean A(Class var0) {
        boolean var1 = true;
        /*
        Method var2 = null;
        String var3 = "Y29tbW9uLkNvbW1vblV0aWxz";
        String var4 = "amF2YS5sYW5nLkNsYXNz";
        String var5 = "dmFsaWRDbGFzc0ludGVncml0eQ==";
        Class var6 = null;

        try {
           var6 = Class.forName(A(var3, ""));
           Class var7 = Class.forName(A(var4, ""));
           var2 = var6.getDeclaredMethod(A(var5, ""), var7, var7, Boolean.TYPE, Long.TYPE);
        } catch (Throwable var8) {
           System.exit(1);
        }

        var1 = var1 && A(var2, var0, false, "ZGlhbG9nLlNhZmVEaWFsb2dzJFNhZmV0eUNoZWNr", 2543265740L);
        var1 = var1 && A(var2, var0, false, "YWdncmVzc29yLmJyb3dzZXJzLkNyZWRlbnRpYWxz", 3889907850L);
        var1 = var1 && A(var2, var0, false, "YWdncmVzc29yLmJyb3dzZXJzLlRhcmdldHM=", 3853403163L);
        */
        return var1;
     }

csproject\src\aggressor\Prefs.java

private static final boolean A(Class var0) {

       boolean var1 = true;
       /*
       Method var2 = null;
       String var3 = "Y29tbW9uLkNvbW1vblV0aWxz";
       String var4 = "amF2YS5sYW5nLkNsYXNz";
       String var5 = "dmFsaWRDbGFzc0ludGVncml0eQ==";
       Class var6 = null;

       try {
          var6 = Class.forName(A(var3, ""));
          Class var7 = Class.forName(A(var4, ""));
          var2 = var6.getDeclaredMethod(A(var5, ""), var7, var7, Boolean.TYPE, Long.TYPE);
       } catch (Throwable var8) {
          System.exit(1);
       }

       var1 = var1 && A(var2, var0, false, "Y29uc29sZS5TdGF0dXNCYXIkU3RhdHVzQmFySGVscGVy", 482118951L);
       var1 = var1 && A(var2, var0, false, "Z3JhcGguTmV0d29ya0dyYXBo", 3864160077L);

        */
       return var1;
    }

csproject\src\dialog\SafeDialogs.java

private static final boolean A(Class var0) {
        boolean var1 = true;
        /*
        Method var2 = null;
        String var3 = "Y29tbW9uLkNvbW1vblV0aWxz";
        String var4 = "amF2YS5sYW5nLkNsYXNz";
        String var5 = "dmFsaWRDbGFzc0ludGVncml0eQ==";
        Class var6 = null;

        try {
           var6 = Class.forName(A(var3, ""));
           Class var7 = Class.forName(A(var4, ""));
           var2 = var6.getDeclaredMethod(A(var5, ""), var7, var7, Boolean.TYPE, Long.TYPE);
        } catch (Throwable var10) {
           System.exit(1);
        }

        var1 = var1 && A(var2, var0, false, "YWdncmVzc29yLnVpLlVzZUxvb2tBbmRGZWVsJExvb2tBbmRGZWVsTWFuYWdlcg==", 684737157L);
        long var11 = 942042510L;
        String var9 = "YWdncmVzc29yLk11bHRpRnJhbWU=";
        var1 = var1 && !A(var2, var0, false, var9, var11 + 1L);
        var1 = var1 && A(var2, var0, false, var9, var11);
        var1 = var1 && A(var2, var0, false, "ZGlhbG9nLkFjdGl2aXR5UGFuZWw=", 3600095813L);

         */
        return var1;
     }

我们全部修改完毕了我们在重新运行一波看看。破解成功

破解成功

我们运行 Pwn3rzs 的服务端测试一波。那我们尝试修改一波
如图
我们搜索Licensed 发现在\AggressorInfo.java 直接copy到这个
csproject\src\common\AggressorInfo.java 修改一下编译尝试一下

package common;

public class AggressorInfo {
   public static final String VERSION = "4.9.1 " + (License.isTrial() ? "Trial" : "(Pwn3rs)");//"Licensed");
   public static final String VERSION_SHORT = "4.9.1";
}

破解完毕。
破解完毕
我们修改一下版权看看
csproject\src\resources\about.html

<html>
	<body>	
		<center><h1>Cobalt Strike 4.9.1 peiqiF4ck</h1></center>

		<p>Advanced Threat Tactics Software</p>
		<p>Release: Oct 10, 2023</p>
		<p></p>
		<p>&copy; Fortra, LLC and its group of companies.</p>
		<p>All trademarks and registered trademarks are the property of their respective owners.</p>
	</body>
</html>

修改编译后搞个listener上线测试。
如图
如图测试屏幕截图和VNC功能正常要关闭杀软进行测试
测试

结语

至此我们成功破解cs 4.9.1客户端并且配合Pwn3rzs 服务端进行使用。其他功能未测试如果有暗装分析方法如上图。后面将讲解分析cs exe客户端生成方式和shellcode 关系。只有系统了解以后我们才能更好的进行免杀操作。我们总结三点
一:对于web渗透来说我们只需要关注信息收集 xday我们可以使用我们自己开发的exp poc工具进行集成最新的exp。那就是webframeworkTools视频教程
软件使用视频教程
webframeworktools5.0 使用教程
https://www.youtube.com/watch?v=TXG7W9WnbiI

webframeworktools5.1 DLL生成器使用
https://www.youtube.com/watch?v=IkhE4cNVNso

webframeworktools5.1 自写dll教程。使用内部封装函数编写上传功能代码。上传内容使用内部封装函数可以转换任意文件内容。编写进行上传操作。内部代码不公开仅做演示操作。其他简单的编写可以去博客看get post的教程。简单的可以直接一键生成exp
https://www.youtube.com/watch?v=aLznRaoG_b4

webframeworktools5.2 ini配置教程
https://www.youtube.com/watch?v=DlupWCAbBgg

视频教程已出。不会的自行学习。旧版本自写dll存在bug。如果没有输出结果属于正常。后续版本将纠正这个bug

二:我们了解了cs工作原理对于内网渗透使用opsec操作等如虎添翼。内网渗透比较难得就是域渗透。后续研究研究也是那个不过也是固定打法。多多练习就会了。

三:主要是代码的问题我们分析了这么多主要是你得有java se开发基础才能听懂。另外不要光看不练。这是学不会的。

参考链接:https://mp.weixin.qq.com/s/BXKPpEo7h3VqYCdOohPw2g 个人觉得我们讲解比较详细


文章作者: peiqiF4ck
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 peiqiF4ck !
  目录