首页
社区
课程
招聘
[原创]JProfiler 6/7 注册机制逆向工程分析
2012-1-10 11:32 11500

[原创]JProfiler 6/7 注册机制逆向工程分析

2012-1-10 11:32
11500
前几天在看JProfiler,直接进入注册部分,试用注册支持 10天,太短了。

因此想分析一下:

Target: JProfiler 6/7 Mac OS X版本 其余版本通用
Tools:   IDA Pro, DJ Java Decompiler
Steps:
   1. 代码定位
   2. 注册算法分析
   3. 注册机实现

1. 代码定位

对于Java软件,首先查看 lib 目录,看看有没有特别的,然后看看 bin目录下的内容,

bin目录中包含jprofiler.jar 这个可能是关键

运行软件,查找特征字符串,比如 Enter license 等

定位到,经过名称混淆的类 如 com.A.A.E.B 这个类,其中包含了诸如 license.key, license, name等字符串,着重查看

再有 com.A.A.E.K 这个里面有些 Please enter a license key. 这种信息,

因此这个地方可能是界面部分的逻辑处理。

com.A.A.E.K 代码如下(经过初步 Rename)

其中 public boolean H() 这个方法必然是代码授权验证检查的地方

int i = b1.F3(strKey, strName, strCompany); 这个地方明显是检测验证信息的地方

由此可见

package com.A.A.E;

import com.A.A.B.J.E;
import com.A.A.B.e;
import net.miginfocom.swing.MigLayout;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;

public abstract class K extends JPanel
        implements ActionListener {

    private static final String I = "single";
    private static final String L = "floating";
    protected Component parent;
    private JRadioButton radioSingleLicense;
    private JRadioButton radioFloatingLicense;
    private ButtonGroup radioGroup;
    private JTextField txtKey;
    private JTextField txtName;
    private JTextField txtCompany;
    private JTextField txtFloatLicense;
    private JTextField txtFloatName;
    private JTextField txtFloatCompany;
    private JButton btnPaste;
    private JButton btnClear;
    private JPanel A;
    private CardLayout B;

    public K(Component component) {
        parent = component;
        R();
        I();
        E();
    }

    public void M() {
        if (radioSingleLicense.isSelected())
            txtName.requestFocus();
        else
            txtFloatName.requestFocus();
    }

    protected String J() {
        return txtKey.getText();
    }

    public void actionPerformed(ActionEvent actionevent) {
        Object obj = actionevent.getSource();
        if (obj == btnClear) {
            txtKey.setText("");
            txtName.setText("");
            txtCompany.setText("");
        } else if (obj == btnPaste)
            b();
        else if (obj == radioSingleLicense)
            B.show(A, "single");
        else if (obj == radioFloatingLicense)
            B.show(A, "floating");
    }

    /**
     * Get Key Content
     * @return String
     */
    public String L() {
        return txtKey.getText().trim();
    }

    public String c() {
        return txtFloatLicense.getText().trim();
    }

    public void a() {
        com.A.A.E.B b1 = U();
        txtName.setText(b1.EA());
        txtCompany.setText(b1.F6());
        String s = b1.E4().trim();
        if (S() && s.startsWith("FLOAT:")) {
            txtFloatLicense.setText(s.substring("FLOAT:".length()));
            B.show(A, "floating");
            radioFloatingLicense.setSelected(true);
            txtFloatName.requestFocus();
        } else {
            txtKey.setText(s);
            B.show(A, "single");
            radioSingleLicense.setSelected(true);
            txtName.requestFocus();
        }
    }

    public boolean H() {
        com.A.A.E.B b1 = U();
        boolean flag = alwaysFalse() && txtName.getText().length() == 0 && txtCompany.getText().length() == 0 && txtKey.getText().length() == 0;
        if (!flag && !validInput())
            return false;
        String strKey;
        String strName;
        String strCompany;
        if (radioSingleLicense.isSelected()) {
            strName = txtName.getText();
            strCompany = txtCompany.getText();
            strKey = L();  // Get License
        } else {
            strName = txtFloatName.getText();
            strCompany = txtFloatCompany.getText();
            strKey = (new StringBuilder()).append("FLOAT:").append(c()).toString();
        }
        com.A.A.E.F.E();
        int i = b1.F3(strKey, strName, strCompany);
        if (i >= 0 || i == -1)
            b1.ED(Math.max(0, i));
        else
            b1.ED(-1);
        if (flag) {
            Y(true);
            return true;
        }
        switch (i) {
            case -2:
                d();
                Z();
                return false;

            case -3:
                K();
                Z();
                return false;

            case -10:
                V();
                Z();
                return false;

            case -11:
                T();
                Z();
                return false;

            case -9:
                _();
                Z();
                return false;

            case -8:
                B();
                Z();
                return false;

            case -4:
                W("The license server is invalid.");
                Z();
                return false;

            case -5:
                W("There was an error communicating with the license server.");
                Z();
                return false;

            case -6:
                W(com.A.A.E.F.I());
                Z();
                return false;
        }
        if (i < 0 && i != -1) {
            throw new RuntimeException((new StringBuilder()).append("Unknown license status ").append(i).toString());
        } else {
            Y(false);
            return true;
        }
    }

    private void Z() {
        if (radioSingleLicense.isSelected())
            txtKey.requestFocus();
        else
            txtFloatLicense.requestFocus();
    }

    protected void Y(boolean flag) {
        com.A.A.E.B b1 = U();
        if (radioSingleLicense.isSelected() || flag) {
            b1.E8(txtName.getText().trim());
            b1.F8(txtCompany.getText().trim());
            b1.EE(L());
        } else {
            b1.E8(txtFloatName.getText().trim());
            b1.F8(txtFloatCompany.getText().trim());
            b1.EE((new StringBuilder()).append("FLOAT:").append(c()).toString());
        }
    }

    protected void R() {
        txtName = new E(10); // JTextField
        txtName.setName("name");
        txtCompany = new E(10); // JTextField
        txtCompany.setName("company");
        txtKey = new E(10); // JTextField
        txtKey.setName("key");
        txtFloatName = new E(txtName.getDocument(), null, 10);
        txtFloatCompany = new E(txtCompany.getDocument(), null, 10);
        txtFloatLicense = new E(10);
        btnPaste = new JButton("Paste From Clipboard");
        btnClear = new JButton("Clear");
        radioSingleLicense = new JRadioButton("Single or evaluation license", true);
        radioFloatingLicense = new JRadioButton("Floating license");
        radioGroup = new ButtonGroup();
        radioGroup.add(radioSingleLicense);
        radioGroup.add(radioFloatingLicense);
    }

    public abstract com.A.A.E.B U();

    protected abstract void d();

    protected abstract void K();

    protected abstract void B();

    protected abstract void _();

    protected abstract void V();

    protected abstract void W(String s);

    protected void T() {
        com.A.A.A.K().J(parent, "<html>The license key you have entered is intended for a <b>floating license server</b>.<br>Please consult your licensing email for download details of the floating license server.<br><br>If you already have an installation of the floating license server, please add this key<br>to the file <tt>license.txt</tt> in your floating license server installation.", 0);
    }

    protected boolean alwaysFalse() {
        return false;
    }

    protected void I() {
        setLayout(new MigLayout("insets 0", "[grow]", (new StringBuilder()).append(S() ? "[]unrel" : "").append("[grow]").toString()));
        if (S()) {
            add(radioSingleLicense, "split");
            add(radioFloatingLicense, "wrap");
        }
        B = new CardLayout();
        A = new JPanel(B);
        A.add(G(), "single");
        A.add(A(), "floating");
        B.show(A, "single");
        add(A, "grow");
        a();
    }

    private JPanel G() {
        JPanel jpanel = new JPanel(new MigLayout("insets 0, wmin 350px", "[][grow]", "[][]para[][]para[]"));
        jpanel.add(new JLabel("Name: "));
        jpanel.add(txtName, "growx, wrap");
        jpanel.add(new JLabel("Company: "));
        jpanel.add(txtCompany, "growx, wrap");
        jpanel.add(new JLabel("License key: "));
        jpanel.add(txtKey, "growx, wrap");
        jpanel.add(F(), "alignx 100%, span, wrap");
        jpanel.add(new e("If you have received the license key by e-mail, you can copy the entire e-mail to the clipboard and use the \"Paste from clipboard\" button to extract the license key."), "growx, span");
        return jpanel;
    }

    private JPanel A() {
        JPanel jpanel = new JPanel(new MigLayout("insets 0, wrap", "[][grow]", (new StringBuilder()).append("[][]para[][]").append(O()).toString()));
        jpanel.add(new JLabel("Name: "));
        jpanel.add(txtFloatName, "growx");
        jpanel.add(new JLabel("Company: "));
        jpanel.add(txtFloatCompany, "growx");
        jpanel.add(new JLabel("License server: "));
        jpanel.add(txtFloatLicense, "growx");
        jpanel.add(new JLabel("(host name or IP address)"), "skip");
        D(jpanel);
        return jpanel;
    }

    protected String O() {
        return "";
    }

    protected void D(JPanel jpanel) {
    }

    protected boolean S() {
        return true;
    }

    protected void E() {
        btnClear.addActionListener(this);
        btnPaste.addActionListener(this);
        radioSingleLicense.addActionListener(this);
        radioFloatingLicense.addActionListener(this);
    }

    private Box F() {
        Box box = Box.createHorizontalBox();
        box.add(btnPaste);
        box.add(btnClear);
        return box;
    }

    private boolean validInput() {
        if (txtName.getText().trim().length() == 0) {
            W("Please enter a name.");
            if (radioSingleLicense.isSelected())
                txtName.requestFocus();
            else
                txtFloatName.requestFocus();
            return false;
        }
        if (radioSingleLicense.isSelected() && L().length() == 0) {
            W("Please enter a license key.");
            txtKey.requestFocus();
            return false;
        }
        if (radioFloatingLicense.isSelected() && c().length() == 0) {
            W("Please enter a license server.");
            txtFloatLicense.requestFocus();
            return false;
        } else {
            return true;
        }
    }

    private void b() {
        Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
        if (transferable != null)
            try {
                Reader reader = DataFlavor.getTextPlainUnicodeFlavor().getReaderForText(transferable);
                BufferedReader bufferedreader = new BufferedReader(reader);
                String s;
                String s1;
                for (s = null; (s1 = bufferedreader.readLine()) != null && (s = Q(s1)) == null;) ;
                if (s != null)
                    txtKey.setText(s);
                else
                    N();
            } catch (UnsupportedFlavorException unsupportedflavorexception) {
                N();
            } catch (IOException ioexception) {
                N();
            }
    }

    private String Q(String s) {
        int i = s.indexOf("E-");
        if (i == -1)
            i = s.indexOf("T-");
        if (i == -1)
            i = s.indexOf("L-");
        if (i == -1)
            i = s.indexOf("A-");
        if (i == -1)
            i = s.indexOf("S-");
        if (i == -1)
            return null;
        int j = s.indexOf('"', i);
        if (j == -1)
            j = s.indexOf(' ', i);
        if (j == -1)
            return s.substring(i);
        else
            return s.substring(i, j);
    }

    private void N() {
        W("No license key was found on the clipboard.");
    }

    public boolean C() {
        int i = U().F0();
        if (i != -1 && i < 0) {
            if (com.A.A.A.K().B(this, "No valid license has been entered. Would you like to quit the application?", com.A.A.B.B.D, com.A.A.B.B.D, 2) == com.A.A.B.B.C) {
                try {
                    System.exit(1);
                } catch (Throwable throwable) {
                }
                return false;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }
}

int i = b1.F3(strKey, strName, strCompany); 这个方法的代码实现是

那个这个B类中的F3方法是切入点,代码如下:

/**
     * Check License status<br/>
     * return values:<br/>
     * 0   OK
     * -1  OK
     * -2
     * -3
     * -4  Server invalid
     * -5  communicating error
     * -6
     * -8
     * -9
     * -10
     * -11
     * @param key
     * @param name
     * @param company
     * @return status
     */
    public int F3(String key, String name, String company) {
        if (key == null)
            key = l;
        if (key.startsWith("FLOAT:")) { // Check float
            String s4 = key.substring("FLOAT:".length());
            int i1 = -1;
            int j1 = s4.lastIndexOf(':');
            if (j1 > -1) {
                try {
                    i1 = Integer.parseInt(s4.substring(j1 + 1));
                } catch (NumberFormatException numberformatexception) {
                }
                s4 = s4.substring(0, j1);
            }
            int k1 = E9();
            int l1 = com.A.A.E.F.C(s4, i1, k1, name, company, F1(), E6() != null ? this : null);
            u = com.A.A.E.F.L();
            EC(l1 > 0 || l1 == -1);
            return l1;
        } else {
            // Check License only
            return E0(key);
        }
    }

包含两个部分,一个是针对 FLOAT license的,另外一个是针对 普通授权的 E0 这个方法

protected abstract int E0(String s1); 这个E0是抽象的,需要在类库中,搜索,

搜索的关键字可以这样,因为抽象,则必须进行重载实现,那么着这个B类的名称(Java VM格式)

就可以了 Lcom/A/A/E/B; 这个代表了一个类,那么可以找到引用的或者是重载的地方了。

经过WinRAR查找,最终定位到 com.jprofiler.frontend.J.K 这个类,其中的代码形式如下:

protected int E0(String s) {
        return (new D()).M(s);
}

这个D 和 com.jprofiler.frontend.J.K 同包 即 com.jprofiler.frontend.J.D

整体代码如下

//public class D extends F
//{
//
//    public D()
//    {
//    }
//
//    protected int K()
//    {
//        return 6;  // JProfiler 版本好
//    }
//
//    protected char[] A()
//    {
//        return (new char[] {
//            'J'   // J 代表 JProfiler
//        });
//    }
//
//    public int M(String licenseKey)
//    {
//        if(licenseKey.startsWith("F-"))
//            return -11;
//        A a = new A(licenseKey);    // 生成授权对象
//        int i = a.M();              // 计算授权状态,同时也代表了  试用版本的过期时间
//        if(i == -3 || i == -2)
//            return i;
//        String s1 = a.C();
//        String s2 = a.K();
//        if(s1 == null || s2 == null)
//            return -3;
//        if(!H(s2, s1, 7, 38, 11))           // 这个地方是关键点
//            return J(licenseKey);
//        else
//            return a.M();
//    }
//}

Java部分校验的最终处理部分
protected static boolean H(String s, String s1, int i, int j, int k) {
        if (s1 == null)
            return false;
        char ac[] = s1.toCharArray();
        int l = 0;
        char ac1[] = ac;
        int i1 = ac1.length;
        for (int j1 = 0; j1 < i1; j1++) {
            char c = ac1[j1];
            l += c;
        }

        String s2 = (new StringBuilder()).append(String.valueOf(l % i)).append(String.valueOf(l % j)).append(String.valueOf(l % k)).toString();
        return s.equals(s2) || s.equals(s2.replace('0', 'a').replace('1', 'b'));
    }

关键部分的授权对象 com.A.A.E.A

public class A
{

    private String licenseKey;
    private String B;
    private String E;
    private static final char C = 45;
    private int A;

    public A(String lic)
    {
        B = "";
        E = "";
        licenseKey = lic != null ? lic.trim() : null;
        if(!validLicense())
            A = -3;
        else
            switch(lic.charAt(0))
            {
            case 69: // 'E'  试用
            case 84: // 'T'
                A = B(lic);
                break;

            case 65: // 'A'
            case 76: // 'L'
            case 83: // 'S'
                A = -1;
                break;

            default:
                A = -3;
                break;
            }
    }

    protected int B(String s)      // 检查试用版本
    {
        int i = com.A.A.E.F.B(s);
        if(i < 4 || i > 5)
            return -3;
        Date date = F(s, i);
        Date date1 = new Date();
        if(date1.compareTo(date) > 0)
            return -2;
        else
            return (int)((date.getTime() - date1.getTime()) / 0x5265c00L);
    }

    private static Date F(String s, int i)
    {
        int j = s.indexOf('-') + 1;
        if(i > 4)
            j = s.indexOf('-', j) + 1;
        j = s.indexOf('-', j) + 1;
        int k = s.indexOf('.', j);
        int l = Integer.parseInt(s.substring(j, k));
        j = k + 1;
        k = s.indexOf('.', j);
        int i1 = Integer.parseInt(s.substring(j, k));
        j = k + 1;
        k = s.indexOf('-', j);
        int j1 = Integer.parseInt(s.substring(j, k));
        Calendar calendar = Calendar.getInstance();
        calendar.set(l, i1 - 1, j1, 0, 0);
        return calendar.getTime();
    }

    public String I()
    {
        return B;
    }

    public String E()
    {
        return E;
    }

    public String H()
    {
        return licenseKey;
    }

    public String G()
    {
        byte abyte0[] = E.getBytes();
        for(int i = 0; i < abyte0.length; i++)
            abyte0[i] = (byte)(abyte0[i] + 2);

        return new String(abyte0);
    }

    private boolean validLicense()   // 检查注册码格式是否正确
    {
        if(licenseKey == null || licenseKey.length() < 3 || licenseKey.charAt(1) != '-')
            return false;
        switch(licenseKey.charAt(0))
        {
        case 69: // 'E'
        case 84: // 'T'
            return fourSplitLicense();

        case 65: // 'A'
        case 76: // 'L'
        case 83: // 'S'
            return twoSplitLicense();  // 正式版
        }
        return false;
    }

    private boolean D(int splitCharLength)
    {
        int j = com.A.A.E.F.B(licenseKey);   // 查看 - 的个数
        if(j < splitCharLength || j > splitCharLength + 1)
            return false;
        int k = A(licenseKey, 0, j);
        if(k == -1 || A(licenseKey, k, 1) > -1)
        {
            return false;
        } else
        {
            B = licenseKey.substring(0, k);
            E = licenseKey.substring(k);
            return true;
        }
    }

    private boolean fourSplitLicense()
    {
        return D(4);
    }

    private boolean twoSplitLicense()
    {
        return D(2);
    }

    public int M()
    {
        return A;
    }

    public String C()
    {
        int i = E.indexOf('#');
        if(i > -1)
            return E.substring(0, i);
        else
            return E;
    }

    public String K()
    {
        int i = E.indexOf('#');
        if(i > -1)
            return E.substring(i + 1);
        else
            return null;
    }

    private static int A(String s, int i, int j)
    {
        int k = i;
        for(int l = 0; l < j; l++)
        {
            k = s.indexOf('-', k);
            if(k == -1)
                return -1;
            k++;
            if(s.length() <= k)
                return -1;
        }

        return k;
    }
}

最终针对 Java部分的格式校验,生成的 License 格式说明如下:

LicenseType-Jversion-user#UserID-nativeHash#JavaHashCode  对于正式版

试用版
LicenseType-Jversion-user#UserID-yyyy.mm.dd-days-nativeHash#JavaHashCode

其中 Java部分主要针对的是 nativeHash 生成 JavaHashCode 的校验

2. Java部分算法分析

注意看 D 这个类的 //        if(!H(s2, s1, 7, 38, 11))           // 这个地方是关键点 部分

这个是可以理解为 H(licCode, nativeHash, 7, 38, 11){
    int sum = 0;
    for (char ch : nativeHash){
        sum += ch;
    }
    String realCode = (sum % 7) append (sum % 38) append (sum % 11);
    compare( licJavaHashCode, realCode);
}

因此Java部分的算法分析就是按照指定的License格式,生成,最后进行 nativeHash的验证。

通过即可启动程序。

2. 2 Native 部分算法分析

单纯的实现Java注册部分,只能够启动程序,任何实际的Profile调试均不起作用。

启动后,发现 JProfiler 控制台 打印了 JProfiler> Error Licese .... 具体内容忘记了,

一般这样的话,我们可以定位这些代码都是通过 JNI 采用本地的 动态链接库实现的,

采用 IDA Pro 进行动态库的分析。

1)由于使用 Mac OS 操作系统,因此直接安装的是 Mac OS X 版本的 JProfiler

找到本地动态库 jprofiler.jnilib 这个文件,拷贝到Windows用 IDA Pro 64bit 打开。

待续

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
免费 6
打赏
分享
最新回复 (10)
雪    币: 328
活跃值: (39)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
vhly 5 2012-1-10 11:41
2
0
Native部分

错误提示信息为  Invalid license key

通过IDA Pro 查找,(针对 64bit Mac OS X 动态库)

找到引用 这个字符串类的地方,在 方法 LiveProfiling::initCommunication(JNIEnv *) 中

会进行打印操作,那么我们定位逻辑,到底从哪里来得。

这个流程是通过 LiveProfilingSession::initParameters(JNIEnv *) 这个方法的返回值进行

处理的。那么进入这个部分 LiveProfilingSession::initParameters(JNIEnv *)

仔细找找能够发现 LicenseChecker::checkLicenseKey(char *, bool) 这个方法,

那么以后就着重 LicenseChecker C++ 类进行分析了。

64位汇编中经常有 rbx, r12d 这种,不明白,

这样也导致了最终注册机没有采用全部独立编写的问题。

LicenseChecker::checkLicenseKey 的逻辑

{
    License:String
    if(license startsWith("FLOAT")){
      // goto FLOAT license check
    }else{
         char ch = license charAt(0);
         if(ch == 'A'){
               // 正式版
               LicenseChecker::checkNormalLicense(license)
         }else{
               // 试用版
         }
    }
}

LicenseChecker::checkNormalLicense(license) 逻辑

{
    license countDashes 获取 - 个数
    if(count > 3){
        LicenseChecker::isChecksumValid(license);
    }
}

LicenseChecker::isChecksumValid(license) 逻辑

{
    strrchr 查找 license 最后一个 - 的位置
    计算这个之前的个数
    必须大于4
    if(len > 4){
         int crcValue;
         clearCRC32(&crcValue);  // crcValue = -1
         addCrc32b(&crcValue, license, len);   // 最后一个 - 之前的内容加到 crcValue中
         addCrc32b(&crcValue, "jprofiler version key1", ""len); // 插入特定版本的key1
         tempCRC = 0;
         getCRC32(&tempCRC, &crcValue); // 将 crcValue中数值处理到 tempCRC

         clearCRC32(&crcValue);  // crcValue = -1
         addCrc32b(&crcValue, license, len);   // 最后一个 - 之前的内容加到 crcValue中
         addCrc32b(&crcValue, "jprofiler version key2", ""len); // 插入特定版本的key2
         tempCRC2 = 0;
         getCRC32(&tempCRC2, &crcValue); // 将 crcValue中数值处理到 tempCRC

         var10:String
         var20:String
         LicenseChecker::calculateChecksum(tempCRC, var10, true); // 将tempCRC转换为字符串
         LicenseChecker::calculateChecksum(tempCRC2, var20, true); // 将tempCRC2转换为字符串

         nativeHash <= var10 append var20;
         // 和 license中最后的 - 之后的内容进行比较 是否相等
    }
}

由此可见上面的方法,就是 nativeHash的计算方法
雪    币: 328
活跃值: (39)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
vhly 5 2012-1-10 12:04
3
0
3. 注册机实现

由于 clearCRC32, addCrc32b, getCRC32 这几个方法,以及 LicenseChecker::calculateChecksum 这个方法,分析比较难,实在没有搞懂,但是整个注册机的
逻辑已经清除,因此准备编写注册机。

关键部分在与上述方法没有代码实现,那么只好用 dll, jnilib 这些动态链接库中的方法进行加载调用了。

经过检查 Windows 版本的dll ,没有导出相应的函数,只好在 Linux或者 Mac 中了。

Linux版本已经完成,采用 dlopen, dlsym 就可以将动态链接库加载,并且根据IDA Pro分析出的函数签名,来进行加载调用生成nativeHash。

一下是 Mac OS X 版本的代码,一共参考

//
//  main.m
//  jprofiler
//
//  Created by  on 12-1-4.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//

#include <mach-o/dyld.h>

#import <Foundation/Foundation.h>

char *getHash(char *hash, char *buf, int len, int key1, int key2, int key3) {
        int sum = 0;
        int i = 0;
        for (; i < len; i++) {
                sum += (hash[i]);
        }
        int l1, l2, l3;
        l1 = sum % key1;
        l2 = sum % key2;
        l3 = sum % key3;
        sprintf(buf, "%d%d%d", l1, l2, l3);
        return buf;
}

static void * lookupSymboAddress(const char *symbolName){
    void * ret = NULL;
    if(symbolName != NULL){
        bool ok = NSIsSymbolNameDefined(symbolName);
        if(ok){
            NSLog(@"Found");
            NSSymbol symb = NSLookupAndBindSymbol(symbolName);
            ret = NSAddressOfSymbol(symb);
            if(ret != NULL){
                NSLog(@"OK");
            }
        }else{
            NSLog(@"NO");
        }

    }
    return ret;
}

int main (int argc, const char * argv[])
{

    // Name: checkLicenseKey__14LicenseCheckerPCcb Linux
    // Name: __ZN14LicenseChecker15checkLicenseKeyEPKcb Mac OS X
        // Type: int checkLicenseKey(char const *,bool)
        typedef int (*CheckFunc)(char const *str, int b);
        // Name: isChecksumValid__14LicenseCheckerPCc   Linux
    // Name: __ZN14LicenseChecker15isChecksumValidEPKc Mac OS X
        // Type: int isChecksumValid(char *s)
        typedef int (*IsChecksumValid)(char *s);
        // Name: clearCRC32__FPUi         Linux
    // Name: __Z10clearCRC32Pj        Mac OS X
        // Type: int clearCRC32(char *s)
        typedef int (*ClearCRC32)(unsigned int *crcValue);
        // Name: addCrc32b__FPUiPCci   Linux
    // Name: __Z9addCrc32bPjPKci   Mac OS X
        // Type: int addCrc32b(unsigned int *, char const *, int)
        typedef int (*AddCrc32b)(unsigned int * crcValue, char const *str, int len);
        // Name: getCRC32__FPUiPCUi   Linux
    // Name: __Z8getCRC32PjPKj    Mac OS X
        // Type: int getCRC32(unsigned int        *, unsigned int        const *)
        typedef int (*GetCRC32)(unsigned int *crcValue,
                            unsigned int const *oldCRCValue);
        // Name: calculateChecksum__14LicenseCheckerUiPcb      Linux
    // Name: __ZN14LicenseChecker17calculateChecksumEjPcb  Mac OS X
        // Type: int calculateChecksum(unsigned int, char *, bool)
        typedef int (*CalculateChecksum)(unsigned int crcValue, char *content, int b);
   
    char *nativeKeyForJ6[] = {
        "__jprofiler_static_tt6",
        "$jprofiler6__cleanup"
    };
   
    char *nativeKeyForJ7[] = {
        "__jpmmp_dynamic_tt8",
        "_$$$dynup_jps"
    };
   
    int javaCodeKeyForJ6[] = {7, 38, 11};
    int javaCodeKeyForJ7[] = {12, 4, 66};
   
    @autoreleasepool {
        
        char * lic = "A-J6-CRACKER#21781-\0";
        char *licType = "A";
        char *version = "6";
        char *user = "CRACKER";
        char *userID = "21781";
        char *expiredDays = "1000";
        switch (argc) {
            case 1:
                printf("Usage: jprofilercracker <licType> <version> <User> <userID> [expiredDays]");
//                return -1;
                break;
            case 2:
                licType = argv[1];
                break;
            case 3:
                licType = argv[1];
                version = argv[2];
                break;
            case 4:
                licType = argv[1];
                version = argv[2];
                user = argv[3];
                break;
               
            case 5:
                licType = argv[1];
                version = argv[2];
                user = argv[3];
                userID = argv[4];
                break;
            case 6:
                licType = argv[1];
                version = argv[2];
                user = argv[3];
                userID = argv[4];
                expiredDays = argv[5];
                break;
        }
        
        int totalLen = strlen(licType)+2+strlen(version)+1+strlen(user)+1+strlen(userID)+1;
        int isTrail = 0;
        if(strncmp(licType,"E",1) == 0 || strncmp(licType,"T",1) == 0 ){
            totalLen += (11 + strlen(expiredDays));
            isTrail = 1;
        }
        
        char *nkey1, *nkey2;
        
        int *jkey;
        
        if (strncmp(version, "6", 1) == 0) {
            nkey1 = nativeKeyForJ6[0];
            nkey2 = nativeKeyForJ6[1];
            jkey = javaCodeKeyForJ6;
        }else if (strncmp(version, "7", 1) == 0) {
            nkey1 = nativeKeyForJ7[0];
            nkey2 = nativeKeyForJ7[1];
            jkey = javaCodeKeyForJ7;
        }
        
        char *nl = (char*)(malloc(totalLen));
        sprintf(nl,"%s-J%s-%s#%s-",licType,version,user,userID);
        if(isTrail){
            // TODO Get Current time and add expired days and gen yyyy.mm.dd-days
            sprintf(nl,"%s-J%s-%s#%s-",licType,version,user,userID);
        }
        
        lic = nl;
        
        bool b = NSAddLibrary("./jprofiler.dylib");
        if(b){
            
            CheckFunc func = (CheckFunc) lookupSymboAddress(
                                               "__ZN14LicenseChecker15checkLicenseKeyEPKcb");
            IsChecksumValid isChecksumValid = (IsChecksumValid) lookupSymboAddress(
                                                                      "__ZN14LicenseChecker15isChecksumValidEPKc");
            ClearCRC32 clearCRC32 = (ClearCRC32) lookupSymboAddress("__Z10clearCRC32Pj");
            AddCrc32b addCrc32b = (AddCrc32b) lookupSymboAddress("__Z9addCrc32bPjPKci");
            GetCRC32 getCRC32 = (GetCRC32) lookupSymboAddress("__Z8getCRC32PjPKj");
            CalculateChecksum calculateChecksum = (CalculateChecksum) lookupSymboAddress("__ZN14LicenseChecker17calculateChecksumEjPcb");
            NSLog(@"OK!");
            
            if (func != NULL && isChecksumValid != NULL && clearCRC32 != NULL
                                && addCrc32b != NULL && getCRC32 != NULL) {
                printf("func found OK\nGen Key for %s\n", lic);
               
               
               
                if (isChecksumValid != NULL) {
                    char *dd = strrchr(lic, '-');
                    //                                printf("strrchr ret %s\n", dd);
                    int len = dd - lic;
                    //                                printf("sub ret %d\n", len);
                    unsigned int crcValue = 0;
                    unsigned int tempCRC = 0;
                    unsigned int tempCRC2 = 0;
                    //                                char *str2;
                    char var10[100];
                    char var20[100];
                    
                    clearCRC32(&crcValue);
                    addCrc32b(&crcValue, lic, len);
                    addCrc32b(&crcValue, nkey1, strlen(nkey1));
                    getCRC32(&tempCRC, &crcValue);
                    
                    //                                printf("tempCRC %d\n", tempCRC);
                    
                    clearCRC32(&crcValue);
                    addCrc32b(&crcValue, lic, len);
                    addCrc32b(&crcValue, nkey2, strlen(nkey2));
                    getCRC32(&tempCRC2, &crcValue);
                    
                    //                                printf("tempCRC2 %d\n", tempCRC2);
                    
                    calculateChecksum(tempCRC, var10, 1);
                    calculateChecksum(tempCRC2, var20, 1);
                    
                    //                                printf("var10 = %s\n",var10);
                    //                                printf("var20 = %s\n",var20);
                    
                    int v10len = strlen(var10);
                    int v20len = strlen(var20);
                    
                    int hashLen = v10len + v20len;
                    char * buf = (char*) (malloc(hashLen));
                    int i;
                    for (i = 0; i < v10len; i++) {
                        buf[i] = var10[i];
                    }
                    int j;
                    for (j = 0; j < v20len; j++) {
                        buf[v10len + j] = var20[j];
                    }
                    
                    char code[100];
                    getHash(buf, code, hashLen, jkey[0],jkey[1],jkey[2]);
                    
                    printf("license is : %s%s#%s\n", lic, buf, code);
                    
                    free(buf);
                }
            } else {
                printf("func not found\n");
            }
        }else{
            NSLog(@"Error!");
        }
        free(nl);
    }
    return 0;
}
雪    币: 328
活跃值: (39)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
vhly 5 2012-1-10 12:10
4
0
NSAddLibrary 等同于 dlopen

lookupSymboAddress 等同于 dlsym
雪    币: 96
活跃值: (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wkxq 2012-1-10 22:38
5
0
支持一下分析的不错
雪    币: 1015
活跃值: (235)
能力值: ( LV12,RANK:440 )
在线值:
发帖
回帖
粉丝
loongzyd 10 2012-1-13 09:12
6
0
先收藏,JAVA逆向学习材料啊。
雪    币: 12807
活跃值: (3242)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ytyay 2012-1-13 18:07
7
0
收藏学习   谢谢分享
雪    币: 437
活跃值: (110)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
tornodo 1 2012-1-14 12:27
8
0
来份Windows的就更好了
雪    币: 328
活跃值: (39)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
vhly 5 2012-1-14 12:58
9
0
关键在于有几个方法,之前尝试进行实现,总是和原始的计算出来的数据不同,所以就采用这种方式,如果能够分析出数值计算的方法,那就可以直接写注册机了
雪    币: 46
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiaocaijk 2012-1-16 21:57
10
0
切操作系统 跟切菜一样~~ lz是用虚拟机 还是有什么好的方法?
雪    币: 190
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dooza 2012-2-7 22:35
11
0
期待来个windows的keygen啊
游客
登录 | 注册 方可回帖
返回