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中似乎金科玉律的访问限制,在我等流氓面前,已不复存在。


下载地址:[url]http://download.csdn.net/source/486830[/url]


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

相关文章
|
14天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
1天前
|
存储 安全 Java
Java并发编程中的高效数据结构:ConcurrentHashMap解析
【4月更文挑战第25天】在多线程环境下,高效的数据访问和管理是至关重要的。Java提供了多种并发集合来处理这种情境,其中ConcurrentHashMap是最广泛使用的一个。本文将深入分析ConcurrentHashMap的内部工作原理、性能特点以及它如何在保证线程安全的同时提供高并发性,最后将展示其在实际开发中的应用示例。
|
2天前
|
Java API 调度
[Java并发基础]多进程编程
[Java并发基础]多进程编程
|
2天前
|
Java API 调度
[AIGC] 深入理解Java并发编程:从入门到进阶
[AIGC] 深入理解Java并发编程:从入门到进阶
|
2天前
|
前端开发 Java 测试技术
Java从入门到精通:4.1.1参与实际项目,锻炼编程与问题解决能力
Java从入门到精通:4.1.1参与实际项目,锻炼编程与问题解决能力
|
2天前
|
SQL Java 数据库连接
Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
|
2天前
|
SQL Java 数据库连接
Java从入门到精通:2.3.1数据库编程——学习JDBC技术,掌握Java与数据库的交互
ava从入门到精通:2.3.1数据库编程——学习JDBC技术,掌握Java与数据库的交互
|
2天前
|
IDE Java 开发工具
Java从入门到精通:1.3.1实践编程巩固基础知识
Java从入门到精通:1.3.1实践编程巩固基础知识
|
7天前
|
IDE Java 物联网
《Java 简易速速上手小册》第1章:Java 编程基础(2024 最新版)
《Java 简易速速上手小册》第1章:Java 编程基础(2024 最新版)
13 0
|
7天前
|
安全 Java 开发者
Java并发编程:深入理解Synchronized关键字
【4月更文挑战第19天】 在Java多线程编程中,为了确保数据的一致性和线程安全,我们经常需要使用到同步机制。其中,`synchronized`关键字是最为常见的一种方式,它能够保证在同一时刻只有一个线程可以访问某个对象的特定代码段。本文将深入探讨`synchronized`关键字的原理、用法以及性能影响,并通过具体示例来展示如何在Java程序中有效地应用这一技术。