Hotaru's Notebook

Markdown Navigator(Multi Markdown) for IntelliJ IDEA 破解

Preface

一直以来我都在寻找一款功能齐全 Markdown 编辑器,目前功能最全的是一款叫做 MarkdownPad 的编辑器,可惜的是它只能运行在Windows上。后来在 IDEA 的插件市场找到了这款Markdown编辑器,叫做 Markdown Navigator(原名为 Multi Markdown),插件基本功能免费,一些特别的功能(比如 开启Markdown预览)就需要购买许可了,于是干脆把它也破掉吧。

开工

先弄个试用版的许可证激活一下看看

插件原价为 $19.99/yr,直接到 这里 填好相关资料就可以拿到15天的全功能试用许可证了。 然后对 LicenseAgent 类的 getLicenseCode(LicenseRequest licenseRequest)ZisValidActivation()Z 下断点,然后调试该插件。 调试时,打开 被调试的IDEA的菜单栏 -> File -> Settings -> Languages & Frameworks -> Markdown,输入许可证,然后观察LicenseAgent类里的代码执行过程,得到下面的大致激活流程。

大致的激活流程

插件先拿着试用版激活码调用 LicenseAgent.getLicenseCode(LicenseRequest)Z,这会从服务器拿到激活响应以json体现:

{
  "status":"LicenseAgent.STATUS_*",
  "message":"比如激活失败就会用这个来告诉用户.",
  "activation_code":"激活响应信息",
  "license_code":"猜测:服务器要求插件更新许可证用的字段,不一定每次都有."
}

下表描述上面的json里key对应的java字段:

key java field
status LicenseAgent.STATUS
message LicenseAgent.MESSAGE
activation_code LicenseAgent.ACTIVATION_CODE
license_code LicenseAgent.LICENSE_CODE

下表描述上面的json里 status 的值对应的java字段:

status java field 含义
ok LicenseAgent.STATUS_OK 激活成功
error LicenseAgent.STATUS_ERROR 激活失败
disable LicenseAgent.STATUS_DISABLE 服务器要求删除本地许可信息

然后把 activation_codelicense_code(如果存在的话)一并存入到LicenseAgent的 activation_codelicense_code 字段里.

之后再调用 LicenseAgent.isValidActivation()Z,逻辑大概这样:

// activation_code:String, 服务器响应的加密后的json字符串.
// activation:JsonObject, 解密后的 activation_code.
public boolean isValidActivation() {
    if (activation_code != null) { // 某些原因导致没能拿到加密的激活信息.
        if (activation == null) { // json字符串还未解密成json对象, activation 才会null.
            activation = 转为JSON对象(解密(activation_code));
        }
        
        if (activation != null activation.agent_signature.equals(LicenseAgent.agent_signagure)) {
            取出json里的信息;// 比如激活时间、过期时间、激活了哪些高级特性。
            return true;// 返回激活成功.
        }
    }
    return false;// 返回激活失败.
}

下面的代码为 activation json对象里的相关字段信息:

{
  "product_name": "idea-multimarkdown",
  "product_version": "2.0.0",
  "agent_signature": "475f99b03f6ec213729d7f5d577c80aa",// LicenseAgent.agent_signature
  "host_name": "电脑或者主机的名称",
  "host_product": "IDEA的build号",
  "license_expires": "2016-08-22", // 许可证过期日期
  "license_type": "trial",/* 许可类型, LicensedFeature.Feature */
  "license_features": 1,/* LicensedFeature.Feature (暂定) */
  "feature_list": {/* 开启了哪些高级特性, MultiMarkdownPlugin.FEATURE_*; */
    "enhanced": 1,
    "development": 2
  },
  "activated_on": "2016-08-07", // 激活日期
  "activation_expires": "2016-08-22" // 过期日期
}

LicenseAgent

直接定位到 LicenseAgent 类然后进行如下修改:

public class LicenseAgent {
    public LicenseAgent() {// 启用各种付费后的特性
        // MultiMarkdownPlugin.FEATURE_*
        featureList = new HashMap<>();
        featureList.put("enhanced", 1);
        featureList.put("development", 2);
    }

    public LicenseAgent(LicenseAgent other) {
        this();// 这个构造函数里也要调用一下.
        updateFrom(other);
    }
    
    public void setLicenseCode(String license_code) {
        // 置空
    }
    
    public void setLicenseActivationCodes(String license_code, String activation_code) {
        // 置空
    }
    
    public void setActivationCode(String activation_code) {
        // 置空
    }
    
    public String getLicenseExpires() {
        return "2999-12-31";// 改成2999年才过期
    }
    
    // 该方法会用之前申请到的试用版许可证向服务器请求激活信息
    public boolean getLicenseCode(LicenseRequest licenseRequest) {
        return true;
    }
    
    public boolean isValidLicense() {
        return true;
    }
    
    public boolean isValidActivation() {
        return true;
    }
    
    public String getLicenseType() {
        return "license"; // LicensedFeature.Feature.LICENSE;
    }
    
    public int getLicenseFeatures() {
        // 这个返回值暂定为这个,因为编译后 插件认为激活成功。
        return LicensedFeature.Feature.LICENSE.getLicenseFlags();
    }
    
    public String getLicenseExpiration() {
        return "2999-12-31";
    }
    
    public String getActivatedOn() {
        return "1900-01-01";
    }
    
    public int getLicenseExpiringIn() {
        return Integer.MAX_VALUE;
    }
    
    public boolean isActivationExpired() {
        return false;
    }
    
    // 其余的上面没提到的方法暂时都不用管.
}

到目前为止,破解工作已经完成了。我发现 LicenseRequest 类会收集一点系统信息,于是干脆把它也搞定吧。

LicenseRequest

方法很简单:在构造函数里把所有对 host_ 开头的字段全都改成别的就行了。

总结

编译代码并替换原版插件jar里对应的类,然后替换掉IDEA的原版jar,然后重启IDEA,破解完成。

References:

  1. http://www.jianshu.com/p/b70e250bed37

#Intellij IDEA #CRACK #Markdown #Multi Markdown #Markdown Navigator