引用: 续:
http://bbs.pediy.com/showthread.php?s=&threadid=32484
 

舵手兄终于处理了 Zelix KlassMaster,为了回应舵手的功劳。
我再次奉献 DashO Pro Evaluation 3.2 版本的逆向分析

DashO Pro 3.2 试用版 注册机
    对于Java软件来说,安全性一直是非常脆弱的。虽然出现了很多保护手段,但是都并不完善。对于Java软件来说使用最多的保护手段就是使用混乱器了。这里使用DashO Pro作为混乱器的效果是很好的。但是怎样才能无限制的使用这个版本呢。制作出注册机就可以了吧?!
Author:vhly[FR]
Tools:DJ Java Decompiler 3.8.8

对于如今的混乱器而言,字符串加密简直是必备的功能,如果哪一个混乱器没有此项功能,那么并不算一个好的混乱器。同时混乱器还应该包含控制流程混乱、堆栈混乱、字节代码混乱等等。只有符合上述功能才算一个好的软件吧?!

1. 思路
对于经过自身混乱的DashO Pro 3.2 Evaluation来说,他的保护非常好,首先,如果按照程序启动顺序的话,就跟本不可能破解。因为这个软件GUI部分使用DashoProGuiEval类作为主类,同时主类会调用b类的方法 a(String [] args),但是最为关键的是,b类当使用DJ Java Decompiler 也就是 jad 时出现非法操作。针对这种情况,应该可以推断,要么使用了自定义类装载器,要么使用了字节代码陷阱或者畸形的类文件。鉴于以前破解Smokescreen时的经验,针对这种大型的,包含许多类文件的软件,首先要找到关键信息。可以直接通过类文件中的常数池读出String类型的常量就可以了。同时DashO使用了字符串加密,解密方法如下:
public static String A_B_C_D(String s)  // DashO 所使用的字符串加密/解密方法
    {
        char ac[] = new char[s.length()];
        s.getChars(0, s.length(), ac, 0);
        char c = '\0';
        for(int i = 0; i < ac.length; i++)
            ac[i] = (char)(ac[i] - 1 ^ c++);

        return new String(ac);
 }

就可以制作字符串提取器了 如下 com.vhly.anti.dasho.DecodeString 类

package com.vhly.anti.dasho;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.util.jar.*;

import com.vhly.classfile.*;  // 添加了我的类文件分析库

public class DecodeString
{
  private String targetName;
  private String outName;
  
  public DecodeString()
  {
    Debug.setEnable(false);
  }

  public static String decodeString(String oldString)
  {
    char ac[] = new char[oldString.length()];
    oldString.getChars(0, oldString.length(), ac, 0);
    char c = '\0';
    for(int i = 0; i < ac.length; i++)
      ac[i] = (char)(ac[i] - 1 ^ c++);
    return new String(ac);
  }
  
  public void setTargetName(String fn)
  {
    targetName = fn;
  }
  
  public void setOutName(String on)
  {
    outName = on;
  }
  
  public void doWork() throws IOException
  {
    JarFile target = new JarFile(targetName);
    FileWriter out = new FileWriter(outName);
    BufferedWriter bout = new BufferedWriter(out);
    PrintWriter pw = new PrintWriter(bout);
    Enumeration en = target.entries();
    JavaClass jc = null;  // 通过输入流获取类
    InputStream in = null;
    while(en.hasMoreElements())
    {
     JarEntry je = (JarEntry)(en.nextElement());
      String name = je.getName();
      if( name.endsWith(".class") )
      {
      in = target.getInputStream(je);
        jc = new JavaClass();
        jc.read(in);
      pw.println("Find The Class:"+name);
      ConstantPool cp = jc.getCP();
        int size = cp.size();
        for(int i=1; i< size; i++)
        {
    CPEntry cpe = cp.getCPEntryAtIndex(i);
    if( cpe.getType() == CPEntry.TYPE_STRING )
      {          CPString cps = (CPString)cpe;        int sindex = cps.getStringIndex();
  CPUtf8 utf8 = (CPUtf8)(cp.getCPEntryAtIndex(sindex));
  String data = utf8.getString();
            pw.println("    String data["+data+"]:");
            pw.println("          \""+decodeString(data)+"\";");
          }
        }
      }
    }
    jc = null;
    in = null;
    pw.close();
    bout.close();
    out.close();
    target = null;
    System.gc();
  }
  
  public static void main(String args[]) throws IOException
  {
    DecodeString ds = new DecodeString();
    ds.setTargetName(args[0]);
    ds.setOutName(args[1]);
    ds.doWork();
    ds = null;
  }
}
由于DashO对类文件进行了处理,上面代码会在某些类上出错。
提取出的字符串内容 (节选完整内容另存文件)

Find The Class:c6.class 包含了程序第一次运行和注册失败后的用户信息注册对话框类
    String data[Sefkxrug}aff ]:
          "Registration?";
    String data[Fmdki&Ucpaz聙j聙 ]:
          "Email Register";      注册的两种方式 必须使用此种方式
    String data[Xea$Wabo|~pz ]:
          "Web Register";      web注册
    String data[Damabj ]:
          "Cancel";
    String data[Cabi ]:
          "Back";
    String data[Tuaonr ]:
          "Submit";
Find The Class:c7.class   构造第一次注册用户信息的对话框
    String data[Giqqq&Igfm ]:
          "First Name?";
    String data[Marx%Lhkn ]:
          "Last Name?";
    String data[Fmdki&hdm|py聙 ]:
          "Email addres联?";
    String data[Qjnnb&Isflpz ]:
          "Phone Number?";
    String data[Ga{$Kqlfn| ]:
          "Fax Number?";
    String data[Doptfl聙)Jpmcaep|z} ]:
          "Compan聞/Affiliation?";
    String data[Uiwpb ]:
          "Title?";
    String data[Bfgrbwv(: ]:
          "Address 1?";
    String data[Bfgrbwv(; ]:
          "Address 2?";
    String data[Diw{ ]:
          "City?";
    String data[Tvdxb ]:
          "State?";
    String data[[is-Ukvtjf+Idjl ]:
          "Zip/Postal Code?";
    String data[Doxnqx聙)[mncdd ]:
          "Countr聞/Region?";
    String data[Teqkfj'J~eio ]:
          "Serial Number?";
    String data[Tvdxrw'Kn{zkli ]:
          "Status Message?";
    String data[uzwFnxvtGiho ]:
          "txtFirstName?";
    String data[Qnhcxa'cg~pz-ub{c2}szq7w:otz>YwSSW kELC  PDOAJ  ]:
          "Please enter your name in the First Name field.?";
 部分出错信息以及控件位置信息未列出
    String data[Qjnnb&isflpz-a|}e2qw5uc8u}|ii>00 FLENRV. ]:
          "Phone number must be at least 10 digits
?";
    String data[Fpwgw&h(igci.~kcyt聙5|d{{}i ]:
          "Enter a valid serial number¦";
    String data[Fpwgw&ei}b+nf聙~|1q}x5zxem:u{ryn  ]:
          "Enter both fi聕st and last names

2. 寻找软件关键点
当找到程序第一次注册的类之后,第二次运行时,会出现 “Step 2” 注册对话框。对话框中出现:“Serial Number”、“Confirmation Code”等字段,那么在我们提取的字符串文件中寻找 “Confirmation Code”字符串 发现,在d0 类中有着部分信息,同时会安装事件处理类 d1 反汇编这个类,发现如下代码片断:
class d1   implements ActionListener
    {
        private final d0 a; /* synthetic field */
        public final void actionPerformed(ActionEvent actionevent)
        {
            boolean flag = true;
            if(d0.a(a))   // return d0.i;
            {
                String s = d0.b(a).getText().trim();
                if(s.length() > 0)
     d0.c(a).t(s);   // cw d0.c(d0 a)  cw.t(s); 就是设置cw.m = s
                else
                 flag = false;
            }
            try
            {
    d0.c(a).b(Integer.parseInt(d0.d(a).getText().trim()));
       // 设置cw.al = int;
            }
            catch(NumberFormatException numberformatexception)
            {
                flag = false;
            }
            if(flag && d0.c(a).ag())  //  cw.ag() -> 真正的序列号校验
            {
                d0.c(a).e(true);
                d0.a(a, 0);
                d0.e(a);
            } else
            {
                d0.c(a).e(false);                JOptionPane.showMessageDialog(((Component)actionevent.getSource()).getParent().getParent(), "Invalid Confirmation Code.  Please try again.", "Error", 0);
            }
        }
            public d1()
            {}
    }

3. 注册算法
关键点  cw类的 ag()方法
    public final boolean ag()
    {
        boolean flag = false;  // al 为软件 Confirmation Code 可以自定义
        int i1 = (al & 0xff) << 8;
        i1 |= (al & 0xff00) >> 8;
        if(m != null)
            try
            {
                if((i1 ^ 0x30cd) == Integer.parseInt(m))  // m为序列号
                    flag = true;
            }
            catch(NumberFormatException numberformatexception) { }
        return flag;
    }




4. 制作注册机
package com.vhly.keygen.dasho;

public class ConfirmCodeSum
{
  public static String gen(String conf)
  {
    int tmp = Integer.parseInt(conf);
    int i1 = (tmp & 0xff) << 8;
    i1 |= (tmp & 0xff00) >> 8;
    i1 ^= 0x30cd;
    String ret = Integer.toString(i1);
    return ret;
  }
  
  public static void main(String args[])
  {
    String conf = "99334827";  // 此处可以随意修改,但必须为十进制数!
System.out.println("Confirm Code:"+conf+"   Gen Serial Code:"+gen(conf));
  }
}


其中的 vhly FR是在第一次注册时填上的 注意第一次输入序列号时候,注册码可以随意,但是必须选择使用 Email Register方式

5. 软件的逆向工程
第一步、去除(EVALUATION ONLY)标记,去除生成类文件中的 DashoEval_前缀,可修改为 DashO_,也就是去除所有的试用信息

首先:由于软件中使用了字符串加密的方式,因此如果我们要加入任何信息,都必须使用加密后的内容,根据解密程序,分析出DashO的字符串加密方法。




代码见下一页:

public class GenString
{
    public static String gen(String s)
    {
        char ac[] = new char[s.length()];
        s.getChars(0, s.length(), ac, 0);
        char c = '\0';
        for(int i = 0; i < ac.length; i++)
            ac[i] = (char)((ac[i] ^ c++)+1);
        return new String(ac);
    }
    
    public static void main(String args[])
    {
      String my_title = "=(Modified by vhly[FR])=";
      System.out.println("Gen String:"+gen(my_title));
      System.out.println("     Method     "+ gen("Method"));
  System.out.println("DashO_Pro_cracked_:"+ gen("DashO_Pro_cracked_"));
    }
    
}

找到关于对话框中的 EVALUATION COPY 的相应加密内容 
         String data[!*HVFJTG]AFF-OB`J9 ]:
          " (EVALUATION COPY)";

   使用直接修改类文件的方式,找到 上述内容
进行体换,替换为  [>*Pmamaonn+jv.yh}iJVGI@+] => “=(Modified by vhly[FR])=”


关于对于类生成后重命名的修改,由于生成的类,使用试用版会使用 DashEval_
作为前缀。我希望修改为 自定义的类名称

重点在于处理 hp.class,采用十六进制编辑器直接修改 CPUtf8

找到 DashEval_的字符串

Gen String:>*Pmamaonn+jv.yh}iJVGI@+
     Method     Newllb
DashO_Pro_cracked_:EarlL[WvhWjznofkuO
vwwwwvvwv_ gen:wwvuttqqW  // 使用这个
Gen String:>*Pmamaonn+jv.yh}iJVGI@+

在hp.class中找到两处包含 Earl的地方 注意时 CPUtf8格式
即: 01 short_len Ear....
     01 short_len Ear...

修改为  wwvuttqqW =〉 vwwwwvvwv_
        wwvuttqq =〉  vwwwwvvwv

之后的生成结果

类名为: vwwwwvvwv_a vwwwwvvwv_b vwwwwvvwv_c 等等
成员变量: 同理

最后对于舵手兄说的 JProbe我现在上班只有周日有时间,不过会继续努力的

Author: vhly[FR]
Date: 2006/10/03
Time: 03:33:33 AM
Have A Good Time And I will Sleep 
All The Time Gen This 5 Hours