• 标 题:JPT日語 Version 1.91
  • 作 者:dayone
  • 时 间:2003/06/19 08:24pm
  • 链 接:http://bbs.pediy.com

目标软件:日本語能力試験合格への道Ver1.91
下载地址:http://www.fujichinese.com/ygu/showsoft.asp?soft_id=4
软件大小:3.48MB
软件授权:共享软件
加密方式:注册码(注册成功后生成jpt.log文件)
适用平台:WindowsXP/2000/98/95/Me/NT(4.0)+Java(TM) 2 Runtime Environment Version 1.4.0以上
破解工具:Jbuilder7、Java源代码反编译专家。
作者:dayone
E-mail:dayone@wxxd.com
关于本文:本文主要目的在于教学,研究java程序的注册方式,请勿将此教程用于商业目的。

  第一次写crack的文章,不对的地方请各位多多指教!
  最近在学日语,在google.com上搜索到一学习日语软件"JPTXXXXXXX1.91",于是下载安装,原来是java编的,看来作者java功底不错,闲来无事看看java程序是如何注册的,也好学学~
 主程序jpt.exe主要是引导JptDicWindow.jar中的JptStart这个类.很好,把它们全"反"了,可是竟然绝大部分都没反应!看来加密过了,后来发现破绽,主类JptStart.class没被加密,于是一个字"反",看一下这个文件的源代码,其中,“//”处是我自己加入的注释。
//Source File Name:   JptStart.java
import java.io.*;
import java.lang.reflect.Method;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.zip.*;
import javax.crypto.*;  //javax.crypto是1.4.0以上新增的包,用于加密
import javax.crypto.spec.DESKeySpec;
public class JptStart extends ClassLoader
{

   private Hashtable htSizes;
   private Hashtable htJarContents;
   private String jarFileName;
   private SecretKey key;
   private Cipher cipher;

   public JptStart(SecretKey secretkey, String s)
       throws GeneralSecurityException, IOException
   {
       htSizes = new Hashtable();
       htJarContents = new Hashtable();
       key = secretkey;
       jarFileName = s;
       String s1 = "DES";
       SecureRandom securerandom = new SecureRandom();
       cipher = Cipher.getInstance(s1);//加密算法
       cipher.init(2, secretkey, securerandom);
       initvalue(s + ".jar");
   }

   public static void main(String args[])
       throws Exception
   {
       String s = args[1];
       String s1 = args[2];
       String args1[] = new String[args.length - 3];
       System.arraycopy(args, 3, args1, 0, args.length - 3);
       byte abyte0[] = Util.readFile(s);
       DESKeySpec deskeyspec = new DESKeySpec(abyte0);
       SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES");
       SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec);
       JptStart jptstart = new JptStart(secretkey, s1);
       Class class1 = jptstart.loadClass(s1);
       String args2[] = new String[1];
       Class aclass[] = {
           (new String[1]).getClass()
       };
       Method method = class1.getMethod("main", aclass);
       Object aobj[] = {
           args1
       };
       method.invoke(null, aobj);
   }

   private void initvalue(String s)
   {
       try
       {
           ZipFile zipfile = new ZipFile(s);
           ZipEntry zipentry;
           for(Enumeration enumeration = zipfile.entries(); enumeration.hasMoreElements();

htSizes.put(zipentry.getName(), new Integer((int)zipentry.getSize())))
               zipentry = (ZipEntry)enumeration.nextElement();

           zipfile.close();
           FileInputStream fileinputstream = new FileInputStream(s);
           BufferedInputStream bufferedinputstream = new BufferedInputStream(fileinputstream);
           ZipInputStream zipinputstream = new ZipInputStream(bufferedinputstream);
           for(ZipEntry zipentry1 = null; (zipentry1 = zipinputstream.getNextEntry()) != null;)
               if(!zipentry1.isDirectory())
               {
                   int i = (int)zipentry1.getSize();
                   if(i == -1)
                       i = ((Integer)htSizes.get(zipentry1.getName())).intvalue();
                   byte abyte0[] = new byte[i];
                   int j = 0;
                   boolean flag = false;
                   int k;
                   for(; i - j > 0; j += k)
                   {
                       k = zipinputstream.read(abyte0, j, i - j);
                       if(k == -1)
                           break;
                   }

                   htJarContents.put(zipentry1.getName(), abyte0);
               }

       }
       catch(NullPointerException nullpointerexception)
       {
           nullpointerexception.printStackTrace();
       }
       catch(FileNotFoundException filenotfoundexception)
       {
           filenotfoundexception.printStackTrace();
       }
       catch(IOException ioexception)
       {
           ioexception.printStackTrace();
       }
   }

   public byte[] getJarResource(String s)
   {
       return (byte[])htJarContents.get(s);
   }

   public Class loadClass(String s, boolean flag)
       throws ClassNotFoundException
   {
       try
       {
           Class class1 = null;
           class1 = findLoadedClass(s);
           if(class1 != null)
               return class1;
           byte abyte0[] = getJarResource(s + ".class");
           if(abyte0 != null)
           {
               byte abyte1[] = cipher.doFinal(abyte0);
               class1 = defineClass(s, abyte1, 0, abyte1.length);
           }
           if(class1 == null)
               class1 = findSystemClass(s);
           if(flag && class1 != null)
               resolveClass(class1);
           return class1;
       }
       catch(GeneralSecurityException generalsecurityexception)
       {
           throw new ClassNotFoundException(generalsecurityexception.toString());
       }
   }
}
怪不得要用1.4.0以上,原来是要用其中的加密类。类JptStart的功能主要就是把加密的类解密再load。我们只要改成把加密的类解密再保存成文件不就得了~接下来就把上面的loadClass方法改成
   public Class loadClass(String s, boolean flag)
       throws ClassNotFoundException
   {
       try
       {
           Class class1 = null;
           class1 = findLoadedClass(s);
           if(class1 != null)
               return class1;
           byte abyte0[] = getJarResource(s + ".class");
           if(abyte0 != null)
           {
               byte abyte1[] = cipher.doFinal(abyte0);
               class1 = defineClass(s, abyte1, 0, abyte1.length);
               //add by dayone
               try {
               Util.writeFile(s + ".class",abyte1);//保存已解密文件
               }
               catch(IOException e) {
                 return findSystemClass(s);
               }
               //

           }
           if(class1 == null)
               class1 = findSystemClass(s);
           if(flag && class1 != null)
               resolveClass(class1);
           return class1;
       }
       catch(GeneralSecurityException generalsecurityexception)
       {
           throw new ClassNotFoundException(generalsecurityexception.toString());
       }
   }
再编译运行就全部OK,下面就简单了,反编译已解密的JptDicWindow.class看其中的getRegisterFlag方法。
private boolean getRegisterFlag()
   {
       boolean flag = false;
       try
       {
           FileInputStream fileinputstream = new FileInputStream("Jpt.log");
           ObjectInputStream objectinputstream = new ObjectInputStream(fileinputstream);
           RegisterData registerdata = (RegisterData)objectinputstream.readObject();
           String s = System.getProperty("user.name");
           String s1 = System.getProperty("os.name");
           String s2 = s + "jptjpt" + s1 + "xyz" + registerdata.loginName + "abc" + String.valueOf(diskSerialNumber);
           char ac[] = s2.toCharArray();
           int i = 1;
           for(int j = 0; j < ac.length; j++)
               i = Math.abs(ac[j] * i + 3959) % 0xf4240;
           i = 0xa04747 + Math.abs(i * (s2.length() + ac.length));//计算登录番号(机器码)
           ////计算暗证番号(注册码),算法一目了然吧~
           int k = 0x7346322 + Math.abs((i / 133) * 171 ^ 0x25cd97d);
           //用delphi算注册码为copy(120873762 +  Abs(((i div 133) * 171) xor 39639421),0,8)  
           //以下比较注册码
           if(registerdata.registerCode.equals(String.valueOf(k).substring(0, 8)))
           {
               nativeIndex = registerdata.nativeIndex;
               flag = true;
           }
           if(registerdata.loginName.equals("friend") &&
registerdata.registerCode.equals("1234567"))              //靠,万能注册码,省得我写什么注册机了~
           {
               nativeIndex = registerdata.nativeIndex;
               flag = true;
           }
           fileinputstream.close();
       }
       catch(Exception exception) { }
       return flag; //改成return true;就不用注册了~
   }
如果主类JptStart.class也被加密就难破解了,但不知道如何实现。看来共享软件最好不要用java编,除非你真的有很好的加密方法。BTW,这个软件做的不错,对学java的朋友有很大的帮助。