黑客攻击受保护的基于Java的程序

本文提供了一些有帮助的黑客技术示例Java 语言(一种计算机语言,尤用于创建网站)开发商避免脆弱点在他们的程序中。它不是为了训练黑客,而是为天真的开发人员设计的,他们认为标准混淆器将使他们免于知识产权被盗。

文本中会有逻辑空白,但这只是为了简洁。任何有经验的开发人员或黑客都可以轻松完成它们。

所有代码示例均取自真实应用程序。

案例1:认证服务器
这客户端-服务器早在互联网出现之前,model就出现了,至今仍在各地使用。与此同时,出现了一个神话,即没有客户端-服务器部分。然而,下面我们将表明这只是一个神话。

该图显示了服务器-客户端数据流如下。为了清楚起见,程序的片段不在这里。所谓的临界点被圈起来了。这是什么将从以下内容中变得清楚。
在这里插入图片描述
关于认证服务器可靠性的神话相当普遍,因此我们将从它开始。我们有一个服务器,它接收一个程序的请求,并在检查后发送一个响应:该程序将继续工作,还是需要付费注册?让我们简单地通过搜索用于局域网或互联网的类(即包中的类)来找到获得请求的点java.net.*例如,HttpURLConnection,以及类似的。

让我们从我们的角度,即开发人员的角度,然后从黑客的角度来看这张图。这个模型中使用的应用程序有两个重要的点:一个是发送请求的点,另一个是从服务器接收响应的点。

用于执行的原始Java方法的片段请求-响应动作:

	boolean authenticate(String requestURL, String params) {
		URL url = null;
		HttpURLConnection conn = null;
		try {
			url = new URL(requestURL);
			conn = (HttpURLConnection) url.openConnection();
			conn.connect();
		} catch (IOException e) {
			showError("Failed connect to " + requestURL + ".");
			return false; // Warning
		}

		String response = "";

		if (conn != null) {
			try (OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream())) {
				writer.write(params); // Warning
				writer.flush(); // Warning
				writer.close();
			} catch (IOException e) {
				showError("Failed write params " + params + ".");
				return false; // Warning
			}

			try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                                                                      conn.getInputStream()))) {
				String line = "";

				while ((line = reader.readLine()) != null) {
					response += line + "\n";
				}

				reader.close();
			} catch (IOException e) {
				showError("Failed read " + response + ".");
				return false; // Warning
			}

			if (conn != null)
				conn.disconnect();
		}

		return response.indexOf("activated") > -1; // Error
	}

易受攻击的命令后跟注释。

之后相同的Java方法困惑一名黑客认为:

     static boolean Z(String a, String aa) {
          String var2 = a;
          HttpURLConnection var4 = null;
          try {
               (var4 = (HttpURLConnection)(new URL(var2)).openConnection()).connect();
          } catch (IOException var36) {
               return true; /* false; */
          }
          String var6 = "";
          if (var4 != null) {
               Object var5;
               Throwable var10000;
               try {
                    a = null;
                    var5 = null;
                    try {
                         OutputStreamWriter var3 = new OutputStreamWriter(var4.getOutputStream());
                         try {
//                              var3.write(a);
//                              var3.flush();
                              var3.close();
                         } finally {
                              if (var3 != null) {
                                   var3.close();
                              }
                         }
                    } catch (Throwable var38) {
                         if (a == null) {
                              var10000 = var38;
                         } else {
                              if (a != var38) {
                                   a.addSuppressed(var38);
                              }
                              var10000 = a;
                         }
                         // throw var10000;
                    }
               } catch (IOException var39) {
                    z("Failed connect to " + a + ".");
                    return true; /* false; */
               }
               HttpURLConnection var45;
               label534: {
                    try {
                         a = null;
                         var5 = null;
                         try {
                              BufferedReader var43 = new BufferedReader(new InputStreamReader(
                                                                        var4.getInputStream()));
                              boolean var21 = false;
                              try {
                                   var21 = true;
                                   a = "";
                                   BufferedReader var44 = var43;
                                   while(true) {
                                        if ((a = var44.readLine()) == null) {
                                             var43.close();
                                             var21 = false;
                                             break;
                                        }
                                        var6 = var6 + a + "\n";
                                        var44 = var43;
                                   }
                              } finally {
                                   if (var21) {
                                        if (var43 != null) {
                                             var43.close();
                                        }
                                   }
                              }
                              if (var43 != null) {
                                   var45 = var4;
                                   var43.close();
                                   break label534;
                              }
                         } catch (Throwable var41) {
                              if (a == null) {
                                   var10000 = var41;
                              } else {
                                   if (a != var41) {
                                        a.addSuppressed(var41);
                                   }
                                   var10000 = a;
                              }
                              // throw var10000;
                         }
                    } catch (IOException var42) {
                         z("Failed to read " + var2 + ".");
                         return true; /* false; */
                    }
                    var45 = var4;
               }
               if (var45 != null) {
                    var4.disconnect();
               }
          }
          return true; /* var6.indexOf(z("0'%-'%%!5")) > -1; */
	}

很容易看出,通过替换97号线以及之前的所有线return…s与return true,注释行19和20我们得到了我们程序的免费版本。请注意,服务器试图确定当前谁在申请身份验证以及有多少这样的请求将会失败(这是几个人的许可证的情况)。

在模糊代码中,找到一个方法的输出可能要困难得多,但是使用pathfinder方法,或者更好地像狗找到猎物一样,它迟早会被找到。黑客从HttpURLConnection或类似的类变量,并遵循通向临界点的线索:var4 -> var4 -> … var3 -> var3 … -> var4 -> return …混淆器引入的其余垃圾可以忽略。在这种情况下,就像在许多其他情况下一样,您的程序的安全性仅取决于黑客的独创性,而不是您的努力。我们将在下一部分讨论这个问题。

案例2:付费游戏
下面是P玩吧显示相应组件和数据流的图表。
在这里插入图片描述
这种模式没有试用。首先,你付钱,然后你得到它。黑客没什么好黑的。

以下句子已经成为开发人员的通用规则:“只要有足够的时间和精力,几乎任何代码都可以进行逆向工程。混淆器会使逆向工程更加困难,在经济上也不切实际。”但这是一个错误的观点。有一种所谓的“偷过一次卖了很多”进攻。黑客入侵程序不是为了自己,而是为了出售它。例如,你仍然可以在网上买到MS Office、Windows 7或Windows 10以及许多其他软件的非法版本,而且价格几乎为零。

因此,黑客需要购买程序并执行前面针对客户端-服务器方案:替换接受激活密钥随着return true 并删除支付指令中的指令。

为了抵御这种最简单的攻击,开发人员使用任何Java加密算法用从服务器(激活密钥).加密部分包括密钥证明,如果成功则启动主程序。下面是代码片段,包括解密和加载类的代码。

此处以及下一部分都使用了该代码,Authenticator.

public class Authenticator {
    public Authenticator(byte[] key, long ... l) {
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        IvParameterSpec iv = new IvParameterSpec(key);
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
            CipherInputStream cis = new CipherInputStream(getClass()
                            .getResourceAsStream("anyfolder/anyname"), cipher);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            copyStream(cis, baos);
            byte[] bytes = baos.toByteArray();
            Class<?> klass = new ClassLoader() {
                Class<?> define(byte[] bytes, int offset, int length) {
                    Class<?> klass = super.defineClass(null, bytes, 0, length);
                    return klass;
                }
            }.define(bytes, 0, bytes.length);
            klass.getConstructor().newInstance();
        } catch (Throwable t) {
            System.exit(1);
        }
    }
}

还有,Launcher,应该使用相同的加密激活密钥并放入任何资源文件夹中。

public class Launcher {
    args = Preloader.args;
    public Launcher() {
        Application.run(args);
    }
}

附在哪里Preloader设置的类args价值观和激活密钥接收自计算机网络服务器付款确认后。

public class Preloader {
    private static byte[] key;
    public static String[] args;

    public static void main(String[] args) {
        receiveConfirmation();
        encryptClass(); 
        new Authenticator(key);
    }
    
    private static void receiveConfirmation() {
        String confirm = responce();
        String[] parts = confirm.split(":");
        key = hexToBytes(parts[0]);
    }

    private static void encryptClass() {
        IvParameterSpec iv = new IvParameterSpec(key);
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            InputStream is = Preloader.class.getResourceAsStream("/Launcher.class");
            CipherInputStream cis = new CipherInputStream(is, cipher);
            String file = "C:\\Workspaces\\anyfolder\\anyname";
            File targetFile = new File(file);
            OutputStream outStream = new FileOutputStream(targetFile);
            copyStream(cis, outStream);
            cis.close();
            outStream.close();
        } catch (Throwable t) {
            System.exit(1);
        }
    }

    private static byte[] hexToBytes(String hex) {
        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            try {
                bytes[i] = (byte) (0xff & Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                return null;
            }
        }
        return bytes;
    }

    private static void copyStream(InputStream in, OutputStream out) {
        byte[] buffer = new byte[4096];
        int read;
        try {
            while ((read = in.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        } catch (IOException e) {
            System.exit(1);
        }
    }

    private static void sendPaymentOrder(float sum) {
        ...
    }

    private static String responce() {
        ...    
    }
}

这种保护方法的优势显而易见:

缺少可以修改的逻辑表达式和变量
pathfinder跟踪在Authenticator类处结束,该类不能被欺骗或绕过
主程序没有入口点,甚至没有它的名字
我们暂且不谈版权保护的问题。

重命名变量、文件和类不会影响安全级别,但可能会给人留下印象。

案例三:定时炸弹
此图显示了的部件和数据流定时炸弹模型。
在这里插入图片描述
该模式适用于限时试用和订阅。

这里逆时针计时器充当服务器。它位于程序内部,因为将它放在服务器上将使我们返回到之前的方案。该请求是“还剩多少时间“答案是time > 0 ? run : exit. 这里的关键点是计数器本身和计数器的输出,如下所示:请求-》呼叫计数器-》响应-》开关过期/否-》。

静态字段开始和周期被添加到Launcher类和构造函数做了一点修改。

public class Launcher {
    private static long start;
    private static long period;
    private static String[] args;
    
    public class Application {
        public static void main(String[] args) {
            ...
        }
    }

    static {
        args = Preloader.args;
        start = Preloader.start;
        period = Preloader.period;
    }

    public Launcher() {
        if (System.currentTimeMillis() - start < period) {
            Application.main(args);
        } else {
            System.exit(1);
        }
    }
}

不像案例1黑客无法找到发射者使用与时间和要更改的行相关的Java关键字(方法)初始化。

因此,这个类必须对黑客隐藏。今天,加密是最合适的手段。我们执行以下操作。首先,我们加密Launcher.class字节,其次,我们将它们移动到一个文件夹中/anyfolder,将该类重命名为anyname,然后使用与 Authenticator班级,就像案例二。

public class Authenticator {
    public Authenticator(byte[] key, long ... l) {
        ...
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            ...
            klass.getConstructor().newInstance();
        } catch (Throwable t) {
            System.exit(1);
        }
    }
}

Preloader和Launcher类是对支付和游戏模型类;增加了工作开始和时间的参数,start和period,以及sum.

public class Preloader {
    private static float sum = 1000000.00f; // added for Case 3
    private static byte[] key;
    public static String[] args;
    public static long start; // added for Case 3
    public static long period; // added for Case 3

    public static void main(String[] args) {
        sendPaymentOrder(sum); // added for Case 3
        receiveConfirmation();
        encryptClass(); 
        new Authenticator(key, start, period);
    }
    
    private static void receiveConfirmation() {
        String confirm = responce();
        String[] parts = confirm.split(":");
        key = hexToBytes(parts[0]);
        start = Long.parseLong(parts[1]); // added for Case 3
        period = Long.parseLong(parts[2]); // added for Case 3
    }

    private static void encryptClass() {
        IvParameterSpec iv = new IvParameterSpec(key);
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            InputStream is = Preloader.class.getResourceAsStream("/Launcher.class");
            CipherInputStream cis = new CipherInputStream(is, cipher);
            String file = "C:\\Workspaces\\anyfolder\\anyname";
            File targetFile = new File(file);
            OutputStream outStream = new FileOutputStream(targetFile);
            copyStream(cis, outStream);
            cis.close();
            outStream.close();
        } catch (Throwable t) {
            System.exit(1);
        }
    }

    private static byte[] hexToBytes(String hex) {
        ...
        return bytes;
    }

    private static void copyStream(InputStream in, OutputStream out) {
        ...
    }

    private static void sendPaymentOrder(float sum) {
        ...
    }
}

但所有试图隐藏激活密钥密钥和类都是在JVM内存中解密的,这一事实使加密受挫。黑客可以使用内存转储来获取他需要的数据。首先,它使用Java工具API。以下代码:

public class DumperAgent implements ClassFileTransformer {
    public static void premain(String args, Instrumentation instr) {
        agentmain(args, instr);
    }

    public static void agentmain(String agentArgs, Instrumentation instr) {
        instr.addTransformer(new DumperAgent(), true);
        Class<?>[] classes = instr.getAllLoadedClasses();
        try {
            instr.retransformClasses(classes);
        }
        catch (UnmodifiableClassException e) {}
    }

    public byte[] transform(ClassLoader loader, String className,
            Class<?> redefinedClass, ProtectionDomain protDomain, byte[] classBytes) {
        dumpClass(className, classBytes);
        return null;
    }

    private static void dumpClass(String className, byte[] classBytes) {
        try {
            className = className.replace("/", File.separator);
            // ...
            FileOutputStream fos = new FileOutputStream(fileName);
            fos.write(classBytes);
            fos.close();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

和Attacher:

public class Attacher {
    private static String pathToAttacherJar, pid;

    public static void main(String[] args) throws Exception {
        VirtualMachine vm = VirtualMachine.attach(pid);
        vm.loadAgent(pathToAttacherJar, null);
    }
}

不幸的是,这一尝试也没有成功。对Java运行时参数的简单分析发现了不需要的参数,程序在初始化时停止工作,没有时间加载任何东西。

    static {
        RuntimeMXBean mxBean = ManagementFactory.getRuntimeMXBean();
        if (mxBean.getInputArguments().contains("-XX:-DisableAttachMechanism") ||
                            mxBean.getInputArguments().contains("-javaagent:")) {
            System.exit(1);
        }
    }

这个片段应该放在第一个类中,该类通常包含main(String[] args)方法。

你能做什么?
你需要做的第一件事是学会从黑客的角度看问题。这是识别你的程序所属的架构,寻找现有的漏洞,以及你的程序中可能存在的漏洞和如何破解它们。

请记住,您总是可以找到免受任何攻击的保护。还记得有一个偷一次,卖很多攻击,但也有一个“保护得好,保护得久“防守。冒充破解软件“分销商”的黑客比你想象的要多。

不要仅仅依赖混淆器、服务器或加密技术。仅保护程序的关键点和部分。使用程序本身的外部结构。安全性应该像程序本身一样设计良好。

编后记
代码保护是一个过程,而不是最终结果。新的黑客方法正在被发明,新版本的虚拟机(Java Virtual Machine的缩写)允许对JVM内存进行更多的操作等。这是一场病毒与反病毒.
换句话说,绝对的武器和绝对的保护都不存在,也不可能存在。任何新的攻击方法都会产生相应的防御方法来抵御这种攻击。
几乎在安全算法开发的每个案例中都应该使用类似于量子物理中的观察者效应的入侵检测过程。
任何观察(干预)、重置、远程代理等。会对观察到的环境造成干扰,可以注意到这一点并采取保护措施。
因此,我们有一个攻防体系演变的经典例子。这意味着每一次进攻行动都有一次防守,反之亦然。注意防守总是在最佳位置。

黑客&网络安全如何学习

今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。

网络安全学习资源分享:

给大家分享我自己学习的一份全套的网络安全学习资料,希望对想学习 网络安全的小伙伴们有帮助!

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

【点击免费领取】CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》

1.学习路线图

在这里插入图片描述

攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去接私活完全没有问题。

2.视频教程

网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。【点击领取视频教程】

在这里插入图片描述

技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本【点击领取技术文档】

在这里插入图片描述

(都打包成一块的了,不能一一展开,总共300多集)

3.技术文档和电子书

技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本【点击领取书籍】

在这里插入图片描述

4.工具包、面试题和源码

“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。

在这里插入图片描述

最后就是我这几年整理的网安方面的面试题,如果你是要找网安方面的工作,它们绝对能帮你大忙。

这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。

参考解析:深信服官网、奇安信官网、Freebuf、csdn等

内容特点:条理清晰,含图像化表示更加易懂。

内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…

在这里插入图片描述

👋全套《黑客&网络安全入门&进阶学习资源包》👇👇👇

这份完整版的学习资料已经上传CSDN,也可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值