Java逆向环境搭建
安装JDK
首先安装JDK。目前JDK版本为12.0.1
。逆向时有个工具需要jdk8
环境,,jdk8
已经停止维护不能免费下载,需要注册用户才能下载。
使用brew
安装jenv
,管理JDK版本。
|
|
安装反编译工具
此处推荐两个工具:
其中,Luyten
仅支持jdk8
环境,jdk12
打开会闪退。因此在安装之后编辑启动脚本,将环境切换设置在脚本#!/bin/bash
之后:
|
|
逆向license
使用Luyten
打开atlassian-extras-3.1.2.jar
,导航到com.atlassian.license.decoder.v2
,打开Version2LicenseDecoder.class
:
|
|
我们看到公钥被硬编码在程序里面,替换或hook都可以达到修改的目的。
下面分析解码license的代码:
|
|
流程如下:
-
从
licenseString
中去掉空白字符;1 2 3 4 5 6 7 8 9 10 11 12 13
private static String removeWhiteSpaces(final String licenseData) { if (licenseData == null || licenseData.length() == 0) { return licenseData; } final char[] chars = licenseData.toCharArray(); final StringBuffer buf = new StringBuffer(chars.length); for (int i = 0; i < chars.length; ++i) { if (!Character.isWhitespace(chars[i])) { buf.append(chars[i]); } } return buf.toString(); }
-
在
licenseString
中,从最后一个’X’的位置后3位开始到licenseString
结束,该值以31进制转码为int
类型,是license的长度,从licenseString
头部取出该长度的string;1 2 3 4 5 6 7 8 9
private String getLicenseContent(final String licenseString) { final String lengthStr = licenseString.substring(licenseString.lastIndexOf(88) + 3); try { final int encodedLicenseLength = Integer.valueOf(lengthStr, 31); return licenseString.substring(0, encodedLicenseLength); } catch (NumberFormatException e) { throw new LicenseException("Could NOT decode license length <" + lengthStr + ">", e); } }
-
使用
base64
转码第二步返回的string。前4byte定义了一个无符号整数,大端对齐,此处定义了License
文本内容的长度,以此长度为分隔,前面是压缩后的License
文本内容,后面是对文本内容进行签名的hash,公钥去验证License
签名是否正确;1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
private byte[] checkAndGetLicenseText(final String licenseContent) { byte[] licenseText; try { final byte[] decodedBytes = Base64.decodeBase64(licenseContent.getBytes()); final ByteArrayInputStream in = new ByteArrayInputStream(decodedBytes); final DataInputStream dIn = new DataInputStream(in); final int textLength = dIn.readInt(); licenseText = new byte[textLength]; dIn.read(licenseText); final byte[] hash = new byte[dIn.available()]; dIn.read(hash); try { final Signature signature = Signature.getInstance("SHA1withDSA"); signature.initVerify(Version2LicenseDecoder.PUBLIC_KEY); signature.update(licenseText); if (!signature.verify(hash)) { throw new LicenseException("Failed to verify the license."); } } catch (InvalidKeyException e) { throw new LicenseException(e); } catch (SignatureException e2) { throw new LicenseException(e2); } catch (NoSuchAlgorithmException e3) { throw new LicenseException(e3); } } catch (IOException e4) { throw new LicenseException(e4); } return licenseText; }
-
跳过
licenseText
前5个byte,将后面内容解压,输出以UTF-8
编码的license
内容;1 2 3 4 5 6 7 8 9 10
private Reader unzipText(final byte[] licenseText) { final ByteArrayInputStream in = new ByteArrayInputStream(licenseText); in.skip(Version2LicenseDecoder.LICENSE_PREFIX.length); final InflaterInputStream zipIn = new InflaterInputStream(in, new Inflater()); try { return new InputStreamReader(zipIn, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new LicenseException(e); } }
-
最后通过明文的
license
实例化证书。1 2 3 4 5 6 7 8 9
private Properties loadLicenseConfiguration(final Reader text) { try { final Properties props = new Properties(); new DefaultPropertiesPersister().load(props, text); return props; } catch (IOException e) { throw new LicenseException("Could NOT load properties from reader", e); } }
使用node实现证书解析
按照之前的分析写代码就可以了,实现如下:
|
|