安全矩阵

 找回密码
 立即注册
搜索
查看: 925|回复: 0

CS 4.4 二开笔记:增强篇

[复制链接]

179

主题

179

帖子

630

积分

高级会员

Rank: 4

积分
630
发表于 2023-10-22 15:37:24 | 显示全部楼层 |阅读模式
CS 4.4 二开笔记:增强篇
本文为CS 4.4 二开笔记系列第二篇,主要是针对aggressor客户端进行功能分析与二开。感觉现在的查到的文章较多在于beacon的协议的分析和针对特征的修改,在aggressor的分析还是比较少的,而aggressor又是使用者进行一系列操作的入口,只掌握sleep来编写cna脚本是远远不够的,因此本文将着重讨论针对aggressor功能的分析与扩充,文中可能会对部分步骤进行省略,主要是提供足够的思路来分析aggressor的源码和增强。

Event Log 显示连接的 teamserver 的 IP

之前在实战中遇到的问题是经常忘了连接的是哪台服务器的teamserver,后来分析了一下cscat 4.5的源码,他的设置方式为通过aggressor进行设置,因为我在开发时没有打算在aggressor处设置配置文件,因此使用了不同的方式实现显示,先看一下效果:



此处涉及到cna脚本的编写及aggressor的源码,cna脚本的编写可以参考下面的官方链接及狼组的中文文档,本文中不再赘述:

cs插件开发 - 先知社区(https://xz.aliyun.com/t/11404
Aggressor-Script | 狼组安全团队公开知识库(https://wiki.wgpsec.org/knowledge/intranet/Aggressor-script.html
https://hstechdocs.helpsystems.c ... s_cobalt-strike.htm

cna脚本会在点击时检索脚本中是否存在相应的函数,如果不存在则进行函数提交,交给java端进行判断及相应,在这里我修改了默认的default.cna脚本,新增了serverip()函数来进行显示:



这个函数的实现并不在cna脚本中,因此在触发时脚本会向aggressor发送serverip这个字符串,然后aggressor会判断是否等于这个字符串来进行响应。aggressor的代码与下面两部分有关:
1.注册
2.响应

注册在DataBridge.java的scriptLoaded()函数中,需要先注册这个函数的键值:



响应在在DataBridge.java的evaluate()函数中,当接收到这个键值传来信号时则做出响应:



aggressorRemoteIp这个字符串是我在connect.java中设置的public static变量,当我们在connect界面连接teamserver时则将对应的host传递给event log,最后达到了我们想要的效果:



钉钉上线提醒

钉钉提醒网上常用的方案为调用agscript来执行python操作,再进行提醒。我感觉这样过于麻烦,因此直接将此功能集成至server端中,只需要在server端的配置文件中填入sever端的配置文件,就可以默认启动,类似:


server端日志显示:



创建方法如下:

新建一个类:



接着在server/Beacons.java中调用这个类,我这里是通过配置文件进行的设置,其他方式大家可以自己研究:



上线效果就是这样:



第一次上线时间显示

这个功能需要涉及到数据库的保存,这里使用的是jdbc,效果如下:



在新增这个功能时,发现了cscat 4.5的一个bug,就是无法正确的显示last,后来发现主要原因在于server/Beacons.java的checkin函数循环导致无法正确刷新map。目前更改后的checkin代码如下:

   
  1. public void checkin(ScListener request, BeaconEntry var2) {
  2.         synchronized (this) {
  3.             if (!var2.isEmpty()) {
  4.                 BeaconEntry var4 = (BeaconEntry) this.privateMap.get(var2.getId());
  5.                 if (var4 == null || var4.isEmpty()) {
  6.                     ServerUtils.addTarget(this.resources, var2.getInternal(), var2.getComputer(), (String) null, var2.getOperatingSystem(), var2.getVersion());
  7.                     ServerUtils.addSession(this.resources, var2.toMap());
  8.                     if (!var2.isLinked() && request != null) {
  9.                         ServerUtils.addC2Info(this.resources, request.getC2Info(var2.getId()));
  10.                     }

  11.                     this.resources.broadcast("eventlog", LoggedEvent.BeaconInitial(var2));
  12.                     this.initial.add(var2.getId());
  13.                     this.resources.process(var2);
  14.                 }
  15.             }

  16.             // 2023-09-19 TOP
  17.             this.Cmp = var2.getComputer();
  18.             if (var2.isSSH() && this.Cmp.contains(SVGSyntax.OPEN_PARENTHESIS)) {
  19.                 this.Cmp = var2.getComputer().replace(SVGSyntax.OPEN_PARENTHESIS, "");
  20.                 this.Cmp = this.Cmp.replace(")", "");
  21.                 this.Cmp = this.Cmp.replace(var2.getPid(), "");
  22.             }
  23.             String BeaconHash = this.hash(var2.getInternal(), var2.getUser(), var2.getProcess(), this.Cmp, var2.getListenerName(), var2.arch(), var2.getPid());
  24.             info BeaconInfo = new info();
  25.             BeaconInfo.BeaconId = var2.getBeaconId();
  26.             BeaconInfo.Internal = var2.getInternal();
  27.             BeaconInfo.External = var2.getExternal();
  28.             BeaconInfo.Process = var2.getProcess();
  29.             BeaconInfo.Arch = var2.arch();
  30.             BeaconInfo.Computer = this.Cmp;
  31.             BeaconInfo.User = var2.getUser();
  32.             BeaconInfo.Hash = BeaconHash;
  33.             try {
  34.                 Connection conn = SqliteSave.OpenDb();
  35.                 try {
  36.                     HashMap<String, String> beacons = SqliteSave.CheckBeaconHash(conn, BeaconHash);
  37.                     if (beacons == null) { // 如果这个机器第一次上线: beacons为null,则打开数据库连接,将BeaconInfo添加到数据库中,然后尝试从beacons中获取"StartTime"并赋值给 BeaconInfo.StartTime
  38.                         Connection conn4 = SqliteSave.OpenDb();
  39.                         SqliteSave.AddBeacon(conn4, BeaconInfo);
  40.                         var2.start = utils.BeijingTime.formatToBeijingTime();
  41.                         ;
  42.                         // 2023-09-18 dingTalk TOP
  43.                         try {
  44.                             String token = TeamServer.globalDingtalkToken;

  45.                             String[] args = new String[2];
  46.                             args[0] = token;
  47.                             args[1] = "CobaltStrike主机上线提醒+1" + "\\n";
  48.                             args[1] += "计算机名:" + var2.getComputer() + "\\n";
  49.                             args[1] += "IP地址:" + var2.getExternal() + "\\n";
  50.                             args[1] += "归属地:" + var2.getIpAddress() + "\\n";
  51.                             args[1] += "用户名:" + var2.getUser() + "\\n";
  52.                             args[1] += "进程名:" + var2.getProcess() + "\\n";
  53.                             args[1] += "PID:" + var2.getPid() + "\\n";

  54.                             utils.DingtalkSendMsg.send(args);
  55.                         } catch (IOException e) {
  56.                             throw new RuntimeException(e);
  57.                         }
  58.                         // 2023-09-18 END
  59.                     } else {
  60.                         Connection conn2 = SqliteSave.OpenDb();
  61.                         HashMap<String, String> beacons2 = SqliteSave.CheckBeacon(conn2, BeaconHash, var2.getBeaconId());
  62.                         if (beacons2 == null) {
  63.                             Connection con3 = SqliteSave.OpenDb();
  64.                             BeaconInfo.StartTime = beacons.get("StartTime");
  65.                             SqliteSave.AddBeacon2(con3, BeaconInfo);
  66.                         } else {
  67.                             var2.start = beacons2.get("StartTime");
  68.                         }
  69.                     }
  70.                     if (conn != null) {
  71.                         conn.close();
  72.                     }
  73.                     this.privateMap.put(var2.getId(), var2);  // <-- core code 此处实时更新
  74.                     // CommonUtils.print_info("[1] var2.getId(): " + var2.getId() + "\nvar2: " + var2.getLastCheckin());
  75.                 } catch (Throwable th) {
  76.                     if (conn != null) {
  77.                         try {
  78.                             conn.close();
  79.                         } catch (Throwable th2) {
  80.                             th.addSuppressed(th2);
  81.                         }
  82.                     }
  83.                     throw th;
  84.                 }
  85.             } catch (SQLException e) {
  86.                 throw new RuntimeException(e);
  87.             }
  88.             // 2023-09-19 END
  89.         }
  90.     }
复制代码




数据库的实现:

  1. //    InitDb(): 初始化数据库。创建一个新的SQLite数据库,并在其中创建一个名为Beacon的表。
  2. //    OpenDb(): 打开数据库连接。连接到SQLite数据库并返回连接。
  3. //    CheckBeaconHash(): 检查特定哈希值的beacon(信标)是否存在。
  4. //    CheckBeacon(): 检查特定哈希值和beacon ID的beacon是否存在。
  5. //    CheckSShBeacon(): 检查SSH beacon是否存在。
  6. //    AddBeacon(): 将一个新的beacon添加到数据库。
  7. //    AddBeacon2(): 将一个新的beacon添加到数据库,但这个方法允许更多的参数。
  8. //    UpBeaconNote(): 更新特定beacon的注释。
  9. //    UpBeaconLastTime(): 更新特定beacon的最后活动时间。
  10. //    UpBeaconId(): 更新特定哈希值的beacon的ID。
  11. public class SqliteSave {
  12.     public static void InitDb() {
  13.         try {
  14.             Class.forName("org.sqlite.JDBC");

  15.             Connection c = DriverManager.getConnection("jdbc:sqlite:sqlite/beacon.db");
  16.             CommonUtils.print_good("[DB] The sqlite/beacon.db is created successfully!");
  17.             Statement stmt = c.createStatement();
  18.             stmt.executeUpdate("CREATE TABLE Beacon (Id INTEGER PRIMARY KEY AUTOINCREMENT, BeaconId        CHAR(50),  Hash        CHAR(50),  StartTime        CHAR(50),  External       CHAR(50),  Internal       CHAR(50),  Computer       CHAR(50),  Process        CHAR(50),  User        CHAR(50),  Arch        CHAR(50),  Note         CHAR(50), UpdateNoteTime         CHAR(50))");
  19.             if (stmt != null) {
  20.                 stmt.close();
  21.             }
  22.             if (c != null) {
  23.                 c.close();
  24.             }
  25.         } catch (Exception e) {
  26.             System.err.println(e.getClass().getName() + ": " + e.getMessage());
  27.             System.exit(0);
  28.         }
  29.         CommonUtils.print_info("[DB] The database is initialized successfully!");
  30.     }

  31.     public static Connection OpenDb() {
  32.         Connection c = null;
  33.         try {
  34.             Class.forName("org.sqlite.JDBC");
  35.             c = DriverManager.getConnection("jdbc:sqlite:sqlite/beacon.db");
  36.         } catch (Exception e) {
  37.             System.err.println(e.getClass().getName() + ": " + e.getMessage());
  38.             System.exit(0);
  39.         }
  40.         return c;
  41.     }

  42.     public static HashMap<String, String> CheckBeaconHash(Connection c, String hash) {
  43.         HashMap<String, String> Beacon = new HashMap<>();
  44.         try {
  45.             PreparedStatement ps = c.prepareStatement("SELECT * FROM Beacon WHERE Hash = (?) ORDER BY UpdateNoteTime DESC;");
  46.             ps.setString(1, hash);
  47.             ResultSet rs = ps.executeQuery();
  48.             while (rs.next()) {
  49.                 if (rs.getString("Hash").equals(hash)) {
  50.                     Beacon.put("StartTime", rs.getString("StartTime"));
  51.                     Beacon.put("Note", rs.getString("Note"));
  52.                     ps.close();
  53.                     c.close();
  54.                     return Beacon;
  55.                 }
  56.             }
  57.             rs.close();
  58.             ps.close();
  59.             c.close();
  60.             return null;
  61.         } catch (SQLException e) {
  62.             throw new RuntimeException(e);
  63.         }
  64.     }

  65.     public static HashMap<String, String> CheckBeacon(Connection c, String hash, String BeaconId) {
  66.         HashMap<String, String> Beacon = new HashMap<>();
  67.         try {
  68.             PreparedStatement ps = c.prepareStatement("SELECT * FROM Beacon WHERE Hash = (?) AND BeaconId = (?) ORDER BY UpdateNoteTime DESC;");
  69.             ps.setString(1, hash);
  70.             ps.setString(2, BeaconId);
  71.             ResultSet rs = ps.executeQuery();
  72.             while (rs.next()) {
  73.                 if (rs.getString("Hash").equals(hash)) {
  74.                     Beacon.put("StartTime", rs.getString("StartTime"));
  75.                     Beacon.put("Note", rs.getString("Note"));
  76.                     ps.close();
  77.                     c.close();
  78.                     return Beacon;
  79.                 }
  80.             }
  81.             rs.close();
  82.             ps.close();
  83.             c.close();
  84.             return null;
  85.         } catch (SQLException e) {
  86.             throw new RuntimeException(e);
  87.         }
  88.     }

  89.     public static HashMap<String, String> CheckSShBeacon(Connection c, info BeaconInfo) {
  90.         HashMap<String, String> Beacon = new HashMap<>();
  91.         try {
  92.             PreparedStatement ps = c.prepareStatement("SELECT * FROM Beacon WHERE Computer = (?) and User = (?) and Arch = (?) and Process = (?) and External = (?) ORDER BY id DESC;");
  93.             ps.setString(1, BeaconInfo.Computer);
  94.             ps.setString(2, BeaconInfo.User);
  95.             ps.setString(3, BeaconInfo.Arch);
  96.             ps.setString(4, BeaconInfo.Process);
  97.             ps.setString(5, BeaconInfo.External);
  98.             ResultSet rs = ps.executeQuery();
  99.             while (rs.next()) {
  100.                 if (rs != null) {
  101.                     Beacon.put("Hash", rs.getString("Hash"));
  102.                     Beacon.put("StartTime", rs.getString("StartTime"));
  103.                     Beacon.put("Note", rs.getString("Note"));
  104.                     Beacon.put("Id", rs.getString("Id"));
  105.                     Beacon.put("External", rs.getString("External"));
  106.                     Beacon.put("Internal", rs.getString("Internal"));
  107.                     Beacon.put(DOMKeyboardEvent.KEY_PROCESS, rs.getString(DOMKeyboardEvent.KEY_PROCESS));
  108.                     Beacon.put("Arch", rs.getString("Arch"));
  109.                     Beacon.put("User", rs.getString("User"));
  110.                     Beacon.put("Computer", rs.getString("Computer"));
  111.                     ps.close();
  112.                     c.close();
  113.                     return Beacon;
  114.                 }
  115.             }
  116.             rs.close();
  117.             ps.close();
  118.             c.close();
  119.             return null;
  120.         } catch (SQLException e) {
  121.             throw new RuntimeException(e);
  122.         }
  123.     }

  124.     public static void AddBeacon(Connection c, info BeaconInfo) {
  125.         String times = utils.BeijingTime.formatToBeijingTime();
  126.         try {
  127.             PreparedStatement ps = c.prepareStatement("INSERT INTO Beacon (Hash,StartTime,Note,BeaconId,External,Process,Arch,User,Computer,Internal,UpdateNoteTime) VALUES (?, ?, "",?,?,?,?,?,?,?,?);");
  128.             ps.setString(1, BeaconInfo.Hash);
  129.             ps.setString(2, times);
  130.             ps.setString(3, BeaconInfo.BeaconId);
  131.             ps.setString(4, BeaconInfo.External);
  132.             ps.setString(5, BeaconInfo.Process);
  133.             ps.setString(6, BeaconInfo.Arch);
  134.             ps.setString(7, BeaconInfo.User);
  135.             ps.setString(8, BeaconInfo.Computer);
  136.             ps.setString(9, BeaconInfo.Internal);
  137.             ps.setString(10, times);
  138.             ps.execute();
  139.             if (ps != null) {
  140.                 ps.close();
  141.             }
  142.             if (c != null) {
  143.                 c.close();
  144.             }
  145.         } catch (SQLException e) {
  146.             throw new RuntimeException(e);
  147.         }
  148.     }

  149.     public static void AddBeacon2(Connection c, info BeaconInfo) {
  150.         try {
  151.             PreparedStatement ps = c.prepareStatement("INSERT INTO Beacon (Hash,StartTime,Note,BeaconId,External,Process,Arch,User,Computer,Internal,UpdateNoteTime) VALUES (?, ?, ?,?,?,?,?,?,?,?,"");");
  152.             ps.setString(1, BeaconInfo.Hash);
  153.             ps.setString(2, BeaconInfo.StartTime);
  154.             ps.setString(3, BeaconInfo.Note);
  155.             ps.setString(4, BeaconInfo.BeaconId);
  156.             ps.setString(5, BeaconInfo.External);
  157.             ps.setString(6, BeaconInfo.Process);
  158.             ps.setString(7, BeaconInfo.Arch);
  159.             ps.setString(8, BeaconInfo.User);
  160.             ps.setString(9, BeaconInfo.Computer);
  161.             ps.setString(10, BeaconInfo.Internal);
  162.             ps.execute();
  163.             if (ps != null) {
  164.                 ps.close();
  165.             }
  166.             if (c != null) {
  167.                 c.close();
  168.             }
  169.         } catch (SQLException e) {
  170.             throw new RuntimeException(e);
  171.         }
  172.     }

  173.     public static void UpBeaconNote(Connection c, String BeaconId, String Note) {
  174.         String times = utils.BeijingTime.formatToBeijingTime();;
  175.         try {
  176.             PreparedStatement ps = c.prepareStatement("UPDATE Beacon set Note = ? , UpdateNoteTime = ? where BeaconId=?;");
  177.             ps.setString(1, Note);
  178.             ps.setString(2, times);
  179.             ps.setString(3, BeaconId);
  180.             ps.executeUpdate();
  181.             ps.close();
  182.             c.close();
  183.         } catch (SQLException e) {
  184.             throw new RuntimeException(e);
  185.         }
  186.     }
  187.     public static void UpBeaconLastTime(Connection c, String BeaconId, String Note) {
  188.         String times = utils.BeijingTime.formatToBeijingTime();;
  189.         try {
  190.             PreparedStatement ps = c.prepareStatement("UPDATE Beacon set LastTime = ? where BeaconId=?;");
  191.             ps.setString(1, times);
  192.             ps.setString(2, BeaconId);
  193.             ps.executeUpdate();
  194.             ps.close();
  195.             c.close();
  196.         } catch (SQLException e) {
  197.             throw new RuntimeException(e);
  198.         }
  199.     }

  200.     public static void UpBeaconId(Connection c, String Hash, String BeaconId) {
  201.         try {
  202.             PreparedStatement ps = c.prepareStatement("UPDATE Beacon set BeaconId = ? where Hash=?;");
  203.             ps.setString(1, BeaconId);
  204.             ps.setString(2, Hash);
  205.             ps.executeUpdate();
  206.             ps.close();
  207.             c.close();
  208.         } catch (SQLException e) {
  209.             throw new RuntimeException(e);
  210.         }
  211.     }
  212. }
复制代码




随机用户名后缀

在护网时遇到比较多的一个问题,就是开着热点上的cs,中途去吃饭了结果回来再连上热点就会显示该用户已使用,导致就要重启客户端,因此在此处新增用户名后缀防止重复登录。

主要修改点在server/ManageUser.java中,在process函数中新增:



这个随机字符串我控制在长度为3,对应实现的类为:



最终效果就是这样,终于解决了这个烦人的问题:



aggressor 新增监听器时默认显示远程 IP

在原版CS中,每次新增监听器时默认值为本地的IP地址,还需要手动改为远程IP地址,因此在这里对这部分代码进行修改:

Connect.java:新增public static String aggressorRemoteIp = "";




然后在ScListenerDialog.java修改显示参数:



show_http和show_https都改掉:



最后更改效果为:


CNA 脚本触发java端内置函数

这个操作就跟上面 Event log显示远程IP差不多,主要单独列出来方便直接看。
default.cna:



item("&New Connection", { openConnectDialog(); });


调用的是openConnectDialog(),这个在java端的aggressor/bridges/AggressorBridges.java中进行判断,如果点击了这个,则调用这个文件中的代码。AggressorBridges.java先是注册,再是响应:

注册:



响应:



最后执行的代码为:

(new ConnectDialog(this.window)).show();


这个代码是在aggressor/dialogs/ConnectDialog.java中的类。实际上aggressor界面上的那些按钮的触发都是这个套路,因此我们后续如果不想用纯粹的cna脚本,就可以用cna脚本触发,函数写死在java端,好处就是把一些不改动的cna脚本写死在里面,不用拷贝给队友时还需要额外附带脚本。cna脚本写死并加载的地方在aggressor/AggressorClient.java中:



aggressor 提取监听器信息

这个没什么绕的地方,主要就是分析代码,我直接贴提取方式了:

  1. String lname = DialogUtils.string(var2, "listener"); // 监听器名称,通过 lname 可获得 lhost 和 lport
  2. String lhost = ListenerUtils.getListener(this.client, lname).getCallbackHosts();    // 监听器IP
  3. String lport = String.valueOf(ListenerUtils.getListener(this.client, lname).getPort()); // 监听器端口

复制代码


我之所以弄这个,是之前想将免杀模块放在本地来进行操作,后来这个方案被我废弃了,打算将免杀流程放在teamserver端进行操作,再回传至aggressor。

aggressor 发送信号

发送信号我是分析的钓鱼模块,aggressor交互都在aggressor/dialogs里面,发送信号使用的是TeamQueue类,CS的通信主要用的就是这个类:

  1. protected TeamQueue conn = null;
  2. this.conn.call("SendSign.test", CommonUtils.args(var1, var2, var3, checksum));
复制代码




其中"SendSign.test"代表的是发送数据的标签,服务端需要根据这个标签进行判断,后面的几个都是传递的参数。我自己实现的调用逻辑为:
dialogAction() -> send() -> final_send()

  1. public void dialogAction(ActionEvent var1, Map var2) {
  2.     String lname = DialogUtils.string(var2, "listener"); // 监听器名称,通过 lname 可获得 lhost 和 lport
  3.     String lhost = ListenerUtils.getListener(this.client, lname).getCallbackHosts();    // 监听器IP
  4.     String lport = String.valueOf(ListenerUtils.getListener(this.client, lname).getPort()); // 监听器端口
  5.    
  6.     String[] stringArray = {lname,lhost,lport};
  7.     this.send(var1, var2, stringArray); // var1 与 var2 固定,var3 传递传给 final_send 的参数
  8. }

  9. public void send(ActionEvent var1, Map var2,String[] var3) {
  10.     this.final_send(var3[0],var3[1],var3[2]);
  11. }
  12. private void final_send(String var1,String var2,String var3) {
  13.     this.conn.call("SendSign.test", CommonUtils.args(var1, var2, var3, checksum));
  14.     System.out.println("发送的checksum为:"+ checksum);
  15. }


  16. server 接收信号

  17. 与dialog.java成对存在的就是server目录中的文件,我这自己新建了一个类,具体接收信号的函数为:

  18.    
  19. public void call(Request var1, ManageUser var2) { // 实际运行的函数
  20.         String var4;
  21.         if (var1.is("SendSign.test", 4)) {  // 前三个参数跟编译相关,第四个为 checksum 校验值
  22.             synchronized(this) {
  23.                 int result = genCrossC2((String) var1.arg(0), (String) var1.arg(1), (String) var1.arg(2)); // 打印输入的参数,需要将 object 强制转为 string
  24.                 CommonUtils.print_info("result: "+ result);
  25.                 if (result == 0){
  26.                     this.resources.broadcast("genCrossC2", (String) var1.arg(3), true);
  27.                     System.out.println("send checksum: "+ (String) var1.arg(3));
  28.                 }
  29.             }
  30.         }
  31.     }

复制代码


其中比较重要的就是var1.is("SendSign.test", 4),这个与aggressor传来的数据是相对应的,4为四个参数。

server 进行广播

广播的代码为:

  1. this.resources.broadcast("genCrossC2", (String) var1.arg(3), true);
复制代码




这个true的含义貌似是aggressor重连后还会接收到这个信息,我改为false也没什么变化,广播的数据为Map类型。

aggressor 接收广播

接收广播部分有个巨坑,如果直接使用TeamQueue提供的setSubscriber回调函数来接收广播,则会导致CS的UI不会刷新。存在问题的代码为:

  1. conn.setSubscriber(new Callback() {
  2.     @Override
  3.     public void result(String call, Object content) { //
  4.         if ("genCrossC2".equals(call)) {
  5.             System.out.println("接收到 genCrossC2 广播!");
  6.             if (content instanceof String) {
  7.                 String contentString = (String) content;
  8.                 if ("OK".equals(contentString)) {
  9.                     System.out.println("广播的内容是 'OK'!");
  10.                 }
  11.             }
  12.         }
  13.     }
  14. });
复制代码




这个代码虽然可以正确接收,但是UI不刷新肯定是存在问题的,后来换了一个方案:

  1. this.conn.setSubscriber(this.data);
  2. String response = data.getDataSafe("genCrossC2").toString();
  3. if(checksum.equals(response)){
  4.     System.out.println("接收到服务端广播的内容是:" + checksum);
  5.     break;
  6. }
复制代码




在使用这个时,一定要注意是否data中存在这个键值对,如果不存在就会空指针异常,因此我在dialogAction中去检索data数据前先使用this.data.put("key","value");进行了置空操作,问题就解决了。

总结

以上是基于下面的模型进行讲解的,掌握这个模型一系列操作方式后CS的可玩性还是非常高的。


本人也是刚刚学习java开发,文中可能存在诸多问题,欢迎大家指出!



看雪ID:bwner
https://bbs.kanxue.com/user-home-951654.htm
*本文为看雪论坛优秀文章,由 bwner 原创,转载请注明来自看雪社区



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-27 22:41 , Processed in 0.014573 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表