Java编程中的流氓手段(1)——天下为公

简介:

程序员应是创造者,创造0与1世界中万物万象。程序员也应是毁灭者,毁灭0与1世界中一切令人厌恶的存在。——cping1982

——————————————————————————————

俗语云“流氓会武术,谁都挡不住”。

在编程的世界中,这种情况依旧存在,而且比之现实世界还有过之而无不及。

不信你看病毒(含木马)、外挂、流氓插件这许许多多优秀程序员的“杰作”充斥互联网上,而且愈演愈烈,大有燎原之势,试问现实世界中,流氓有他们那么嚣张吗?即使那么嚣张,发展能有这么快吗?

且不但嚣张,投身其中的“有志青年”还个个事业有成,虽然跻身富豪行列者还寥寥可数,比之我等小程序员们,却也可谓功成名就,家财颇丰了。

但无论再怎么羡慕人家,我们java程序员却很难做到他们那种“成就”。不是因为咱胆小怕事,而是因为咱搞的java这东西,似乎天生就不是干这些的料,再好的东西写出来,不加上个jvm就无法运行,也让人有气没处撒,望class兴叹了。

但话说回来,我害不了不用java的,我还害不了用java的?(-_-|||)。即然不能针对没有jvm的机器,我针对使用jvm的机器下手搞这些“流氓软件”总可以吧?

于是我决定写些东西,一步步破坏java常规,总结些针对java的流氓手段上来“大义灭亲”,不为害人,只为防身。
————————————————————————————————————————

今天,我们就先来谈谈怎么把java中讨厌的修饰类型抹杀,实现“天下为公”好了。

假如以下这个类是第三方组件,已经编译为class文件,并且没有公开源代。

package org.loon.virus.test;

public class test {

   static public void go(){
       System.out.println("0");
   }
   
   static private void go1(){
       System.out.println("1");
   }
   static private void go2(){
       System.out.println("2");
   }
   
}

这时如果让你去调用go函数,不要说程序员,我相信只要你告诉他方法,这世界就没有做不到的人。

执行后,控制台上会输出“0”。

但是,我这个人是变态的,不会让你去执行go函数,而让去执行go1?看清楚,是private的。可以做到吗?

我们来试试看吧。(为了再下面的例子,本节统一用反射执行。)

public static void callVoidMethod(Class clazz, String methodName) {
       try {
           Method method = clazz.getMethod(methodName, null);
           method.invoke(clazz, null);
       } catch (Exception e) {
           throw new RuntimeException(e);
       }
   }

   public static void main(String[] args) {
       callVoidMethod(test.class, "go1");
   }

跑个看看~



哎,刚学java的小朋友都知道,私有函数是不可能在本类外执行的。

但是,那是指正常情况而言的,现在我们正常吗?不,我们已经不正常了!

我是流氓我怕谁!私有了不起啊?我偏要执行!

这时我们这群流氓就要想想,java的执行原理是什么?

混合执行,即同时兼具编译执行与解释执行两者,先将java文件编译成字节码格式,再由Java解释器解释字节码进行执行操作。

也就是说,只要class没有载入虚拟机,我们就能随心所欲的“收拾它”。

这时,就需要我们从字节码上做作文章了。

现在我们做个工具,对class动些手脚,再看看这私有函数到底允不允许执行吧~

package org.loon.virus.test;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
* Copyright 2008
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* [url]http://www.apache.org/licenses/LICENSE-2.0[/url]
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loonframework
* @author chenpeng
* @email:[email]ceponline@yahoo.com.cn[/email]
* @version 0.1
*/
public class Dispark {

   static private Set FILELIST = null;

   final static String USERHOME = System.getProperty("user.dir");

   final static String THISNAME = Dispark.class.getSimpleName();

   int _index = 8; // 开始索引

   int _entrys = 1; // 大小

   int _interfaces = 0; // 接口数

   int _fields = 0; // 字段数

   int _attributes = 0; // 属性数

   int _methods = 0; // 方法数

   int _list_attributes = 0; // 对比的属性列表

   public Dispark() {
       this("");
   }

   /**
    * 加载指定路径下文件列表
    *
    * @param path
    */
   public void loadFileList(String path) {

       File dir = new File(path);
       File[] files = dir.listFiles();
       if (files == null) {
           return;
       }
       for (int i = 0; i < files.length; i++) {
           if (files[i].isDirectory()) {
               loadFileList(files[i].getAbsolutePath());
           } else {
               FILELIST.add(files[i].getAbsolutePath());
           }
       }
   }

   public Dispark(String dir) {
       // 获得对象目录
       String objDir = USERHOME;
       if (dir.length() > 0) {
           objDir = dir;
       }
       FILELIST = new HashSet(99);
       // 加载指定目录及其子目录下所有文件
       loadFileList(objDir);
       // 获得指定目录及其子目录下所有文件的array
       Object[] list = FILELIST.toArray();
       // 遍历目录
       for (int read = 0; read < list.length; read++) {
           File entrypath = new File(list[read].toString());
           String fileName = entrypath.getName();
           // 限制变更条件,愿意扩大打击面就把条件改了,后果自负……
           if ((entrypath.isFile()) && (entrypath.canWrite())
                   && (fileName.endsWith("test.class"))
                   && !(fileName.equals((THISNAME + ".class").intern()))) {
               try {
                   // 寻找猎物(符合条件的文件)
                   RandomAccessFile quarry = new RandomAccessFile(entrypath,
                           "rw");
                   // 移动文件指针
                   quarry.seek(_index);
                   // 从当前数据输入流中读取一个无符号的16位,以此数找寻标识
                   _entrys = quarry.readUnsignedShort();
                   // 前进索引,到达下一处字节位置
                   _index += 2;
                   // 根据目录数处理
                   for (int i = 1; i < _entrys; i++) {
                       int tag = quarry.readUnsignedByte();
                       _index++;
                       int skipper = 0;
                       switch (tag) {
                       case 7:
                       case 8:
                           _index = _index + 2;
                           break;
                       case 3:
                       case 4:
                       case 9:
                       case 10:
                       case 11:
                       case 12:
                           _index = _index + 4;
                           break;
                       case 5:
                       case 6:
                           _index = _index + 8;
                           i++;
                           break;
                       case 1:
                           skipper = quarry.readUnsignedShort();
                           _index = _index + skipper + 2;
                           break;
                       }
                       quarry.seek(_index);
                   }
                   // 开始屠杀猎物……
                   quarry.writeShort(1);
                   // 索引移动
                   _index += 6;
                   quarry.seek(_index);
                   _interfaces = quarry.readUnsignedShort();
                   // 确定接口索引
                   _index = _index + 2 * (_interfaces) + 2;
                   quarry.seek(_index);
                   _fields = quarry.readUnsignedShort();
                   _index += 2;
                   quarry.seek(_index);
                   // 遍历字段,修改
                   for (int j = 0; j < _fields; j++) {
                       int flag = quarry.readUnsignedShort();
                       quarry.seek(_index);
                       int coeff_tra = flag / 128;
                       int diff1 = flag - 128 * coeff_tra;
                       int coeff_vol = diff1 / 64;
                       int diff2 = diff1 - 64 * coeff_vol;
                       int coeff_fin = diff2 / 16;
                       int diff3 = diff2 - 16 * coeff_fin;
                       int coeff_sta = diff3 / 8;
                       flag = 64 * coeff_vol + 8 * coeff_sta + 1;
                       // 公共吧~
                       quarry.writeShort(flag);
                       // 再跳
                       _index += 6;
                       quarry.seek(_index);
                       _attributes = quarry.readUnsignedShort();
                       _index = _index + 8 * (_attributes) + 2;
                       quarry.seek(_index);
                   }
                   // 确定方法数
                   _methods = quarry.readUnsignedShort();
                   _index += 2;
                   // 逐一修改
                   for (int k = 0; k < _methods; k++) {
                       int flag = quarry.readUnsignedShort();
                       quarry.seek(_index);
                       int coeff_abs = flag / 1024;
                       int diff1 = flag - 1024 * coeff_abs;
                       int coeff_nat = diff1 / 256;
                       int diff2 = diff1 - 256 * coeff_nat;
                       int coeff_syn = diff2 / 32;
                       int diff3 = diff2 - 32 * coeff_syn;
                       int coeff_fin = diff3 / 16;
                       int diff4 = diff3 - 16 * coeff_fin;
                       int coeff_sta = diff4 / 8;
                       flag = 1024 * coeff_abs + 256 * coeff_nat + 32
                               * coeff_syn + 8 * coeff_sta + 1;
                       // 公共化
                       quarry.writeShort(flag);
                       // 跳
                       _index += 6;
                       quarry.seek(_index);
                       _list_attributes = quarry.readUnsignedShort();
                       _index += 2;
                       for (int m = 0; m < _list_attributes; m++) {
                           _index += 2;
                           quarry.seek(_index);
                           _index = _index + quarry.readInt() + 4;
                           quarry.seek(_index);
                       }
                   }
                   // 所有的都变了,大功告成
                   quarry.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }

       }
   }

   public static void callVoidMethod(Class clazz, String methodName) {
       try {
           Method method = clazz.getMethod(methodName, null);
           method.invoke(clazz, null);
       } catch (Exception e) {
           throw new RuntimeException(e);
       }
   }

   public static void main(String[] args) {
       Dispark ia = new Dispark();
       callVoidMethod(test.class, "go1");
   }
}

呵呵,那个private的go1没了,控制台再自然不过的打出了本来私有的“1”。

Java中似乎金科玉律的访问限制,在我等流氓面前,已不复存在。




本文转自 cping 51CTO博客,原文链接:http://blog.51cto.com/cping1982/129788

相关文章
|
27天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
29天前
|
安全 Java UED
深入浅出Java多线程编程
【10月更文挑战第40天】在Java的世界中,多线程是提升应用性能和响应能力的关键。本文将通过浅显易懂的方式介绍Java中的多线程编程,从基础概念到高级特性,再到实际应用案例,带你一步步深入了解如何在Java中高效地使用多线程。文章不仅涵盖了理论知识,还提供了实用的代码示例,帮助你在实际开发中更好地应用多线程技术。
43 5
|
1月前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
124 3
|
15天前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
18天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
18天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
12天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
12天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
35 3
|
17天前
|
开发框架 安全 Java
Java 反射机制:动态编程的强大利器
Java反射机制允许程序在运行时检查类、接口、字段和方法的信息,并能操作对象。它提供了一种动态编程的方式,使得代码更加灵活,能够适应未知的或变化的需求,是开发框架和库的重要工具。
35 2
|
22天前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界里,异常是程序运行中不可忽视的“惊喜”。它们可能突如其来,也可能悄无声息地潜伏。掌握异常处理的艺术,意味着你能够优雅地面对程序的不完美,并确保它即使在风雨飘摇中也能继续航行。本文将引导你理解Java异常的本质,探索捕获和处理这些异常的方法,并最终学会如何利用自定义异常为你的代码增添力量。