程序员应是创造者,创造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