本文共 6567 字,大约阅读时间需要 21 分钟。
程序员应是创造者,创造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