热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

我是怎样使用javassist将代码注入到帝国OL并进行调试的

帝国OL是拉阔一款手机网络游戏(腾讯也有代理),我在中学时代玩儿过。帝国OL还维护着KJava版本游戏客户端,这意味着我们可以在PC端使用模拟器玩儿游戏。不过这篇文章我主要是关注如何通过代

  帝国OL是拉阔一款手机网络游戏(腾讯也有代理),我在中学时代玩儿过。

  帝国OL还维护着KJava版本游戏客户端,这意味着我们可以在PC端使用模拟器玩儿游戏。

  不过这篇文章我主要是关注如何通过代码注入拦截其客户端代码调用并测试其方法内容的。

 

  声明:本人并没有任何对于帝国OL游戏代码的逆向工程、改编、分发或从中获利的行为,本篇文章的执行数据和工作流程及所有言论和工作都是为了学习之用,如果文章中的内容侵犯了任何个人或集体的利益,请联系我关闭本篇文章。在您观看此篇文章时则视为您已经默认了此文章为学习性质,否则请勿继续向下观看。

 

  帝国OL游戏客户端代码是混淆加密过的,在最新版客户端中(截至时间2013-11-09,客户端适用手机型号N5800)共有一个启动类和149个方法提供类,和一般混淆结果一样,我们如果阅读class文件或通过反编译工具查看会发现它类的类名、方法名和全局变量名都是诸如a/aa/ba/bc等无意义、无规律的名称,而且加密过后的代码是无法从反编译结果直接再次编译的。

  那我们还能对游戏运行流程进行调试吗?比如监控游戏方法调用?

  答案是肯定的。我们可以使用Java的一个第三方类库,专门用于对class文件进行操作。  

  

  我先将所需的工具和jar包发上来,大家可以下载或搜索下载。

  Javassist:对Java字节码文件进行操作的类库,看起来和Java自己的reflection API很像,不过在对class文件进行操作时功能更加强大。

  Kemulator:这个东西大家肯定不陌生,最常用的就是在电脑上玩儿手机游戏。我推荐0.9.4版本,比较稳定。

  jd-gui:Java字节码文件反编译工具,使用很方便。不过对于双重循环有时翻译不出来……我们主要用于查看一点信息

 

  好,我们现在理一下思路:Javassist有一个功能,就是在某个方法执行之前或之后插入代码,而且还能拿到此次方法调用的所有参数类型和值。

  (注:此方法必须有方法体,且不是private的,当然,本身private方法我们也可以变成public,但为了保留原来代码的完整性,我在这次操作里没有改动)

 

  那我们可以这样:向游戏函数中每个方法中插入一条语句,这条语句很简单,就是为了调用我们的某个函数,并把参数传递过来,我们进行操作。

  就像这样:

public static void beCall(String methodName, Object[] params) {
}

  这个beCall函数就是要插入到游戏原来代码方法中的内容。beCall函数接收两个参数,第一个我定义为方法的签名,包括方法名和参数类型,第二个参数是方法被调用时传递的参数。

  下面是我注入后的代码:

  

  从上图的情况来看,我们对class的操作是成功的,我们只需要把ViewMethodCall拷入帝国OL客户端jar包即可。

  (ViewMethodCall即我上文的类,beCall是公开的静态函数)

 

  由于此篇文章涉及到某些政策问题,我就不将详细步骤贴出来了。

  下面是我最后实现的功能:

  

  我可以用左侧的窗体进行调试,调试主要在beCall函数接收到的参数值中寻找,比如下图我就是在寻找当游戏角色坐标改变时游戏内部的方法调用过程:

  

  然后我立即移动至24,当移动过程完成立即点击“停止调试”,因为在调试过程中的计算是十分占用计算机运算效率和内存的:

  

  由于政策因素(-_- 如果我被查水表大家为我默哀),我只贴出两个关键类的代码,我对于class文件操作的代码请联系我获取:

  1 package form;
2
3 import java.awt.Dimension;
4 import java.awt.Toolkit;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.util.Hashtable;
8 import java.util.List;
9
10 import javax.swing.JButton;
11 import javax.swing.JComboBox;
12 import javax.swing.JDialog;
13 import javax.swing.JLabel;
14 import javax.swing.JOptionPane;
15 import javax.swing.JScrollPane;
16 import javax.swing.JTextArea;
17 import javax.swing.JTextField;
18 import javax.swing.UIManager;
19
20 import test.ViewMethodCall;
21
22 /**
23 *
24 * @author RyanShaw
25 */
26 public class FrmMain extends JDialog implements ActionListener {
27
28 private static final long serialVersiOnUID= -8049035809432056277L;
29
30 private boolean debug = false;
31
32
33 /**
34 * 调试寻找数据类型提示
35 */
36 private JLabel lblFindType;
37 /**
38 * 调试寻找的数据
39 */
40 private JTextField txtFindValue;
41 /**
42 * 调试寻找数据提示
43 */
44 private JLabel lblFindValue;
45 /**
46 * 调试寻找的数据类型
47 */
48 private JComboBox comFindType;
49
50 /**
51 * 调试按钮
52 */
53 private JButton btnDbg;
54
55 /**
56 * 出现寻找数据的方法调用
57 */
58 private JTextArea txtMethodCalls;
59 /**
60 * 数据方法调用滚动支持
61 */
62 private JScrollPane spMethodCalls;
63 /**
64 * 整个调试流程里方法调用的顺序
65 */
66 private JTextArea txtMethodTrace;
67 /**
68 * 方法调用流程滚动支持
69 */
70 private JScrollPane spMethodTrace;
71
72 public FrmMain() {
73 setTitle("帝国OL注入式调试工具");
74 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
75 setSize(640,360);
76 setResizable(false);
77 setLayout(null);
78
79 // 设置窗体居中
80 Toolkit kit = Toolkit.getDefaultToolkit();
81 Dimension screenSize = kit.getScreenSize();
82 int screenWidth = screenSize.width;
83 int screenHeight = screenSize.height;
84 int windowWidth = this.getWidth();
85 int windowHeight = this.getHeight();
86 setLocation(screenWidth / 2 - windowWidth / 2, screenHeight / 2
87 - windowHeight / 2);
88
89 initComponents();
90 }
91
92 private void initComponents() {
93 lblFindType = new JLabel("数据类型:");
94 lblFindType.setSize(80,24);
95 lblFindType.setLocation(28, 18);
96
97 comFindType = new JComboBox(new String[]{"数字","字串"});
98 comFindType.setSize(80, 24);
99 comFindType.setLocation(100, 18);
100
101 lblFindValue = new JLabel("寻找数值:");
102 lblFindValue.setSize(80, 24);
103 lblFindValue.setLocation(200, 18);
104
105 txtFindValue = new JTextField();
106 txtFindValue.setSize(145, 24);
107 txtFindValue.setLocation(260, 18);
108
109 btnDbg = new JButton("启动调试");
110 btnDbg.setSize(80,24);
111 btnDbg.setLocation(28, 48);
112 btnDbg.addActionListener(this);
113
114 txtMethodCalls = new JTextArea();
115 txtMethodCalls.setSize(568, 100);
116 txtMethodCalls.setLocation(28, 80);
117 //txtMethodCalls.setEditable(false);
118 //txtMethodCalls.setLineWrap(true);
119 //txtMethodCalls.setWrapStyleWord(true);
120
121 spMethodCalls = new JScrollPane();
122 spMethodCalls.setViewportView(txtMethodCalls);
123 spMethodCalls.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
124 spMethodCalls.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
125 spMethodCalls.setSize(568, 100);
126 spMethodCalls.setLocation(28, 80);
127
128 txtMethodTrace = new JTextArea();
129 txtMethodTrace.setSize(568, 100);
130 txtMethodTrace.setLocation(28, 200);
131 //txtMethodTrace.setEditable(false);
132 //txtMethodTrace.setLineWrap(true);
133 //txtMethodTrace.setWrapStyleWord(true);
134
135 spMethodTrace = new JScrollPane();
136 spMethodTrace.setViewportView(txtMethodTrace);
137 spMethodTrace.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
138 spMethodTrace.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
139 spMethodTrace.setSize(568, 100);
140 spMethodTrace.setLocation(28, 200);
141
142 add(lblFindType);
143 add(comFindType);
144 add(lblFindValue);
145 add(txtFindValue);
146 add(btnDbg);
147 //add(txtMethodCalls);
148 //add(txtMethodTrace);
149 add(spMethodCalls);
150 add(spMethodTrace);
151 }
152
153 @Override
154 public void actionPerformed(ActionEvent e) {
155 debug = !debug;
156 if(debug) {
157 btnDbg.setText("停止调试");
158 switch(comFindType.getSelectedIndex()){
159 case 0:
160 String val = txtFindValue.getText().trim();
161 if(val.isEmpty()) return;
162 try{
163 int intval = Integer.parseInt(val);
164 ViewMethodCall.enableDebug(ViewMethodCall.DEBUG_TYPE_INT, intval);
165 }catch(Exception ex){
166 JOptionPane.showMessageDialog(this, ex.getMessage());
167 debug = !debug;
168 btnDbg.setText("启动调试");
169 }
170 break;
171 case 1:
172 String val1 = txtFindValue.getText().trim();
173 if(val1.isEmpty()) return;
174 ViewMethodCall.enableDebug(ViewMethodCall.DEBUG_TYPE_STR, val1);
175 }
176 }else{
177 btnDbg.setText("启动调试");
178 ViewMethodCall.disableDebug();
179 txtMethodCalls.setText("");
180 txtMethodTrace.setText("");
181 Hashtable callcounter = ViewMethodCall.getDebugResult();
182 for(String methodname : callcounter.keySet()){
183 txtMethodCalls.append(methodname);
184 txtMethodCalls.append("\t");
185 txtMethodCalls.append(callcounter.get(methodname).toString());
186 txtMethodCalls.append("\n");
187 }
188 List calltrace = ViewMethodCall.getMethodCallStackTrace();
189 for(String tracele : calltrace){
190 txtMethodTrace.append(tracele);
191 txtMethodTrace.append("\n");
192 }
193 }
194 }
195
196 /*public static void main(String[] args) {
197 java.awt.EventQueue.invokeLater(new Runnable() {
198 public void run() {
199 try {
200 UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
201 } catch (Exception e) {
202
203 }
204 new FrmMain().setVisible(true);
205 }
206 });
207 }*/
208 }
FrmMain
 1 package test;
2
3 import java.util.ArrayList;
4 import java.util.Hashtable;
5 import java.util.List;
6
7 import javax.swing.UIManager;
8
9 import form.FrmMain;
10
11 public class ViewMethodCall {
12 public static final int DEBUG_TYPE_INT = 1;
13 public static final int DEBUG_TYPE_STR = 0;
14
15 private Hashtable callcounter = new Hashtable();
16 private List callStackTrace = new ArrayList();
17 private boolean debugenable = false;
18 private int debugType = DEBUG_TYPE_INT;
19 private String debugStr = null;
20 private int debugInt = -1;
21 private static ViewMethodCall me = new ViewMethodCall();
22 private ViewMethodCall(){
23 java.awt.EventQueue.invokeLater(new Runnable() {
24 public void run() {
25 try {
26 UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
27 } catch (Exception e) {
28
29 }
30 new FrmMain().setVisible(true);
31 }
32 });
33 }
34 static {}
35
36 public static void beCall(String methodName, Object[] params) {
37 if(!me.debugenable) return;
38 me.callStackTrace.add(methodName);
39 switch(me.debugType){
40 case DEBUG_TYPE_INT:
41 for(Object obj : params)
42 if(obj != null && obj instanceof Integer && (Integer)obj == me.debugInt)
43 if(me.callcounter.contains(methodName))
44 me.callcounter.put(methodName, me.callcounter.get(methodName) + 1);
45 else
46 me.callcounter.put(methodName, 1);
47 break;
48 case DEBUG_TYPE_STR:
49 for(Object obj : params)
50 if(obj != null && obj instanceof String && obj.equals(me.debugStr))
51 if(me.callcounter.contains(methodName))
52 me.callcounter.put(methodName, me.callcounter.get(methodName) + 1);
53 else
54 me.callcounter.put(methodName, 1);
55 break;
56 }
57 }
58
59 public static void enableDebug(int type, Object value){
60 me.debugenable = true;
61 me.callcounter.clear();
62 me.callStackTrace.clear();
63 me.debugType = type;
64 switch(type){
65 case DEBUG_TYPE_INT:
66 me.debugInt = (Integer) value;
67 break;
68 case DEBUG_TYPE_STR:
69 me.debugStr = (String) value;
70 break;
71 }
72 }
73
74 public static void disableDebug(){
75 me.debugenable = false;
76 }
77
78 public static Hashtable getDebugResult(){
79 return me.callcounter;
80 }
81
82 public static List getMethodCallStackTrace(){
83 return me.callStackTrace;
84 }
85 }
ViewMethodCall

  如果看不懂请不要深究,不然到时候查水表被多带走一个&=*

 

 欢迎您移步我们的交流群,无聊的时候大家一起打发时间:Programmer Union

 或者通过QQ与我联系:点击这里给我发消息

 (最后编辑时间2013-11-09 16:24:31)

 


推荐阅读
  • 第一步:PyQt4Designer设计程序界面该部分设计类同VisvalStudio内的设计,改下各部件的objectName!设计 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 有没有一种方法可以在不继承UIAlertController的子类或不涉及UIAlertActions的情况下 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • Android源码中的Builder模式及其作用
    本文主要解释了什么是Builder模式以及其作用,并结合Android源码来分析Builder模式的实现。Builder模式是将产品的设计、表示和构建进行分离,通过引入建造者角色,简化了构建复杂产品的流程,并且使得产品的构建可以灵活适应变化。使用Builder模式可以解决开发者需要关注产品表示和构建步骤的问题,并且当构建流程发生变化时,无需修改代码即可适配新的构建流程。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • C# WPF自定义按钮的方法
    本文介绍了在C# WPF中实现自定义按钮的方法,包括使用图片作为按钮背景、自定义鼠标进入效果、自定义按压效果和自定义禁用效果。通过创建CustomButton.cs类和ButtonStyles.xaml资源文件,设计按钮的Style并添加所需的依赖属性,可以实现自定义按钮的效果。示例代码在ButtonStyles.xaml中给出。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • iOS Swift中如何实现自动登录?
    本文介绍了在iOS Swift中如何实现自动登录的方法,包括使用故事板、SWRevealViewController等技术,以及解决用户注销后重新登录自动跳转到主页的问题。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
author-avatar
yushun
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有