CS 4.4 二开笔记:基础篇
2023-10-14 16:52:56 Author: mp.weixin.qq.com(查看原文) 阅读量:17 收藏

本篇为 CS 4.4 二开基础篇,主要对整个破解流程、漏洞修复、主题修改、新增功能进行讲解,针对CS的扫描方法及Bypass方法后续进行补充。


使用工具

1.Idea 2023

2.java-decompiler.jar

3.cs 4.4

4.CrackSleeve


逆向流程

新建两个文件夹,cs_bin里面放原版cs,cs_src放之后反编译过后的cs,再将java-decompiler.jar放到根目录。目录结构如下:

cobaltstrike
├─cs_bin
│ └─ cobaltstrike.jar
└─cs_src
└─java-decompiler.jar

切换 java 版本到 java 11:

sudo update-alternatives --config java

在根目录输入以下命令将cs_bin/cobaltstrike.jar进行反编译,反编译后的文件放到cs_src文件夹中:

java -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true cs_bin/cobaltstrike.jar cs_src/

正常反编译如图所示:

新建 idea java 项目,工程命名为cs,在工程下面新建两个文件夹:

1.decompiled_src(放置反编译版cs解压后的文件)
2.lib(放置原版cs)



把反编译好的cs_src/cobaltstrike.jar,复制到decompiled_src中,然后把它解压出来,可看到一个完整的反编译后的目录:

随后把原始的cs_bin/cobaltstrike.jar,复制到lib中:




接下来点击File->Project Structure->Modules,点击Dependencies,点击+号选择JARs or Directories:



选择lib中的原版 cobalt strike.jar,点击ok,勾选:



打开lib->META-INF->MANIFEST.MF,可以看到Main Class,复制aggressor.Aggressor:

至此依赖关系设置完了,现在进入File->Project Structure->Artifacts—>JAR—>From modules with dependencies:

Main Class填入aggressor.Aggressor,点击ok,再点击Apply:


接下来到decompiled_src/aggressor/Aggressor.java,右键选择Refactor->Copy File:



点击 To directory 右边的...

新建目录,目录创建在src下,src目录是我们进行修改代码的地方:




创建aggressor,名字必须要与decompiled_src一致,如果是在aggressor下拷贝的文件就要放在src/aggressor下。如果是在aggressor/bridges下拷贝的文件就要放在src/aggressor/bridges下,目录结构必须保持一致:

完成上面的操作后Aggressor.java就被拷贝到src/aggressor,并且自动打开了Aggressor.java:


打开decompiled_src/META-INF/MANIFEST.MF,把里面的所有内容复制到src/META-INF/MANIFEST.MF


修改前:




修改后:




之后就是我们要修改哪个文件,就可以在完整的源码中找到那个文件,然后右键Refactor然后Copy File到这个目录然后进行修改,修改完成之后就可以选Build—>Build Artifacts—>Build进行编译,编译后的文件在out -> artifacts -> cs_jar -> cs.jar




点击Run->Profile->Edit Configurations设置参数,点击+号,选择JAR Application




Name可以随便输入,我输入的是MyCs,在Path to JAR中选择out中我们修改并编译好的jar包,
设置
VM options:-XX:+AggressiveHeap -XX:+UseParallelGC
选择好后点击Apply:




运行之后像下面这样就说明成功了,剩下的就是破解许可证和除掉插桩了:



永久许可

修改/src/common/Authorization.java文件:

先在src目录下新建common目录:





接着修改Authorization()函数,直接用下面的代码对该函数进行覆盖:

   

public Authorization() {
try {
this.watermark=999999;
this.validto="forever";
this.valid = true;
final byte[] bytes = {94, -104, 25, 74, 1, -58, -76, -113, -91, -126, -90, -87, -4, -69, -110, -42};
MudgeSanity.systemDetail("valid to", "perpetual");
MudgeSanity.systemDetail("id", this.watermark + "");
SleevedResource.Setup(bytes);
}
catch (Exception ex2) {
MudgeSanity.logException("auth file parsing", ex2, false);
}
}

虽然我们已经有了许可,但因为暗桩的存在我们无法正常运行。


去除暗桩

存在暗桩的地方:

1.common/Helper
2.common/Starter
3.common/Starter2
4.beacon/CommandBuilder

common目录下的 Helper、Starter、Starter2 清除暗桩后可以进入客户端:




直接搜每个代码的exit,将其注释掉:




最后再重新build artifacts,再运行就可以了:




beacon/CommandBuilder中的暗桩会让client和temserver连续连接4小时后无法执行命令,将这部分注释掉:




至此除去暗桩部分就完成了。


漏洞修复

CS 修复错误路径泄漏stage

修改 cloudstrike/WebServer.java,新增对uri的判断,如果请求的uri开头不是/就相应404,新增代码如下:

else if (!uri.startsWith("/")) {
return this.processResponse(uri, method, header, param, false, null, new Response("404 Not Found.","text/html",""));
}

CVE-2022-39197 RCE

该漏洞是通过XSS造成的RCE,需要修改common/BeaconEntry.java将xss漏洞点进行修复:

下载commons-lang3-3.13.0.jarcommons-text-1.10.0.jar,导入到lib文件夹中:



接着设置Project Structure中的ModulesArtifacts





接着在代码中使用StringEscapeUtils.escapeHtml4对漏洞点进行转义就完成了漏洞的修复。

import org.apache.commons.text.StringEscapeUtils;

HashMap var1 = new HashMap();
var1.put("external", StringEscapeUtils.escapeHtml4(this.ext));
var1.put("internal", StringEscapeUtils.escapeHtml4(this.intz));
var1.put("host", StringEscapeUtils.escapeHtml4(this.intz));
var1.put("user", StringEscapeUtils.escapeHtml4(this.user));
var1.put("computer", StringEscapeUtils.escapeHtml4(this.comp));
var1.put("last", StringEscapeUtils.escapeHtml4(this.diff + ""));
var1.put("lastf", StringEscapeUtils.escapeHtml4(this.getLastCheckin()));
var1.put("id", StringEscapeUtils.escapeHtml4(this.id));
var1.put("pid", StringEscapeUtils.escapeHtml4(this.getPid()));
var1.put("is64", StringEscapeUtils.escapeHtml4(this.is64));
var1.put("pbid", StringEscapeUtils.escapeHtml4(this.pbid));
var1.put("note", StringEscapeUtils.escapeHtml4(this.note));
var1.put("barch", StringEscapeUtils.escapeHtml4(this.barch));
var1.put("arch", StringEscapeUtils.escapeHtml4(this.barch));
var1.put("port", StringEscapeUtils.escapeHtml4(this.getPort()));
var1.put("charset", StringEscapeUtils.escapeHtml4(this.getCharset()));
var1.put("phint", StringEscapeUtils.escapeHtml4(this.hint + ""));
var1.put("process", StringEscapeUtils.escapeHtml4(this.proc));
var1.put("_accent", StringEscapeUtils.escapeHtml4(this.accent));
var1.put("listener", StringEscapeUtils.escapeHtml4(this.lname));
var1.put("build", (this.build));
var1.put("address", this.getIpAddress(this.ext)); // add address


主题修改

修改配置界面提示

aggressor/dialogs/ConnectDialog文件:

修改后:




启动服务端测试:

teamserver文件:

if [ -e ./cobaltstrike.store ]; then
echo "Will use existing X509 certificate and keystore (for SSL)"
else
echo "Generating X509 certificate and keystore (for SSL)"
keytool -keystore ./cobaltstrike.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias cobaltstrike -dname "C=US, ST=Maryland, L=Pasadena, O=Brent Baccala, OU=FreeSoft, CN=www.freesoft.org/[email protected]"
fi

# start the team server.
java -XX:ParallelGCThreads=4 -Dcobaltstrike.server_port=22222 -Djavax.net.ssl.keyStore=./cobaltstrike.store -Djavax.net.ssl.keyStorePassword=231422 -server -XX:+AggressiveHeap -XX:+UseParallelGC -classpath ./csproject.jar server.TeamServer $*

teamserver <host> <password> [/path/to/c2.profile] [YYYY-MM-DD]

<host> is the (default) IP address of this Cobalt Strike team server
<password> is the shared password to connect to this server
[/path/to/c2.profile] is your Malleable C2 profile
[YYYY-MM-DD] is a kill date for Beacon payloads run from this server



启动客户端测试:

client文件:

java -XX:ParallelGCThreads=4 -XX:+AggressiveHeap -XX:+UseParallelGC -Xmx1024M -jar csproject.jar $*

修改客户端 About 和图标

原版:


1.介绍部分位置在resources/about.html。

2.许可部分位置在resources/credits.txt。

3.图标位置在resources/armitage-icon.gifresources/armitage-logo.gif,icon是32×32px,logo是256×256px

关于图标的引用在aggressor/dialogs/AboutDialog中,可以对引入的logo大小进行修改,我将大小改为了192×192px,并设置图片与许可之间相隔10px:

package aggressor.dialogs;

import aggressor.Aggressor;
import common.AObject;
import common.CommonUtils;
import dialog.DialogUtils;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.text.DefaultCaret;

public class AboutDialog extends AObject {
public void show() {
JFrame var1 = DialogUtils.dialog("About", 320, 200);
var1.setLayout(new BorderLayout());

Icon originalIcon = DialogUtils.getIcon("resources/armitage-logo.gif");
ImageIcon icon = (ImageIcon) originalIcon;
Image image = icon.getImage();
Image scaledImage = image.getScaledInstance(192, 192, Image.SCALE_SMOOTH);
icon = new ImageIcon(scaledImage);

JLabel var2 = new JLabel(icon);
var2.setBackground(Color.black);
var2.setForeground(Color.gray);
var2.setOpaque(true);
var2.setIconTextGap(5); // Set the gap between the icon and text to 5px

JTextArea var3 = new JTextArea();
var3.setBackground(Color.black);
var3.setForeground(Color.gray);
var3.setEditable(false);
var3.setFocusable(false);
var3.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
var3.setOpaque(false);
var3.setLineWrap(true);
var3.setWrapStyleWord(true);

String var4 = CommonUtils.bString(CommonUtils.readResource("resources/about.html"));
var2.setText(var4);
var3.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
((DefaultCaret)var3.getCaret()).setUpdatePolicy(1);

JScrollPane var5 = new JScrollPane(var3, 22, 31);
var5.setPreferredSize(new Dimension(var5.getWidth(), 100));

String var6 = CommonUtils.bString(CommonUtils.readResource("resources/credits.txt"));
var3.setText(var6);

var5.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
var1.add(var2, "Center");
var1.add(var5, "South");
var1.pack();
var1.setLocationRelativeTo(Aggressor.getFrame());
var1.setVisible(true);
}
}

与原文件对比:

修改后:


修改界面主题

CS使用的是Swing UI,这里下载flatlaf主题:
https://mvnrepository.com/artifact/com.formdev/flatlaf/3.2




将下载后的jar文件导入到lib文件夹中:




打开Project Structure,打开Modules->Dependencies,添加这个jar到依赖中,勾选然后应用:





接着打开artifacts,引用flatlaf.jar,再点击应用:



添加后:




接着在主函数调用这里直接调用主题,文件为aggressor/Aggressor.java

FlatLightLaf.setup();



要把主题的代码放在super.initializeStarter(this.getClass());前面,这行代码是进行初始化的。


因为已经导入了包,所以在输入Flat后就会提示可选主题,我使用的是明亮主题,运行后:



修复图标问题

session 修复


修复:
aggressor/browsers/Sessions中添加:
导入包:

import java.awt.Component;
import java.util.function.Consumer;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

新增修复代码并居中:

// repair session show
this.table.getColumnModel().getColumns().asIterator().forEachRemaining(new Consumer<TableColumn>() {
public void accept(TableColumn tableColumn) {
tableColumn.setCellRenderer(new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component tableCellRendererComponent = table.getDefaultRenderer(String.class).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
((JLabel) tableCellRendererComponent).setIcon(null);
((JLabel) tableCellRendererComponent).setHorizontalAlignment(JLabel.CENTER); // 设置水平居中对齐
return tableCellRendererComponent;
}
});
}
});

修改后的完整代码:

package aggressor.browsers;

import aggressor.AggressorClient;
import aggressor.DataUtils;
import common.AObject;
import common.Callback;
import dialog.DialogUtils;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowListener;
import javax.swing.JComponent;
import ui.ATable;
import ui.GenericTableModel;
import ui.TablePopup;

import java.awt.Component;
import java.util.function.Consumer;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class Sessions extends AObject implements Callback, TablePopup {
protected AggressorClient client = null;
protected GenericTableModel model = null;
protected ATable table = null;
protected String[] cols = new String[]{" ", "external", "internal", "listener", "user", "computer", "note", "process", "pid", "arch", "last"};
protected boolean multipleSelect;

public ATable getTable() {
return this.table;
}

public void setColumns(String[] var1) {
this.cols = var1;
}

public Sessions(AggressorClient var1, boolean var2) {
this.client = var1;
this.multipleSelect = var2;
}

public ActionListener cleanup() {
return this.client.getData().unsubOnClose("beacons", this);
}

public WindowListener onclose() {
return this.client.getData().unsubOnClose("beacons", this);
}

public boolean hasSelectedRows() {
return this.model.hasSelectedRows(this.table);
}

public Object[] getSelectedValues() {
return this.model.getSelectedValues(this.table);
}

public Object getSelectedValue() {
return this.model.getSelectedValue(this.table) + "";
}

public void showPopup(MouseEvent var1) {
DialogUtils.showSessionPopup(this.client, var1, this.model.getSelectedValues(this.table));
}

public JComponent getContent() {
if (this.cols.length == 11) {
this.model = DialogUtils.setupModel("id", this.cols, DataUtils.getBeaconModel(this.client.getData()));
} else {
this.model = DialogUtils.setupModel("id", this.cols, DataUtils.getBeaconModel(this.client.getData()));
}

this.table = DialogUtils.setupTable(this.model, this.cols, this.multipleSelect);
if (this.cols.length == 11) {
DialogUtils.sortby(this.table, 2, 8);
this.table.getColumn("arch").setPreferredWidth(96);
this.table.getColumn("arch").setMaxWidth(96);
} else {
DialogUtils.sortby(this.table, 1);
}

// repair session show
this.table.getColumnModel().getColumns().asIterator().forEachRemaining(new Consumer<TableColumn>() {
public void accept(TableColumn tableColumn) {
tableColumn.setCellRenderer(new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component tableCellRendererComponent = table.getDefaultRenderer(String.class).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
((JLabel) tableCellRendererComponent).setIcon(null);
((JLabel) tableCellRendererComponent).setHorizontalAlignment(JLabel.CENTER); // 设置水平居中对齐
return tableCellRendererComponent;
}
});
}
});

this.table.getColumn(" ").setPreferredWidth(32);
this.table.getColumn(" ").setMaxWidth(32);
this.table.getColumn("pid").setPreferredWidth(96);
this.table.getColumn("pid").setMaxWidth(96);
this.table.getColumn("last").setPreferredWidth(96);
this.table.getColumn("last").setMaxWidth(96);
DialogUtils.setupImageRenderer(this.table, this.model, " ", "image");
DialogUtils.setupTimeRenderer(this.table, "last");
this.table.setPopupMenu(this);
this.client.getData().subscribe("beacons", this);
return DialogUtils.FilterAndScroll(this.table);
}

public void result(String var1, Object var2) {
if (this.table.isShowing()) {
DialogUtils.setTable(this.table, this.model, DataUtils.getBeaconModelFromResult(var2));
}

}
}

target 修复

通过修改aggressor/browsers/Targets.java的getContent函数的表格属性即可修复:


未修复前:




修复后:



新增代码:

DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
centerRenderer.setHorizontalAlignment(JLabel.CENTER);
this.table.getColumn("address").setCellRenderer(centerRenderer);
this.table.getColumn("name").setCellRenderer(centerRenderer);
this.table.getColumn("note").setCellRenderer(centerRenderer);

修复后完整代码:

package aggressor.browsers;

import aggressor.AggressorClient;
import common.AObject;
import common.AdjustData;
import common.BeaconEntry;
import common.CommonUtils;
import dialog.ActivityPanel;
import dialog.DialogUtils;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;

import ui.ATable;
import ui.GenericTableModel;
import ui.QueryRows;
import ui.TablePopup;

public class Targets extends AObject implements AdjustData, TablePopup, QueryRows {
protected AggressorClient client = null;
protected ActivityPanel dialog = null;
protected GenericTableModel model = null;
protected ATable table = null;
protected String[] cols = new String[]{" ", "address", "name", "note"};
protected LinkedList targets = new LinkedList();
protected Set compromised = new HashSet();

public Targets(AggressorClient var1) {
this.client = var1;
}

public ATable getTable() {
return this.table;
}

public ActionListener cleanup() {
return this.client.getData().unsubOnClose("targets, beacons", this);
}

public Map format(String var1, Object var2) {
HashMap var3 = new HashMap((Map)var2);
boolean var4 = this.compromised.contains((String)var3.get("address"));
ImageIcon var5 = DialogUtils.TargetVisualizationSmall(var3.get("os") + "", CommonUtils.toDoubleNumber(var3.get("version") + "", 0.0D), var4, false);
var3.put("image", var5);
var3.put("owned", var4 ? Boolean.TRUE : Boolean.FALSE);
return var3;
}

public JComponent getContent() {
this.client.getData().subscribe("beacons", this);
this.targets = this.client.getData().populateListAndSubscribe("targets", this);
this.model = DialogUtils.setupModel("address", this.cols, this.targets);
this.table = DialogUtils.setupTable(this.model, this.cols, true);
this.table.setPopupMenu(this);
DialogUtils.sortby(this.table, 1);
Map var1 = DialogUtils.toMap("address: 125, name: 125, note: 625");
DialogUtils.setTableColumnWidths(this.table, var1);
this.table.getColumn(" ").setPreferredWidth(32);
this.table.getColumn(" ").setMaxWidth(32);
DialogUtils.setupImageRenderer(this.table, this.model, " ", "image");
DialogUtils.setupBoldOnKeyRenderer(this.table, this.model, "address", "owned");
DialogUtils.setupBoldOnKeyRenderer(this.table, this.model, "name", "owned");
DialogUtils.setupBoldOnKeyRenderer(this.table, this.model, "note", "owned");

DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
centerRenderer.setHorizontalAlignment(JLabel.CENTER);
this.table.getColumn("address").setCellRenderer(centerRenderer);
this.table.getColumn("name").setCellRenderer(centerRenderer);
this.table.getColumn("note").setCellRenderer(centerRenderer);

return DialogUtils.FilterAndScroll(this.table);
}

public Map[] getSelectedRows() {
return this.model.getSelectedRows(this.table);
}

public void showPopup(MouseEvent var1) {
Stack var2 = new Stack();
var2.push(CommonUtils.toSleepArray(this.model.getSelectedValues(this.table)));
this.client.getScriptEngine().getMenuBuilder().installMenu(var1, "targets", var2);
}

public void refresh() {
this.targets = CommonUtils.apply("targets", this.targets, this);
DialogUtils.setTable(this.table, this.model, this.targets);
}

public void result(String var1, Object var2) {
if ("targets".equals(var1)) {
this.targets = new LinkedList((LinkedList)var2);
this.refresh();
if (this.dialog != null) {
this.dialog.touch();
}
} else if ("beacons".equals(var1)) {
HashSet var3 = new HashSet();
Iterator var4 = ((Map)var2).values().iterator();

while(var4.hasNext()) {
BeaconEntry var5 = (BeaconEntry)var4.next();
if (var5.isActive()) {
var3.add(var5.getInternal());
}
}

if (!var3.equals(this.compromised)) {
this.compromised = var3;
this.refresh();
}
}

}

public boolean hasSelectedRows() {
return this.model.hasSelectedRows(this.table);
}

public void notifyOnResult(ActivityPanel var1) {
this.dialog = var1;
}
}

文件浏览器修复

修改aggressor/Aggressor.java

package aggressor;

import aggressor.dialogs.ConnectDialog;
import aggressor.ui.UseSynthetica;
import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatIntelliJLaf;
import com.formdev.flatlaf.FlatLightLaf;
import common.Authorization;
import common.License;
import common.Requirements;
import common.Starter;
import javax.swing.UIManager;
import sleep.parser.ParserConfig;

public class Aggressor extends Starter {
public static final String VERSION = "4.4 (20210801) " + (License.isTrial() ? "Trial" : "Licensed");
public static final String VERSION_SHORT = "4.4";
private static MultiFrame B = null;

public static MultiFrame getFrame() {
return B;
}

public static void main(String[] var0) {
Aggressor var1 = new Aggressor();
var1.A(var0);
}

private final void A(String[] var1) {
ParserConfig.installEscapeConstant('c', "\u0003");
ParserConfig.installEscapeConstant('U', "\u001f");
ParserConfig.installEscapeConstant('o', "\u000f");
(new UseSynthetica()).setup();

// repair file explorer
Object DirIcon = UIManager.get("FileView.directoryIcon");
Object DirIcon2 = UIManager.get("FileView.fileIcon");
Object DirIcon3 = UIManager.get("FileView.computerIcon");
Object DirIcon4 = UIManager.get("FileView.hardDriveIcon");
Object DirIcon5 = UIManager.get("FileView.floppyDriveIcon");

Requirements.checkGUI();

try {
FlatIntelliJLaf.setup();
UIManager.put("FileView.directoryIcon",DirIcon);
UIManager.put("FileView.fileIcon",DirIcon2);
UIManager.put("FileView.computerIcon",DirIcon3);
UIManager.put("FileView.hardDriveIcon",DirIcon4);
UIManager.put("FileView.floppyDriveIcon",DirIcon5);
} catch( Exception ex ) {
System.err.println( "Failed to initialize LaF" );
}

License.checkLicenseGUI(new Authorization());
B = new MultiFrame();
super.initializeStarter(this.getClass());
(new ConnectDialog(B)).show();
}
}

listeners 修复

文件为aggressor/windows/ListenerManager.java


新增:

// Add the cell renderer
DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
centerRenderer.setHorizontalAlignment(JLabel.CENTER);
for (int i = 0; i < this.table.getColumnCount(); i++) {
this.table.getColumnModel().getColumn(i).setCellRenderer(centerRenderer);
}

修改后可正常显示:




修改后的完整代码为:

package aggressor.windows;

import aggressor.AggressorClient;
import aggressor.ColorManager;
import aggressor.DataManager;
import aggressor.DataUtils;
import aggressor.dialogs.ScListenerDialog;
import common.AObject;
import common.AdjustData;
import common.BeaconEntry;
import common.Callback;
import common.CommonUtils;
import common.ListenerTasks;
import common.TeamQueue;
import cortana.Cortana;
import dialog.DialogUtils;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;

import ui.ATable;
import ui.GenericTableModel;
import ui.QueryableTable;
import ui.TablePopup;

public class ListenerManager extends AObject implements AdjustData, Callback, ActionListener, TablePopup {
protected TeamQueue conn = null;
protected Cortana engine = null;
protected DataManager data = null;
protected AggressorClient client = null;
protected GenericTableModel model = null;
protected ATable table = null;
protected String[] cols = new String[]{"name", "payload", "host", "port", "bindto", "beacons", "profile"};

public ListenerManager(AggressorClient var1) {
this.engine = var1.getScriptEngine();
this.conn = var1.getConnection();
this.data = var1.getData();
this.client = var1;
this.model = DialogUtils.setupModel("name", this.cols, CommonUtils.apply("listeners", DataUtils.getListenerModel(this.data), this));
this.data.subscribe("listeners", this);
}

public ActionListener cleanup() {
return this.data.unsubOnClose("listeners", this);
}

public void actionPerformed(ActionEvent var1) {
if ("Add".equals(var1.getActionCommand())) {
(new ScListenerDialog(this.client)).show();
} else if ("Edit".equals(var1.getActionCommand())) {
if (!this.model.hasSelectedRows(this.table)) {
DialogUtils.showError(DialogUtils.MessageID.A_ROW_MUST_BE_SELECTED);
return;
}

String var2 = this.model.getSelectedValue(this.table) + "";
(new ListenerTasks(this.client, var2)).edit();
} else {
int var3;
Object[] var4;
if ("Remove".equals(var1.getActionCommand())) {
var4 = this.model.getSelectedValues(this.table);
if (var4.length == 0) {
DialogUtils.showError(DialogUtils.MessageID.ROWS_MUST_BE_SELECTED);
return;
}

for(var3 = 0; var3 < var4.length; ++var3) {
(new ListenerTasks(this.client, (String)var4[var3])).remove();
}
} else if ("Restart".equals(var1.getActionCommand())) {
var4 = this.model.getSelectedValues(this.table);
if (var4.length == 0) {
DialogUtils.showError(DialogUtils.MessageID.ROWS_MUST_BE_SELECTED);
return;
}

for(var3 = 0; var3 < var4.length; ++var3) {
this.conn.call("listeners.restart", CommonUtils.args(var4[var3]), new Callback() {
public void result(String var1, Object var2) {
if (var2 != null) {
DialogUtils.showInfo("Updated and restarted listener: " + var2);
}

}
});
}
}
}

}

public void showPopup(MouseEvent var1) {
JPopupMenu var2 = new JPopupMenu();
JMenu var3 = new JMenu("Color");
var3.add((new ColorManager(this.client, new QueryableTable(this.table, this.model), "listeners")).getColorPanel());
var2.add(var3);
Object[] var4 = this.model.getSelectedValues(this.table);
Stack var5 = new Stack();
var5.push(CommonUtils.toSleepArray(var4));
this.client.getScriptEngine().getMenuBuilder().setupMenu(var2, "listeners", var5);
var2.show((Component)var1.getSource(), var1.getX(), var1.getY());
}

public JComponent getContent() {
JPanel var1 = new JPanel();
var1.setLayout(new BorderLayout());
this.table = DialogUtils.setupTable(this.model, this.cols, true);

// Add the cell renderer
DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
centerRenderer.setHorizontalAlignment(JLabel.CENTER);
for (int i = 0; i < this.table.getColumnCount(); i++) {
this.table.getColumnModel().getColumn(i).setCellRenderer(centerRenderer);
}

this.table.setPopupMenu(this);
DialogUtils.setupListenerStatusRenderer(this.table, this.model, "name");
DialogUtils.setTableColumnWidths(this.table, DialogUtils.toMap("name: 125, payload: 250, host: 125, port: 60, bindto: 60, beacons: 250, profile: 125"));
DialogUtils.sortby(this.table, 0);
JButton var2 = new JButton("Add");
JButton var3 = new JButton("Edit");
JButton var4 = new JButton("Remove");
JButton var5 = new JButton("Restart");
JButton var6 = new JButton("Help");
var2.addActionListener(this);
var3.addActionListener(this);
var4.addActionListener(this);
var5.addActionListener(this);
var6.addActionListener(DialogUtils.gotoURL("https://www.cobaltstrike.com/help-listener-management"));
var1.add(DialogUtils.FilterAndScroll(this.table), "Center");
var1.add(DialogUtils.center(var2, var3, var4, var5, var6), "South");
return var1;
}

public Map format(String var1, Object var2) {
Map var3 = (Map)var2;
String var4 = DialogUtils.string(var3, "bid");
String var5 = DialogUtils.string(var3, "payload");
if (!"".equals(var4) && "windows/beacon_reverse_tcp".equals(var5)) {
BeaconEntry var6 = DataUtils.getBeacon(this.data, var4);
if (var6 == null) {
var3.put("status", "pivot session does not exist");
} else if (!var6.isAlive()) {
var3.put("status", "pivot session is not alive");
}

return var3;
} else {
return var3;
}
}

public void result(String var1, Object var2) {
LinkedList var3 = CommonUtils.apply(var1, ((Map)var2).values(), this);
DialogUtils.setTable(this.table, this.model, var3);
}
}


新增功能

session 新增 IP 归属地显示


查询IP使用的库为:https://github.com/jarod/qqwry-java

在src目录下新建qqwry目录,将源码拷贝进去:




aggressor/browsers/Sessions.java新增:



cs 4.5猫猫版将所有自定义函数封装到了util.Utils中,然后再调用。

qqwry.dat放到resources中,引用路径则为resources/qqwry.dat

   

public static String getIpAddress(String ipaddress) {
if (ipaddress.length() > 15 || ipaddress.equals("unknown") || ipaddress.equals("")) {
return "未知";
}
try {
// QQWry qqwry = new QQWry(Paths.get("qqwry.dat", new String[0]));

byte[] data = CommonUtils.readResource("resources/qqwry.dat"); // add
QQWry qqwry = new QQWry(data);

IPZone ipzone = qqwry.findIP(ipaddress);
return ipzone.getMainInfo();
} catch (Exception e) {
return "Exception: " + e.getMessage();
}
}

这样直接调用会很卡顿,因此使用static解决程序开销问题:

   

/* add address */
private static QQWry qqwry;
static {
try {
byte[] data = CommonUtils.readResource("resources/qqwry.dat");
qqwry = new QQWry(data);
} catch (Exception e) {

}
}
public static String getIpAddress(String ipaddress) {
if (ipaddress.length() > 15 || ipaddress.equals("unknown") || ipaddress.equals("")) {
return "未知";
}
try {
// QQWry qqwry = new QQWry(Paths.get("qqwry.dat", new String[0]));

IPZone ipzone = qqwry.findIP(ipaddress);
return ipzone.getMainInfo();
} catch (Exception e) {
return "Exception: " + e.getMessage();
}
}

然后在toMap()中再调用这个函数:

var1.put("address", this.getIpAddress(this.ext)); // add address

修改后的完整代码如下:

package common;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;
import sleep.runtime.SleepUtils;

import qqwry.IPZone;
import qqwry.QQWry;

public class BeaconEntry implements Serializable, Loggable {
public static final int LINK_NONE = 0;
public static final int LINK_GOOD = 1;
public static final int LINK_BROKEN = 2;
protected String id = "";
protected String pid = "";
protected String ver = "";
protected int build = 0;
protected String intz = "";
protected String comp = "";
protected String user = "";
protected String is64 = "0";
protected String ext = "";
protected long last = System.currentTimeMillis();
protected long diff = 0L;
protected int state = 0;
protected int hint = 0;
protected String pbid = "";
protected String note = "";
protected String barch = "";
protected boolean alive = true;
protected String port = "";
protected boolean sane = false;
protected String chst = null;
protected String proc = "";
protected String accent = "";
protected String lname = "";
protected byte[] ptr_gmh;
protected byte[] ptr_gpa;

protected String address; // add address

public String getAddress() {
return this.address;
}
public static final int METADATA_FLAG_NOTHING = 1;
public static final int METADATA_FLAG_X64_AGENT = 2;
public static final int METADATA_FLAG_X64_SYSTEM = 4;
public static final int METADATA_FLAG_ADMIN = 8;

public int getBuild() {
return this.build;
}

public String getId() {
return this.id;
}

public boolean sane() {
return this.sane;
}

public String getListenerName() {
return this.lname;
}

public String getPort() {
return this.port;
}

public void die() {
this.alive = false;
}

public boolean isAlive() {
return this.alive;
}

public boolean isActive() {
if (!this.isAlive()) {
return false;
} else {
return !this.isLinked() || this.getLinkState() == 1;
}
}

public String getComputer() {
return this.comp;
}

public boolean isEmpty() {
return this.intz == null || this.intz.length() == 0;
}

public String getUser() {
return this.user;
}

public String getInternal() {
return this.intz;
}

public String getExternal() {
return this.ext;
}

public String getPid() {
return this.isSSH() ? "" : this.pid;
}

public PivotHint getPivotHint() {
return new PivotHint(this.hint);
}

public double getVersion() {
try {
if (this.isSSH() && this.ver.startsWith("CYGWIN_NT-")) {
return Double.parseDouble(CommonUtils.strip(this.ver, "CYGWIN_NT-"));
} else {
return this.isBeacon() ? Double.parseDouble(this.ver) : 0.0D;
}
} catch (Exception var2) {
return 0.0D;
}
}

public String getNote() {
return this.note;
}

public String getParentId() {
return this.pbid;
}

public boolean isLinked() {
return this.pbid.length() > 0;
}

public int getLinkState() {
return this.state;
}

public byte[] getFunctionHint(String var1) {
AssertUtils.TestSetValue(var1, "GetProcAddress, GetModuleHandleA");
if ("GetProcAddress".equals(var1)) {
return this.ptr_gpa;
} else if ("GetModuleHandleA".equals(var1)) {
return this.ptr_gmh;
} else {
throw new RuntimeException("Unknown function hint '" + var1 + "'");
}
}

public String arch() {
return this.barch;
}

public boolean is64() {
if (!this.is64.equals("1") && !this.is64.equals("0")) {
CommonUtils.print_warn("is64 is: '" + this.is64 + "'");
}

return this.is64.equals("1");
}

public boolean isAdmin() {
return this.getUser().endsWith(" *");
}

public void setExternal(String var1) {
if (this.checkExt(var1)) {
this.ext = var1;
} else {
CommonUtils.print_error("Refused to assign: '" + var1 + "' [was: '" + this.ext + "'] as external address to Beacon: '" + this.id + "'");
}

}

public void setLastCheckin(long var1) {
this.last = var1;
}

public void setNote(String var1) {
this.note = var1;
}

public void setAccent(String var1) {
this.accent = var1;
}

public String getAccent() {
return this.accent;
}

public boolean idle(long var1) {
return this.diff >= var1;
}

public String getLastCheckin() {
String var1 = "ms";
long var2 = this.diff;
if (var2 > 1000L) {
var2 /= 1000L;
var1 = "s";
if (var2 > 60L) {
var2 /= 60L;
var1 = "m";
}

if (var2 > 60L) {
var2 /= 60L;
var1 = "h";
}

return var2 + var1;
} else {
return var2 + var1;
}
}

public BeaconEntry(byte[] var1, String var2, String var3, String var4) {
boolean var5;
try {
DataParser var6 = new DataParser(var1);
var6.big();
var6.consume(20);
this.id = Long.toString(CommonUtils.toUnsignedInt(var6.readInt()));
this.pid = Long.toString(CommonUtils.toUnsignedInt(var6.readInt()));
this.port = Integer.toString(CommonUtils.toUnsignedShort(var6.readShort()));
byte var7 = var6.readByte();
if (CommonUtils.Flag(var7, 1)) {
this.barch = "";
this.pid = "";
this.is64 = "";
} else if (CommonUtils.Flag(var7, 2)) {
this.barch = "x64";
} else {
this.barch = "x86";
}

this.is64 = CommonUtils.Flag(var7, 4) ? "1" : "0";
var5 = CommonUtils.Flag(var7, 8);
byte var8 = var6.readByte();
byte var9 = var6.readByte();
this.ver = var8 + "." + var9;
this.build = var6.readShort();
byte[] var10 = var6.readBytes(4);
this.ptr_gmh = var6.readBytes(4);
this.ptr_gpa = var6.readBytes(4);
if ("x64".equals(this.barch)) {
this.ptr_gmh = CommonUtils.join(var10, this.ptr_gmh);
this.ptr_gpa = CommonUtils.join(var10, this.ptr_gpa);
}

this.ptr_gmh = CommonUtils.bswap(this.ptr_gmh);
this.ptr_gpa = CommonUtils.bswap(this.ptr_gpa);
var6.little();
this.intz = AddressList.toIP(CommonUtils.toUnsignedInt(var6.readInt()));
var6.big();
if ("0.0.0.0".equals(this.intz)) {
this.intz = "unknown";
}
} catch (IOException var11) {
MudgeSanity.logException("Could not parse metadata!", var11, false);
this.sane = false;
return;
}

String var12 = CommonUtils.bString(Arrays.copyOfRange(var1, 51, var1.length), var2);
String[] var13 = var12.split("\t");
if (var13.length > 0) {
this.comp = var13[0];
}

if (var13.length > 1) {
this.user = var13[1];
}

if (var13.length > 2) {
if (this.isSSH()) {
this.ver = var13[2];
} else {
this.proc = var13[2];
}
}

if (var5) {
this.user = this.user + " *";
}

this.ext = var3;
this.chst = var2;
this.lname = var4;
this.sane = this.sanity();

this.address = ""; // add address
}

public String getCharset() {
return this.chst;
}

public boolean sanity() {
LinkedList var1 = new LinkedList();

try {
return this._sanity(var1);
} catch (Exception var3) {
this.id = "0";
this.intz = "";
MudgeSanity.logException("Validator blew up!", var3, false);
return false;
}
}

public boolean checkExt(String var1) {
if (var1 == null) {
return true;
} else if ("".equals(var1)) {
return true;
} else {
String var2;
if (var1.endsWith(" ⚯ ⚯") && var1.length() > 5) {
var2 = var1.substring(0, var1.length() - 4);
} else if (var1.endsWith(" ⚯⚯") && var1.length() > 4) {
var2 = var1.substring(0, var1.length() - 3);
} else {
var2 = var1;
}

return CommonUtils.isIP(var2) || CommonUtils.isIPv6(var2) || "unknown".equals(var2);
}
}

public String getProcess() {
return this.proc;
}

public boolean _sanity(LinkedList var1) {
if (!CommonUtils.isNumber(this.id)) {
var1.add("id '" + this.id + "' is not a number");
this.id = "0";
}

if (!"".equals(this.intz) && !CommonUtils.isIP(this.intz) && !CommonUtils.isIPv6(this.intz) && !"unknown".equals(this.intz)) {
var1.add("internal address '" + this.intz + "' is not an address");
this.intz = "";
}

if (!this.checkExt(this.ext)) {
var1.add("external address '" + this.ext + "' is not an address");
this.ext = "";
}

if (!"".equals(this.pid) && !CommonUtils.isNumber(this.pid)) {
var1.add("pid '" + this.pid + "' is not a number");
this.pid = "0";
}

if (!"".equals(this.port) && !CommonUtils.isNumber(this.port)) {
var1.add("port '" + this.port + "' is not a number");
this.port = "";
}

if (!"".equals(this.is64) && !CommonUtils.isNumber(this.is64)) {
var1.add("is64 '" + this.is64 + "' is not a number");
this.is64 = "";
}

if (this.ver != null && this.ver.length() > 64) {
var1.add("ver '" + this.ver + "' is too long. Truncating");
this.ver = this.ver.substring(0, 63);
}

if (this.comp != null && this.comp.length() > 64) {
var1.add("comp '" + this.comp + "' is too long. Truncating");
this.comp = this.comp.substring(0, 63);
}

if (this.user != null && this.user.length() > 64) {
var1.add("user '" + this.user + "' is too long. Truncating");
this.user = this.user.substring(0, 63);
}

if (var1.size() <= 0) {
return true;
} else {
Iterator var2 = var1.iterator();
CommonUtils.print_error("Beacon entry did not validate");

while(var2.hasNext()) {
System.out.println("\t" + CommonUtils.scrub(var2.next() + ""));
}

return false;
}
}

public BeaconEntry(String var1) {
this.id = var1;
this.sane = this.sanity();
}

public void touch() {
this.diff = System.currentTimeMillis() - this.last;
}

public BeaconEntry copy() {
BeaconEntry var1 = new BeaconEntry(this.id);
var1.pid = this.pid;
var1.ver = this.ver;
var1.build = this.build;
var1.intz = this.intz;
var1.comp = this.comp;
var1.user = this.user;
var1.is64 = this.is64;
var1.ext = this.ext;
var1.diff = this.diff;
var1.last = this.last;
var1.state = this.state;
var1.pbid = this.pbid;
var1.note = this.note;
var1.alive = this.alive;
var1.barch = this.barch;
var1.port = this.port;
var1.chst = this.chst;
var1.hint = this.hint;
var1.proc = this.proc;
var1.accent = this.accent;
var1.lname = this.lname;
var1.ptr_gmh = this.ptr_gmh;
var1.ptr_gpa = this.ptr_gpa;
return var1;
}

/* add address */
private static QQWry qqwry;
static {
try {
byte[] data = CommonUtils.readResource("resources/qqwry.dat");
qqwry = new QQWry(data);
} catch (Exception e) {
// Handle the exception...
}
}
public static String getIpAddress(String ipaddress) {
if (ipaddress.length() > 15 || ipaddress.equals("unknown") || ipaddress.equals("")) {
return "未知";
}
try {
// QQWry qqwry = new QQWry(Paths.get("qqwry.dat", new String[0]));

IPZone ipzone = qqwry.findIP(ipaddress);
return ipzone.getMainInfo();
} catch (Exception e) {
return "Exception: " + e.getMessage();
}
}

public Map toMap() {
HashMap var1 = new HashMap();
var1.put("external", this.ext);
var1.put("internal", this.intz);
var1.put("host", this.intz);
var1.put("user", this.user);
var1.put("computer", this.comp);
var1.put("last", this.diff + "");
var1.put("lastf", this.getLastCheckin());
var1.put("id", this.id);
var1.put("pid", this.getPid());
var1.put("is64", this.is64);
var1.put("pbid", this.pbid);
var1.put("note", this.note);
var1.put("barch", this.barch);
var1.put("arch", this.barch);
var1.put("port", this.getPort());
var1.put("charset", this.getCharset());
var1.put("phint", this.hint + "");
var1.put("process", this.proc);
var1.put("_accent", this.accent);
var1.put("listener", this.lname);
var1.put("build", this.build);
var1.put("address", this.getIpAddress(this.ext)); // add address

if (this.alive) {
var1.put("alive", "true");
} else {
var1.put("alive", "false");
}

if (this.state != 0) {
if (this.state == 1) {
var1.put("state", "good");
} else if (this.state == 2) {
var1.put("state", "broken");
}
}

var1.put("os", this.getOperatingSystem());
var1.put("ver", Double.toString(this.getVersion()));
if (this.isSSH()) {
var1.put("session", "ssh");
} else if (this.isBeacon()) {
var1.put("session", "beacon");
} else {
var1.put("session", "unknown");
}

return var1;
}

public boolean wantsMetadata() {
return this.user.length() == 0;
}

public String title() {
return this.isBeacon() ? this.title("Beacon") : "SSH " + this.intz;
}

public String title(String var1) {
return var1 + " " + this.intz + "@" + this.pid;
}

public String toString() {
return this.getId() + " -> " + this.title() + ", " + this.getLastCheckin();
}

public Stack eventArguments() {
Stack var1 = new Stack();
var1.push(SleepUtils.getHashWrapper(this.toMap()));
var1.push(SleepUtils.getScalar(this.id));
return var1;
}

public void link(String var1, int var2) {
this.pbid = var1;
this.state = 1;
this.hint = var2;
}

public void delink() {
this.state = 2;
this.lname = "";
}

public String getBeaconId() {
return this.id;
}

public String getLogFile() {
return this.isSSH() ? "ssh_" + this.id + ".log" : "beacon_" + this.id + ".log";
}

public String getLogFolder() {
return null;
}

public long getLogLimit() {
return 0L;
}

public String getLogEventName() {
return "Beacon Entry";
}

public boolean isBeacon() {
return !this.isSSH();
}

public boolean isSSH() {
return "session".equals(CommonUtils.session(this.id));
}

public String getOperatingSystem() {
if (this.isBeacon()) {
return "Windows";
} else if ("".equals(this.ver)) {
return "Unknown";
} else if ("Darwin".equals(this.ver)) {
return "MacOS X";
} else {
return this.ver.startsWith("CYGWIN_NT-") ? "Windows" : this.ver;
}
}

public void formatEvent(DataOutputStream var1) throws IOException {
var1.writeBytes(CommonUtils.formatLogDate(System.currentTimeMillis()));
var1.writeBytes(" ");
var1.writeBytes("[metadata] ");
if (this.isLinked()) {
var1.writeBytes("beacon_" + this.getParentId() + " -> " + this.getInternal() + "; ");
} else if ("".equals(this.getExternal())) {
var1.writeBytes("unknown <- " + this.getInternal() + "; ");
} else {
var1.writeBytes(this.getExternal() + " <- " + this.getInternal() + "; ");
}

if (this.isSSH()) {
CommonUtils.writeUTF8(var1, "computer: " + this.getComputer() + "; ");
CommonUtils.writeUTF8(var1, "user: " + this.getUser() + "; ");
var1.writeBytes("os: " + this.getOperatingSystem() + "; ");
var1.writeBytes("port: " + this.getPort());
} else {
CommonUtils.writeUTF8(var1, "computer: " + this.getComputer() + "; ");
CommonUtils.writeUTF8(var1, "user: " + this.getUser() + "; ");
var1.writeBytes("process: " + this.getProcess() + "; ");
var1.writeBytes("pid: " + this.getPid() + "; ");
var1.writeBytes("os: " + this.getOperatingSystem() + "; ");
var1.writeBytes("version: " + this.getVersion() + "; ");
var1.writeBytes("build: " + this.getBuild() + "; ");
var1.writeBytes("beacon arch: " + this.barch);
if (this.is64()) {
var1.writeBytes(" (x64)");
}
}

var1.writeBytes("\n");
}
}

到此,CS 4.4 二开基础篇就结束了,文中若存在问题欢迎留言。

Cobalt Strike破解过程
(https://maidang.cool/2021/6634.html)

GitHub - LztCode/cobaltstrike4.5_cdf: cobaltstrike4.5版本破/解、去除checksum8特征、bypass BeaconEye、修复错误路径泄漏stage、增加totp双因子验证、修复CVE-2022-39197等
(https://github.com/LztCode/cobaltstrike4.5_cdf/tree/master)

对cobaltstrike4.4的简单魔改
(https://ucasers.cn/%E5%AF%B9cobaltstrike4.4%E7%9A%84%E7%AE%80%E5%8D%95%E9%AD%94%E6%94%B9/#title-16)

cobalt strike beacon dll 改造实现免杀 - zpchcbd - 博客园
(https://www.cnblogs.com/zpchcbd/p/16880166.html)

geacon_pro 备份

GitHub - 10cks/geacon_pro: 重构了Cobaltstrike Beacon,行为对国内主流杀软免杀,支持4.1以上的版本。A cobaltstrike Beacon bypass anti-virus, supports 4.1+ version.
(https://github.com/10cks/geacon_pro#%E7%9B%AE%E5%89%8D%E9%9C%80%E8%A6%81%E6%94%B9%E8%BF%9B%E7%9A%84%E5%9C%B0%E6%96%B9)

https://archive.org/details/github.com-H4de5-7-geacon_pro_-_2022-11-01_12-02-43

CVE-2022-39197 CS RCE复现分析

(http://www.bmth666.cn/2023/03/22/CVE-2022-39197-CS-RCE%E5%A4%8D%E7%8E%B0%E5%88%86%E6%9E%90/)

看雪ID:bwner

https://bbs.kanxue.com/user-home-951654.htm

*本文为看雪论坛优秀文章,由 bwner 原创,转载请注明来自看雪社区

# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复

球分享

球点赞

球在看


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458523105&idx=2&sn=8301e007cc833536b21aa5690ee39e0b&chksm=b18d256b86faac7de6c68d68d18d4034816ad44889ac2ef4153c0cb6052be33598d6e7c632e6&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh