Android逆向实战HOOK解析(frida篇)
集合中的properties类似是键值对的数据类型,通过用于获取文件输入输出流
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class Hello {
public static void main(String[] args) {
String currentWorkingDir = System.getProperty("user.dir");//获取本地工作区path
String filePath = currentWorkingDir + File.separator + "demo.properties";//添加'\'以及文件名
System.out.println(filePath);
Properties properties = new Properties();
//创建相应的对象
try (FileInputStream fis = new FileInputStream(filePath)) { //通过路径去获取路径去获取程序的输入流
properties.load(fis); //通过对象去读取加载程序的输入流
String className = properties.getProperty("class");
String methodName = properties.getProperty("method");
System.out.println(className + " " + methodName);
} catch (IOException e) {
System.err.println("无法读取属性文件: " + e.getMessage());
}
}
}
这里利用反射我们将原本利用转载class类名和函数的文件进行载入,通过读取类名以及函数发方法名进行直接的执行函数方法
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class Hello {
public static void main(String[] args) {
String currentWorkingDir = System.getProperty("user.dir");
String filePath = currentWorkingDir + File.separator + "demo.properties";
System.out.println(filePath);
Properties properties = new Properties();
try (FileInputStream fis = new FileInputStream(filePath)) {
properties.load(fis);
String className = properties.getProperty("class");
String methodName = properties.getProperty("method");
System.out.println(className + " " + methodName);
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod(methodName);
// 因为是静态方法,所以第一个参数为null,第二个参数是方法的参数数组,这里为空数组
method.invoke(null, (Object[]) new Object[0]);
} catch (IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
System.err.println("无法读取属性文件或调用方法: " + e.getMessage());
}
}
}
class chen_chen_chen {
public static void test0() {
System.out.println("Hello, Chen_chen_chen!");
}
public static void test1() {
System.out.println("Hello, test1");
}
}
demo.properties:
class=chen_chen_chen
method=test0 or method=test1
class类对象的获取
Class<?> clazz= Class.forName(className);//通过路径获取类
Class<?> class1=chen_chen_chen.class;//通过类名去获取类
Class<?> class2=new chen_chen_chen().getClass();//通过对象去获取类
下面两个基本没必要
Class<?> class3=chen_chen_chen.class.getClassLoader().loadClass(className);//通过类名去利用ClassLoader的loadClass获取类
Class<?> class4=newchen_chen_chen().getClass().getClassLoader().loadClass(className);
class不同类的获取
Class<?> clazz= Class.forName(class$子类(或者是数字,应该于class中的第几个内部class)(内部类));//获取class内部类
通过反射对于属性进行获取数据
Field nameField = chen_chen_chenClass.getField("name");
String name =(String)nameField.get(o2);
System.out.println("name = " + name);
Field ageField = chen_chen_chenClass.getField("age");
int age =(int)ageField.get(o2);
System.out.println("age = "+age);
反射调用方法:
Method method = chen_chen_chenClass.getMethod("test", String.class);//函数名以及类类型
String string = (String)method.invoke(o2,"nihao_work");//传参
System.out.println(string);
反射获取父类
Class<?>[] Use = chen_chen_chenClass.getInterfaces();
System.out.println(Use.length);
System.out.println(Use[0]);
反射遍历类的属性/同理将Fields修改为Method就可以遍历类的方
Class<?> chen_chen_chenClass = Class.forName("chen_chen_chen");
Field [] fields = chen_chen_chenClass.getDeclaredFields();
for(Field field:fields){
System.out.println(field);
}
利用匿名类去重写OnClick方法实现button控件的使用(获取并取得控件对象的方法)
findViewById同样可以获得控件对象
binding = ActivityMainBinding.inflate(getLayoutInflater());//创建绑定实例
setContentView(binding.getRoot());//利用视图来获取创建获取布局的根视图
Button button1 = binding.button1;//面对对应的视图创建相应的对象
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("nihao","123456");
}
});
==================================
重写onclick类
button1.setOnClickListener(new MyClick()); //申请一个类实现我们点击触发的事件
class MyClick implements View.OnClickListener {
@Override
public void onClick(View view) {
Log.d("xiaojianbnag", "onClick");
}
}
===================================
在本类中去实现我们的鼠标点击的事件处理
button1.setOnClickListener(this)//由于是在本类中,直接申请本类
public void onClick(View view) {
switch (view.getId()) {
case R.id.button1:
Log.d("xiaojianbnag", "onClick");
break;
}
}
Toast土司提示框
Toast.makeText(MainActivity.this,"按钮点击",Toast.LENGTH_SHORT).show();
as中的字符串通常不会直接应用,而是通过在res——>valuse里面去获取值。
对于这样的数据,我们可以利用jeb在values——>public.xml里面去找到对应的值,之后找到了name之后利用name——>在strings.xml中去找到string
build.gradle
会记录该程序的执行的相关SDK版本,获取是相关SDK能够接受的最大或最小版本,以及相关的链接的libc库等相关的信息
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "com.chen.demo"
compileSdk = 34
defaultConfig {
applicationId = "com.chen.demo"
minSdk = 24 //版本
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags += "-std=c++11" //使用的cpp
}
}
}
buildTypes {
release {
isMinifyEnabled = false //是否使用混淆
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
buildFeatures {
viewBinding = true
}
}
dependencies {
//链接的库
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}
AndroidManifest
会包含相应的Android程序的相关信息,比如入口函数的名称,所使用的主题,或者所使用的图标....
假如这里设置了 android:name=".Myapplicaton" 那么函数的执行情况就是先Myapplicaton再MainActivity
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Demo"
tools:targetApi="31"
android:name=".Myapplicaton"
>
<activity
android:name=".MainActivity"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
遍历HashMap
import java.util.HashMap;
import java.util.Set;
public class one11 {
public static void main(String[] args) {
HashMap<Object , Object> objectHashMap = new HashMap<Object, Object>();
objectHashMap.put("key1", "value1");
objectHashMap.put("key2", "value2");
objectHashMap.put("key3", "value3");
objectHashMap.put("key4", "value4");
Set<Object> set = objectHashMap.keySet();//通过set去获取键对象存入列表中
for (Object key : set) {
System.out.println("Key: " + key + ", Value: " + objectHashMap.get(key));
}
}
}
#
Key: key1, Value: value1
Key: key2, Value: value2
Key: key3, Value: value3
Key: key4, Value: value4
通过包名去获取程序的方法中遇到构造函数以及主动调用
var bBase64 = Java.use("android.util.Base64")//这里获取了程序中的Base64类,最后主动调用并输出
var DESKeySpec1 = Java.use("javax.crypto.spec.DESKeySpec")
DESKeySpec1.$init.overload('[B').implementation=function(a){//通过$init 来调用构造函数
console.log("DESKeySpec1 arg:",bBase64.encodeToString(a,0));
this.$init(a);
}
对于我们的Hook过程,我们可以通过主动调用去判断是否有重写错位frida过程中的方法
var requestUtil = Java.use("com.dodonew.online.http.RequestUtil");
requestUtil.encodeDesMap.overload('java.lang.String', 'java.lang.String', 'java.lang.String').implementation = function(a,b,c){
console.log("encodeDesMap arg1:",a);
console.log("encodeDesMap arg2:",b);
console.log("encodeDesMap arg3:",c);
var result = this.encodeDesMap(a,b,c);
console.log("encodeDesMap result:",result);
return result;
}
requestUtil.encodeDesMap("nihao", "123456","7891011");//直接调用,看是否有参数值被打印
通过HOOK方法来实现关键函数定位
利用Log.getStackTraceString()方法来打印堆栈
正常在安卓端可以通过LogOutPut(Log.getStackTraceString(new Throwable()))来打印堆栈,我们通过frida利用js来实现注入
function showstack(){
console.log(
Java.use("android.util.Log").getStackTraceString(
Java.use("java.lang.Throwable").$new()
)
);
}//同理Log.getStackTraceString(new Throwable())
var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印
hashMap.put.implementation = function(key, value) {
if(key.equals("username")||key.equals("userPwd"))//条件判断
{
showstack();
console.log("key " + key + ", value: " + value);
}
return this.put(key, value);
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
同理去利用HOOKArrayList
var arrayList = Java.use("java.util.ArrayList");
arrayList.add.overload('int', 'java.lang.Object').implementation=function(key, value) {
// showstack()
console.log(".add.overload('int', 'java.lang.Object') key " + key + ", value: " + value);
return this.add(key, value);
}
arrayList.add.overload('java.lang.Object').implementation=function(key) {
// showstack()
console.log("arg:.overload('java.lang.Object')" + key);
return this.add(key);
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
利用isEmpty()方法来实现关键函数定位
var textUtils = Java.use("android.text.TextUtils")
textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {
console.log("arg:.implementation:" + a);
return this.isEmpty(a);
------------------------------------------------------------------------------
------------------------------------------------------------------------------
利用collections.sort()来实现关键函数定位
var collections = Java.use("java.util.Collections")
collections.sort.overload('java.util.List').implementation=function(a){
var arrayList = Java.cast(a,Java.use("java.util.ArrayList"));//向下转型
console.log("arg:.implementation:" + arrayList.toString());
return this.sort(a);
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
利用JSONObject来实现
var JSONObject = Java.use("org.json.JSONObject");
JSONObject.put.overload('java.lang.String', 'java.lang.Object').implementation=function(a,b){
console.log("JSONObject.put:" + a );
console.log("arg:.implementation:" +b);
return this.put(a,b);
}
JSONObject.getString.overload('java.lang.String').implementation=function(a){
console.log("JSONObject.getString:" + a);
return this.getString(a);
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
土司Toast
var Toast = Java.use("android.widget.Toast");
Toast.show.implementation=function(){
console.log("Toast.show");
return this.show;
Base64.encodeToString方法
------------------------------------------------------------------------------
------------------------------------------------------------------------------
var base64 = Java.use("android.util.Base64");
base64.encodeToString.overload('[B', 'int').implementaiton = function(a,b){
showstack();
console.log("Base64.encode",JSON.stringify(a));
var result =this.encodeToString(a,b);
console.log("result:"+result);
return result;
------------------------------------------------------------------------------
------------------------------------------------------------------------------
getBytes
str.getBytes.overload('java.lang.String').implementation=function(a){
showstack();
var result = this.getBytes(a);
var newStr = str.$new(result);
console.log(newStr);
系统源码对于字符串构造函数的处理方式
当我们想要通过字符串来实现关键函数的定位时,我们就需要去HOOK字符串的构造函数,了解字符串是在什么时候进行构造和初始化的
查看一下系统源码对于字符串的处理:
public String() {
throw new RuntimeException("Stub!");
}
public String(@RecentlyNonNull String original) {
throw new RuntimeException("Stub!");
}
public String(char[] value) {
throw new RuntimeException("Stub!");
}
public String(char[] value, int offset, int count) {
throw new RuntimeException("Stub!");
}
public String(int[] codePoints, int offset, int count) {
throw new RuntimeException("Stub!");
}
/** @deprecated */
@Deprecated
public String(byte[] ascii, int hibyte, int offset, int count) {
throw new RuntimeException("Stub!");
}//还有很多重载的string函数
我们就不去一一例举完String函数了,我们可以看到的是所以的string函数都是直接抛出了一个错误
其实字符串是通过StringFactory()函数来实现字符串构造的
利用StringFactory()方法实现HOOK
var Stringfactory = Java.use("java.lang.StringFactory");
Stringfactory.newStringFromString.overload('java.lang.String').implementation = function(a){
var result = this.newStringFromString(a);
console.log("Stringfactory.newStringFromString:"+result);
return result;
}
Stringfactory.newStringFromChars.overload('[C').implementation= function(a){
showstack();
var result = this.newStringFromChars(a);
console.log("Stringfactory.newStringFromChars:"+result);
return result;
}
关于字符串拼接的底层原理
我们在去利用字符串拼接来实现关键代码HOOK的时候,我们要去了解字符串拼接底层的实现过程,才能去真正的理解我们需要HOOK的函数
static String simpleConcat(Object first, Object second) {
String s1 = stringOf(first);
String s2 = stringOf(second);
if (s1.isEmpty()) {//检查两个字符串是否为空
// newly created string required, see JLS 15.18.1
return new String(s2);
}
if (s2.isEmpty()) {
// newly created string required, see JLS 15.18.1
return new String(s1);
}
// start "mixing" in length and coder or arguments, order is not
// important
long indexCoder = mix(initialCoder(), s1);
indexCoder = mix(indexCoder, s2);
//这两步是根据两个字符串的长度相加得到要创建newArray的大小,所以其实拼接的时候还是按照数组来实现的
byte[] buf = newArray(indexCoder);
// prepend each argument in reverse order, since we prepending
// from the end of the byte array
indexCoder = prepend(indexCoder, buf, s2);
indexCoder = prepend(indexCoder, buf, s1);
//这两步将申请的array逐步的按照每个字符串的大小进行赋值
return newString(buf, indexCoder);
//最后调用newString来实现真正的构造字符串
我们可以看到的是,其实底层是利用数组array来拼接,最后调用出newString来实现真正的新字符串的创建。
利用这一点,我们就可以去HOOK newString()方法来实现通过字符串拼接过程中的HOOK代码,其中拼接过程中也会调用StringBuilder、StringBuffer方法,所以HOOK这两个系统函数也同样可以。
var sbuilder = Java.use("java.lang.StringBuilder");
sbuilder.toString.implementation = function()
{
var result = this.toString();
if(result.indexOf("Encrypt")!=-1)
{
showstack();
}
console.log("StringBuilder.toString:"+result);
return result;
}
var sbuffer = Java.use("java.lang.StringBuffer");
sbuffer.toString.implementation = function()
{
var result = this.toString();
if(result.indexOf("Encrypt")!=-1)
{
showstack();
}
console.log("StringBuffer.toString:"+result);
return result;
}
/*StringBuilder.toString:{"Encrypt":"NIszaqFPos1vd0pFqKlB42Np5itPxaNH\/\/FDsRnlBfgL4lcVxjXii\/UNcdXYMk0E1xRbP6wYR3bc\nqUsCY5VtJheBR6Vch9pVChB27DkdOh5kJ\/BCfnDkinSNzhb+4F5XRxXIiouW0HYNPUY8PfugWoiG\nJJLaSkJd0xTfYWoFaaoi2D4CDfqfwEgtz0EHpAWAAxWMUaUS\/6ZZ4IB\/NAR7CiHHriMnUXZR\n"}
StringBuilder.toString:java.lang.StringBuilder.toString(Native Method)
StringBuilder.toString: at java.lang.StringBuilder.toString(Native Method)
StringBuilder.toString:com.dodonew.online.http.JsonRequest.addRequestMap(JsonRequest.java:117)
StringBuilder.toString: at com.dodonew.online.http.JsonRequest.addRequestMap(JsonRequest.java:117)
StringBuilder.toString:com.dodonew.online.ui.LoginActivity.requestNetwork(LoginActivity.java:161)
StringBuilder.toString: at com.dodonew.online.ui.LoginActivity.requestNetwork(LoginActivity.java:161)
StringBuilder.toString:com.dodonew.online.ui.LoginActivity.login(LoginActivity.java:134)
StringBuilder.toString: at com.dodonew.online.ui.LoginActivity.login(LoginActivity.java:134)
StringBuilder.toString:com.dodonew.online.ui.LoginActivity.onClick(LoginActivity.java:103)
StringBuilder.toString: at com.dodonew.online.ui.LoginActivity.onClick(LoginActivity.java:103)
StringBuilder.toString:android.view.View.performClick(View.java:7140)
StringBuilder.toString: at android.view.View.performClick(View.java:7140)
StringBuilder.toString:android.view.View.performClickInternal(View.java:7117)
StringBuilder.toString: at android.view.View.performClickInternal(View.java:7117)
StringBuilder.toString:android.view.View.access$3500(View.java:801)
StringBuilder.toString: at android.view.View.access$3500(View.java:801)
StringBuilder.toString:android.view.View$PerformClick.run(View.java:27351)
StringBuilder.toString: at android.view.View$PerformClick.run(View.java:27351)
StringBuilder.toString:android.os.Handler.handleCallback(Handler.java:883)
StringBuilder.toString: at android.os.Handler.handleCallback(Handler.java:883)
StringBuilder.toString:android.os.Handler.dispatchMessage(Handler.java:100)
StringBuilder.toString: at android.os.Handler.dispatchMessage(Handler.java:100)
StringBuilder.toString:android.os.Looper.loop(Looper.java:214)
StringBuilder.toString: at android.os.Looper.loop(Looper.java:214)
StringBuilder.toString:android.app.ActivityThread.main(ActivityThread.java:7356)
StringBuilder.toString: at android.app.ActivityThread.main(ActivityThread.java:7356)
StringBuilder.toString:java.lang.reflect.Method.invoke(Native Method)
StringBuilder.toString: at java.lang.reflect.Method.invoke(Native Method)
StringBuilder.toString:com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
StringBuilder.toString: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
StringBuilder.toString:com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
StringBuilder.toString: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
StringBuffer.toString:java.lang.Throwable
*/
利用enumerateLoadedClassesSync来枚举出已经加载之后的类名
var classArr = Java.enumerateLoadedClassesSync();
for(var i=0; i<classArr.length; i++)
{
console.log("classname",classArr[i]);
}//程序中app的类可能会改变,我们要通过枚举之后来判断
通过枚举出来的类,我们找到了控制findViewById的参数控件的类"android.support.v7.app.AppCompatActivity"通过hook进一步来实现控件ID的输出,最后比对我们需要的控件ID,来实现showstack()
var appCompatActivity=Java.use("android.support.v7.app.AppCompatActivity");
console.log(appCompatActivity);
appCompatActivity.findViewById.implementation=function(a)
{
console.log("findViewB yId:"+a);
if(a==btn_login_id)
{
showstack();
console.log("findViewById btn_login_id------stack()");
}
return this.findViewById(a);
}
这里我们需要注意:我们假如去hook控件,要使用frida开启 -f 来自启动app,而不是我们来打开,因为控件在我们程序启动出现界面之前就完成了初始化和操作,所以要使用frida来自启动程序
frida -U -f com.dodonew.online -l .\test1.js
-f FILE, --file=FILE spawn FILE
-F, --attach-frontmost //要取消-F 选择-f
attach to frontmost application
setOnClickListener()方法用于实现点击事件的处理。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("nihao","123456");
}//对于按钮button1来进行事件处理
所以我们可以Hook这个setOnClickListener()方法来实现我们的过程。
函数参数的修改以及返回值的修改
var Money = Java.use("com.xiaojianbang.hook.Money")
var str =Java.use("java.lang.String")
Money.getInfo.implementation=function(){
var result = this.getInfo();
console.log("Money.getInfo.implementation",result);
//return result
//return "chen_chen_chen";
return str.$new("chen_chen_chen");//这里本来应该返回result,但是我们修改了返回值,并且由于我们应该返回的是java类型的字符串,所以调用了java的类包来实现的。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
var Money = Java.use("com.xiaojianbang.hook.Money");
var Wallet = Java.use("com.xiaojianbang.hook.Wallet");
Wallet.deposit.implementation=function(a){
console.log("Wallet.deposit.implementation",a.getInfo());
return this.deposit(Money.$new("美元",200));
}//同理
对于可能遇到自定义的类的参数,由java转为了js之后的打印问题
由于可能是自定义的类,对于方法没有进行重写toString()方法,从而导致打印不出对于的参数,这里以map类型的HashMap为例,让我们能够得到正确的输出结果。
var StringBuild=Java.use("java.lang.StringBuilder");
var Utils= Java.use("com.xiaojianbang.hook.Utils");
Utils.shufferMap.implementation=function(a){
// console.log("Utils.shufferMap.implementation",a.toString());
var set = a.keySet();
var it =set.iterator();
var result = StringBuild.$new();
while(it.hasNext()) {
var key = it.next();
var value = a.get(key);
result.append(value);
}
console.log("Utils.shufferMap.implementation",result.toString());
return this.shufferMap(a);}
}
面对于多个参数和多个重载函数的时候,我们应该怎么去做到一次性进行Hook,就可以直接得到结果
var Utils = Java.use("com.xiaojianbang.hook.Utils");
var overloadarr = Utils.getCalc.overloads;//获取overload的数组
for (var i=0; i< overloadarr.length; i++)//遍历数组
{
overloadarr[i].implementation=function()
{
var params="";
for(var j=0;j<arguments.length;j++)//利用js中的arguments方法获取参数
{
params += "arg"+j+":"+arguments[j]+"\n";
};
console.log("Utils.getCalc.overloads["+i+"]-----",params);
return this.getCalc.apply(this,arguments)//利用apply()方法来实现call原函数 apply(object,参数数组)
}
}
遍历多个属于同一个类的成员中的方法
var Money =Java.use("com.xiaojianbang.hook.Money")
Money.$new("日元",500000);//主动调用
Java.choose("com.xiaojianbang.hook.Money", //获取创建了Money类中的所有成员
{
onMatch: function(obj)
{
console.log(obj.getInfo());//调用getInfo()
},
onComplete: function()
{
console.log("done");//匹配完成之后的输出
}
}
)
赋值和修改类的字段
//static静态变量修改--直接获取class可以直接访问
var Money =Java.use("com.xiaojianbang.hook.Money");
console.log("Money.flag1",Money.flag.value);
Money.flag.value = "日元";
console.log("Money.flag2",Money.flag.value);
-------------------------------------------------------------------
-------------------------------------------------------------------
//通过利用Java.choose去实现获取类成员进行修改
var Money =Java.use("com.xiaojianbang.hook.Money");
Java.choose("com.xiaojianbang.hook.Money",{
onMatch: function(obj)
{
console.log("money.currency:"+obj.currency.value);
},
onComplete:function()
{
console.log("done");
}
})
Java.choose("com.xiaojianbang.hook.BankCard",{
onMatch: function(obj)
{
console.log("BankCard.currency:"+obj._accountName.value);//这里类中的方法名和变量名相同,要加上'_'
},
onComplete:function()
{
console.log("done");
}
})
匿名内部类的Hook
var MainActivity$1 = Java.use("com.xiaojianbang.app.MainActivity$1")
MainActivity$1.getInfo.implementation = function() {
var result = this.getInfo();
console.log("MainActivity$1.getInfo.implementation",result);
return result;
}
主动调用AndriodStdio里面的base64.decode()解密base64字符串,实现混淆函数HOOK
var base64 = Java.use("android.util.Base64");
var Money = Java.use("com.xiaojianbang.hook.Money");
var String = Java.use("java.lang.String");
console.log(base64.decode("Z2V0SW5mbw==",0));//由于decode之后的结果是 byte[]类型,需要进行转置为String 带入Money[]
console.log(String.$new(base64.decode("Z2V0SW5mbw==",0)))
Money[String.$new(base64.decode("Z2V0SW5mbw==",0))].implementation = function() {//通过Money[方法变量]方式访问 同理为Money.getInfo()
var result = this.getInfo()
console.log("Money.getInfo.implementation",result);
return result;
}
dex混淆保护,通过对于原始dex进行读取,然后进行加密获取新的dex文件,实现混淆保护
try {
File oldDexFile = new File("/data/data/com.xiaojianbang.testdex/classes.dex");
File newDexFile = new File("/data/data/com.xiaojianbang.testdex/new_classes.dex");
Log.d("xiaojianbang", "从assets中读取classes.dex...");
InputStream ins = getApplicationContext().getAssets().open("classes.dex");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int index;
while ((index = ins.read(bytes)) != -1)
baos.write(bytes, 0, index);
Log.d("xiaojianbang", "开始写出classes.dex到" + oldDexFile.toString() + "...");
FileOutputStream fos = new FileOutputStream(oldDexFile);
fos.write(baos.toByteArray());
ins.close();
fos.close();
baos.close();
---------------------------------------------------------------------------------------------------
//以上是读取dex,设置读取完成之后的存储位置
Log.d("xiaojianbang", "开始用dexlib2加载classes.dex...");
Opcodes opcodes = Opcodes.forApi(29);
DexFileFactory.writeDexFile(oldDexFile.toString(), DexFileFactory.loadDexFile(oldDexFile, opcodes));//加载DEX文件,并将其转换为内部表示形式,以便进行进一步操作
DexBackedDexFile dexBackedDexFile = DexFileFactory.loadDexFile(oldDexFile, opcodes);//用于调试或验证DEX文件是否能被正确加载和写入
Log.d("xiaojianbang", "dexFile: " + dexBackedDexFile.getClass().getName());
DexRewriter rewriter = new DexRewriter(new RewriterModule() {
@Override
public Rewriter<ClassDef> getClassDefRewriter(Rewriters rewriters) {
//传递一个匿名RewriterModule实例来定义重写逻辑
return new ClassDefRewriter(rewriters) {
@Override
public ClassDef rewrite(ClassDef classDef) {
String className = classDef.getType();
if (className.contains("com/xiaojianbang")) {
//检查类名是否包含特定字符串(如"com/xiaojianbang")
return new RewrittenClassDef(classDef) {
@Override
public String getType() {//重写 getType()
String class_fullName = classDef.getType();
int start = class_fullName.lastIndexOf("/");
int end = class_fullName.lastIndexOf("$");
int length = class_fullName.length();
if (end < 0) {
if (class_fullName.contains(";")) {
end = length - 1;
} else {
end = length;
}
}
String class_name = class_fullName.substring(start + 1, end);
String class_name_enc = Base64.encodeToString(class_name.getBytes(), 0);
Log.d("xiaojianbang", class_name + " " + class_name_enc);
return class_fullName.replace(class_name, class_name_enc);
}
@Override
public String getSourceFile() {
return null;
}
};
}
return super.rewrite(classDef);
}
};
}
@Override
public Rewriter<Field> getFieldRewriter(Rewriters rewriters) {
return new FieldRewriter(rewriters) {
@Override
public Field rewrite(Field field) {
String className = field.getDefiningClass();
if (className.contains("com/xiaojianbang")) {
return new RewrittenField(field) {
@Override
public String getName() {
return Base64.encodeToString(field.getName().getBytes(), 0);
}
};
}
return super.rewrite(field);
}
};
}
@Override
public Rewriter<Method> getMethodRewriter(Rewriters rewriters) {
return new MethodRewriter(rewriters) {
@Override
public Method rewrite(Method value) {
String className = value.getDefiningClass();
if (className.contains("com/xiaojianbang")) {
return new RewrittenMethod(value) {
@Override
public String getName() {
String methodName = Base64.encodeToString(value.getName().getBytes(), 0);
Log.d("xiaojianbang", "methodName: " + methodName);
return methodName;
}
};
}
return super.rewrite(value);
}
};
}
});
----------------------------------------------------------------------------------------------------
//以上是通过对于dex文件中的类名方法名进行对比,假如有"com/xiaojianbang"字段的就去获取并进行base64
DexFile rewriteDexFile = new DexFileRewriter(rewriter).rewrite(dexBackedDexFile);
DexPool dexPool = new DexPool(rewriteDexFile.getOpcodes());
//rewrite方法会根据rewriter中定义的规则修改DEX文件的内容,并返回一个新的DexFile对象,其中包含修改后的DEX数据。
for (ClassDef classDef : rewriteDexFile.getClasses()) {
dexPool.internClass(classDef);
}
//dexPool.internClass(buildClasses());
//创建了一个DexPool对象,它用于管理DEX文件中的类定义。通过遍历rewriteDexFile中的所有类定义(ClassDef对象),并使用dexPool.internClass(classDef)方法将它们注册到DexPool中。
dexPool.writeTo(new FileDataStore(newDexFile));
//DexFileFactory.writeDexFile(newDexFile.toString(), rewriteDexFile);
Log.d("xiaojianbang", "new_classes.dex生成完毕,保存路径为:" + newDexFile.toString());
} catch (IOException e) {
e.printStackTrace();
}
利用反射来实现获取类中的属性
var Wallet = Java.use("com.xiaojianbang.hook.Wallet");
console.log("methods:");
var methods =Wallet.class.getDeclaredMethods();//获取已经声明的方法
for (var i = 0; i < methods.length; i++) {
console.log(methods[i].getName());
}
console.log("----------------------------------------------------------------");
var Constructor = Wallet.class.getDeclaredConstructors();//获取已经声明的构造器
console.log("Constructor:");
for (var i = 0; i < Constructor.length; i++) {
console.log(Constructor[i].getName());
}
console.log("----------------------------------------------------------------");
var Fields = Wallet.class.getDeclaredFields();//获取已经声明的字段(变量)
console.log("Fields:");
for (var i = 0; i < Fields.length; i++) {
console.log(Fields[i].getName());
}
console.log("----------------------------------------------------------------");
var Classes = Wallet.class.getDeclaredClasses();//获取已经声明的内部类
console.log("Classes:");
for (var i = 0; i < Classes.length; i++) {
console.log(Classes[i].getName());
var Wallet$InnerStructure = Classes[i].getDeclaredFields();//获取已经声明的内部类的内部属性
console.log("----------------------------------------------------------------");
console.log("Wallet$InnerStructure:");
for (var j = 0; j < Wallet$InnerStructure.length; j++) {
console.log("Wallet$InnerStructure" + Wallet$InnerStructure[j].getName());
}
}
结合上面的过程,我们再去HOOK每个方法的重载函数
var Utils = Java.use("com.xiaojianbang.hook.Utils");
console.log("methods:");
var methods =Utils.class.getDeclaredMethods();
for (var i = 0; i < methods.length; i++) {
console.log(methods[i].getName());
let methodsname = methods[i].getName();
Hook_method_overload(methodsname);
}
function Hook_method_overload(methods_name) {
{
var overloadsarr = Utils[methods_name].overloads;
console.log("methods_name:",methods_name);
for (var i = 0; i < overloadsarr.length; i++)
{
overloadsarr[i].implementation = function()
{
console.log("Utils.getCalc.overloads["+i+"]-----");//打印重载方法名和参数
var params="";
for(var j=0;j<arguments.length;j++)//利用js中的arguments方法获取参数
{
params += "arg"+j+":"+arguments[j]+"\n";
};
console.log("Utils.getCalc.overloads["+i+"]-----",params);
return this[methods_name].apply(this,arguments)//利用apply()方法来实现call原函数 apply(object,参数数组)
}
}
}
}
Java的dex文件注入
我们通过Android studio去创新自己的APK文件,里面包含了想要实现的类,方法......,利用frida注入,从而实现我们利用方法使用
package com.chen.myapplication;
import android.util.Log;
public class Test {
public static void test()
{
Log.d("tag","test!!!!!!!!!!!!注入");
}
}
JS注入代码
Java.openClassFile("/data/local/tmp/Testclasses.dex").load();//这里的先要将自己生成的APK文件中的dex文件push到手机端去
var Test=java.use("com.chen.myapplication.Test");
//Test.test.implementation=function()
//{
// return this.test;
//}
Test.test();
数据类型由Map转为HashMap的过程以及Java.cast的使用方法
var Utils = Java.use("com.xiaojianbang.hook.Utils");
Utils.shufferMap2.implementation=function(a){
console.log("Utils.shufferMap2.implementation",a);
console.log("Utils.shufferMap2.implementation",a.toString());
var result = Java.cast(a,Java.use("java.util.HashMap"));
console.log(result);
return this.shufferMap2(a);
}
通过js来创建数组的过程传递给JAVA程序
var Utils = Java.use("com.xiaojianbang.hook.Utils");
var result =Utils.myPrint(["chen_chen_chen","xiaojianbang","ahhaha"]);
console.log(result);
var array =Java.array(
"Ljava.lang.String;",
["chen_chen_chen","xiaojianbang","ahhaha"]
);
var result1 = Utils.myPrint(array);
console.log(result1);
注意:
/* public static String myPrint(Object... objArr) {
StringBuilder sb = new StringBuilder();
for (Object obj : objArr) {
sb.append(obj);
sb.append("|");
}
return sb.toString();
}
*/
这里假如要HOOK或者是创建的函数是Object类型的,字符串是可以直接进行传值的,但是int或者是bool类型的数据是不可以直接传值的,我们这里可以看到的是传入的是Object类型,也就是所有的对象都可以进行传入,但是失败。
原因是我们传入的数据其实在java底层是通过创建相应的方法进行传值的,比如 int ——>Integer bool ——>Bool
所以我们也要进行数据的封装,最后达到应有的效果
var Utils = Java.use("com.xiaojianbang.hook.Utils");
var
result=Utils.myPrint([
"chen_chen_chen",
Java.use("java.lang.Integer").$new(100),
Java.use("java.lang.Boolean").$new(false)
]);
console.log(result);
===================================================================================
创建一个Array来进行传值
var ArrayList = Java.use("java.util.ArrayList");
var arrry = ArrayList.$new();
arrry.add("chen_chen_chen");
arrry.add(Java.use("java.lang.Integer").$new(100));
arrry.add(Java.use("java.lang.Boolean").$new(false));
console.log(arrry)
var Utils = Java.use("com.xiaojianbang.hook.Utils");
var result =Utils.myPrint(arrry);
console.log(result);
ClassLoader不同对于同一个类加载之后的class是不同的,我们可能可以按照对应了的包名来进行搜索,但是结果可能是不对的,这时候我们可以去枚举相应的类加载器来查看相应的加载过程
Java.enumerateClassLoaders(
{
onMatch:function(loader)
{
try
{
console.log(loader);
Java.classFactory.loader = loader;
var dynamic = Java.use("com.xiaojianbang.app.Dynamic")
console.log("找到了!!!!!!!!",dynamic);
dynamic.sayHello.implementation = function(){
console.log("Hook dynamic is run!!!!!!")
return "chen_chen_chen_chen"
}
}
catch(e)
{
console.log("False")
};
},
onComplete:function(){
console.log("enumerateClassLoaders completed");
}
});
/*
dalvik.system.PathClassLoader[DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /vendor/lib64, /system/product/lib64, /system/lib64, /vendor/lib64, /system/product/lib64]]]
False
java.lang.BootClassLoader@58f6796
False
dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.xiaojianbang.app-qX4fZHwYEPhfLXNw8TFC2g==/base.apk"],nativeLibraryDirectories=[/data/app/com.xiaojianbang.app-qX4fZHwYEPhfLXNw8TFC2g==/lib/arm64, /data/app/com.xiaojianbang.app-qX4fZHwYEPhfLXNw8TFC2g==/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/product/lib64]]]
False
dalvik.system.DexClassLoader[DexPathList[[dex file "/data/user/0/com.xiaojianbang.app/app_shell/xiaojianbang.dex"],nativeLibraryDirectories=[/data/app/com.xiaojianbang.app-qX4fZHwYEPhfLXNw8TFC2g==/lib/arm64, /system/lib64, /vendor/lib64, /system/product/lib64]]]
找到了!!!!!!!! <class: com.xiaojianbang.app.Dynamic>
enumerateClassLoaders completed
*/
接口函数的HOOK
由于我们知道,可能会发现某个可疑或者是包含我们关键参数的接口,里面存在着我们希望定位到关键函数的成员属性,所以我们回去时不时的HOOK接口,从而定位代码(由于可能存在对应的混淆点,比如方法名全为a,b,c,d或者一些难以通过交叉引用得到的保护方法)我们就需要HOOK接口函数
var classes = Java.enumerateLoadedClassesSync();
for(const index in classes)
{
let className = classes[index];
if(className.indexOf("com.xiaojianbang")==-1)continue;
console.log("com.xiaojianbang's classname",className);
//打印已经加载的class同时满足类名中包含"com.xiaojianbang"
let clazz = Java.use(className);
let resultArr = clazz.class.getInterfaces();//获取接口
for(let i=0;i<resultArr.length;i++)
{
if(resultArr[i].toString().indexOf("com.xiaojianbang.app.TestRegisterClass")==-1)//比对接口是否是包含该接口
{
console.log(className,resultArr);
}
}
}
/*
com.xiaojianbang's classname com.xiaojianbang.app.InterfaceDemo
com.xiaojianbang's classname com.xiaojianbang.app.TestAbstract
com.xiaojianbang's classname com.xiaojianbang.hook.BankCard
com.xiaojianbang's classname com.xiaojianbang.encrypt.RSA_Base64
com.xiaojianbang's classname com.xiaojianbang.hook.Wallet$InnerStructure
com.xiaojianbang's classname com.xiaojianbang.app.TestRegisterClass
com.xiaojianbang's classname com.xiaojianbang.app.MainActivity$1
com.xiaojianbang's classname com.xiaojianbang.ndk.NativeHelper
com.xiaojianbang's classname com.xiaojianbang.app.MainActivity
com.xiaojianbang.app.MainActivity interface android.view.View$OnClickListener
com.xiaojianbang's classname com.xiaojianbang.hook.Money
com.xiaojianbang's classname com.xiaojianbang.hook.Wallet
com.xiaojianbang's classname com.xiaojianbang.app.AbstractDemo
com.xiaojianbang's classname com.xiaojianbang.app.databinding.ActivityMainBinding
com.xiaojianbang.app.databinding.ActivityMainBinding interface androidx.viewbinding.ViewBinding
com.xiaojianbang's classname com.xiaojianbang.hook.Utils
com.xiaojianbang's classname com.xiaojianbang.reflect.ReflectEncrypt
*/可以让我们发现对应的"com.xiaojianbang"字段的类名以及接口名的对应
同理的方法获取父类的函数名
var classes = Java.enumerateLoadedClassesSync();
for(const index in classes)
{
try{
let className = classes[index];
if(className.indexOf("com.xiaojianbang")==-1)continue;
console.log("com.xiaojianbang's classname",className);
//打印已经加载的class同时满足类名中包含"com.xiaojianbang"
let clazz = Java.use(className);
let resultArr = clazz.class.getSuperclass();//获取接口
console.log("clazz.class.getSuperclass()",resultArr)
for(let i=0;i<resultArr.length;i++)
{
if(resultArr[i].toString().indexOf("com.xiaojianbang.app.TestRegisterClass")==-1)//比对接口是否是包含该接口
{
console.log(className,resultArr);
}
}
}
catch(e){
console.log("className:"+"error:----------------------------------")
}
}
objection自动化HOOK工具
启动:
objection -g <app:com.xiaojianbang.app> explore
使用:
android hooking list classes
//加载出所以的已加载的类,对应——> Java.enumerateLoadedClassesSync()
android hooking search classes <pattern字段>
//加载出所有包含字段的类
android hooking list class_methods <类名、路径> 如:com.xiaojianbang.hook.Wallet
//加载出对应类的类方法
android hooking watch class <类名>
//Hook对应类的所有方法(不包含构造函数)
android hooking watch class_method <路径、类名>如:com.xiaojianbang.hook.Wallet.getBalance
//Hook对应的类方法,同时可以选择参数:--dump-args 打印参数--dump-return 打印返回值 --dump-backtrace 打印堆栈
objection插件wallbreaker的使用
//objection插件加载:
plugin load "c:\Users\19817\Desktop\Wallbreaker-master" Wallbreaker Loaded plugin: Wallbreaker
//相关类搜索:
plugin Wallbreaker classsearch xiaojianbang
//相关方法的查看
plugin Wallbreaker classdump com.xiaojianbang.hook.Utils
//类方法实现的查看
plugin Wallbreaker objectsearch com.xiaojianbang.hook.Money
[0x294a]: com.xiaojianbang.hook.Money@959256
//利用实现的类名去实现相应类的方法查看
plugin Wallbreaker objectdump 0x2622
package com.xiaojianbang.hook
class Money {
/* static fields */
static String flag; => QQ:24358757
/* instance fields */
int amount; => 100
String currency; => 人民币
/* constructor methods */
com.xiaojianbang.hook.Money(String, int);
/* static methods */
static String getFlag();
static void setFlag(String);
/* instance methods */
int getAmount();
String getCurrency();
String getInfo();
void setAmount(int);
void setCurrency(String);
}
利用objection patchapk 重新打包apk,将frida-gadget注入(免root注入)
我们frida中的root注入的原理就是通过frida生成的一个so文件,实现root注入,我们通过将要hook的程序进行重新打包,并且将frida-gadget一并注入的结果就是,我们不需要root权限就可以直接实现注入过程
(这里会有代理检测,所以可以提前把对应版本的frida-gadget下到对应的框架版本里面去)
objection patchapk -a arm64-v8a -V 14.2.18 -s c:\Users\19817\Desktop\HookDemo.apk
成功打包之后直接frida注入就可以
frida -UF ./exp.js
base64java编码
import java.util.Base64;
public class Base64Example {
public static void main(String[] args) {
// 待编码的字符串
String originalString = "Hello, World!";
// 使用Base64的getEncoder()方法获取编码器
Base64.Encoder encoder = Base64.getEncoder();
// 对字符串进行编码,注意:字符串需要先转换为字节数组
String encodedString = encoder.encodeToString(originalString.getBytes());
// 输出编码后的字符串
System.out.println("Encoded String: " + encodedString);
// 如果你想要进行URL安全的Base64编码,可以使用Base64.getUrlEncoder()
Base64.Encoder urlEncoder = Base64.getUrlEncoder();
String urlEncodedString = urlEncoder.encodeToString(originalString.getBytes());
// 输出URL安全的编码后的字符串
System.out.println("URL Encoded String: " + urlEncodedString);
}
}
WebView的可调式权限Hook
Java.perform(function(){
var WebView = Java.use('android.webkit.WebView');
WebView.$init.overload('android.content.Context').implementation = function(a){
console.log("WebView.$init is called!");
var retval = this.$init(a);
this.setWebContentsDebuggingEnabled(true);
return retval;
}
WebView.$init.overload('android.content.Context', 'android.util.AttributeSet').implementation = function(a, b){
console.log("WebView.$init is called!");
var retval = this.$init(a, b);
this.setWebContentsDebuggingEnabled(true);
return retval;
}
WebView.$init.overload('android.content.Context', 'android.util.AttributeSet', 'int').implementation = function(a, b, c){
console.log("WebView.$init is called!");
var retval = this.$init(a, b, c);
this.setWebContentsDebuggingEnabled(true);
return retval;
}
WebView.$init.overload('android.content.Context', 'android.util.AttributeSet', 'int', 'boolean').implementation = function(a, b, c, d){
console.log("WebView.$init is called!");
var retval = this.$init(a, b, c, d);
this.setWebContentsDebuggingEnabled(true);
return retval;
}
WebView.$init.overload('android.content.Context', 'android.util.AttributeSet', 'int', 'int').implementation = function(a, b, c, d){
console.log("WebView.$init is called!");
var retval = this.$init(a, b, c, d);
this.setWebContentsDebuggingEnabled(true);
return retval;
}
WebView.$init.overload('android.content.Context', 'android.util.AttributeSet', 'int', 'java.util.Map', 'boolean').implementation = function(a, b, c, d, e){
console.log("WebView.$init is called!");
var retval = this.$init(a, b, c, d, e);
this.setWebContentsDebuggingEnabled(true);
return retval;
}
WebView.$init.overload('android.content.Context', 'android.util.AttributeSet', 'int', 'int', 'java.util.Map', 'boolean').implementation = function(a, b, c, d, e, f){
console.log("WebView.$init is called!");
var retval = this.$init(a, b, c, d, e, f);
this.setWebContentsDebuggingEnabled(true);
return retval;
}
//这里以上全是Hook的是WebView不同重载函数初始化的过程,其中this.setWebContentsDebuggingEnabled(true)也就是为了将调试权限设置为webview可调式的过程
WebView.setWebContentsDebuggingEnabled.implementation = function(){
this.setWebContentsDebuggingEnabled(true);
console.log("setWebContentsDebuggingEnabled is called!");
}
//这里是hook了这个setWebContentsDebuggingEnabled方法,不管如何就设置为ture
});
谷歌的webview的网址
chrome://inspect/#devices
假如我们发现的app其实是h5,也就是原本我们看到的一个登录的图示其实是浏览器的登录图示,只是直接嵌套到了app中,那么我们在实现了webview的调试权限之后就可以直接去利用web的js逆向了
md5和base64以及SHA加密java代码
MessageDigest md51 = MessageDigest.getInstance("MD5");//md5实现的两种方法
md51.update("md5_encode_date".getBytes());
byte[] result = md51.digest();
StringBuilder hexString1 = new StringBuilder();
for (byte b : result) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString1.append('0');
hexString1.append(hex);
}
System.out.println(hexString1);
System.out.println(Base64.getEncoder().encodeToString(result));
-----------------------------------------------------------------------------------------------
byte[] md5 = MessageDigest.getInstance("MD5").digest("md5_encode_date".getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : md5) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
System.out.println(hexString);
System.out.println(Base64.getEncoder().encodeToString(md5));
}
-----------------------------------------------------------------------------------------------
String date="{\"appid\":\"2\",\"username\":\"15828712516\",\"password\":\"passworf777\",\"cpsId\":\"tg001lxx\",\"imei\":\"2ffe0d3b-9a69-4513-9e7f-87d4334432bb\"}";
String base64_date =Base64.getEncoder().encodeToString(date.getBytes());//Base64
System.out.println(base64_date);
--------------------------------------------------------------------------------------
MessageDigest md5 = MessageDigest.getInstance("SHA-256");//SHA-1,SHA-256.....
md5.update("md5_encode_date".getBytes());
byte[] result = md5.digest();
StringBuilder hexString = new StringBuilder();
for (byte b : result) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
System.out.println(hexString);
System.out.println(Base64.getEncoder().encodeToString(result));
Java利用第三方库实现加密签名算法通杀
Java.perform(function () {
function showStacks() {
console.log(
Java.use("android.util.Log")
.getStackTraceString(
Java.use("java.lang.Throwable").$new()
)
);
}
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
function toBase64(tag, data) {
console.log(tag + " Base64: " + ByteString.of(data).base64());
}
function toHex(tag, data) {
console.log(tag + " Hex: " + ByteString.of(data).hex());
}
function toUtf8(tag, data) {
console.log(tag + " Utf8: " + ByteString.of(data).utf8());//以上是通过利用第三方库实现base64,hex,utf8的转换过程
}
// toUtf8("xiaojianbang",[48, 49, 50, 51, 52]);
// toBase64("xiaojianbang",[48, 49, 50, 51, 52]);
// toHex("xiaojianbang",[48, 49, 50, 51, 52]);
var messageDigest = Java.use("java.security.MessageDigest");//以下是对于messageDigest的update方法进行的HOOK,由于update是传入的是需要进行加密的byte数组,所以通过hook来查看初始数据
messageDigest.update.overload('byte').implementation = function (data) {
console.log("MessageDigest.update('byte') is called!");
showStacks();
return this.update(data);
}
messageDigest.update.overload('java.nio.ByteBuffer').implementation = function (data) {
console.log("MessageDigest.update('java.nio.ByteBuffer') is called!");
showStacks();
return this.update(data);
}
messageDigest.update.overload('[B').implementation = function (data) {
console.log("MessageDigest.update('[B') is called!");
showStacks();
var algorithm = this.getAlgorithm();//getAlgorithm()是该类下面的对应方法,是用来获取getInstance()中的内容的,也就是利用的算法
var tag = algorithm + " update data";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
console.log("=======================================================");
return this.update(data);
}
messageDigest.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {
console.log("MessageDigest.update('[B', 'int', 'int') is called!");
showStacks();
var algorithm = this.getAlgorithm();
var tag = algorithm + " update data";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
console.log("=======================================================", start, length);
return this.update(data, start, length);
}
//这里以上是update的hook,以下是digest的hook
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
messageDigest.digest.overload().implementation = function () {
console.log("MessageDigest.digest() is called!");
showStacks();
var result = this.digest();
var algorithm = this.getAlgorithm();
var tag = algorithm + " digest result";
toUtf8(tag, result);
toHex(tag, result);
toBase64(tag, result);
console.log("=======================================================");
return result;
}
messageDigest.digest.overload('[B').implementation = function (data) {
console.log("MessageDigest.digest('[B') is called!");
showStacks();
var algorithm = this.getAlgorithm();
var tag = algorithm + " digest data";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
var result = this.digest(data);
var tags = algorithm + " digest result";
toUtf8(tag, result);
toHex(tags, result);
toBase64(tags, result);
console.log("=======================================================");
return result;
}
messageDigest.digest.overload('[B', 'int', 'int').implementation = function (data, start, length) {
console.log("MessageDigest.digest('[B', 'int', 'int') is called!");
showStacks();
var algorithm = this.getAlgorithm();
var tag = algorithm + " digest data";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
var result = this.digest(data, start, length);
var tags = algorithm + " digest result";
toHex(tags, result);
toBase64(tags, result);
console.log("=======================================================", start, length);
return result;
}
//这里以上是hook的digest(),同时将其的参数进行hex,utf8,base64的转码进行输出
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
var mac = Java.use("javax.crypto.Mac");
mac.init.overload('java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (key, AlgorithmParameterSpec) {
console.log("Mac.init('java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!");
return this.init(key, AlgorithmParameterSpec);
}
mac.init.overload('java.security.Key').implementation = function (key) {
console.log("Mac.init('java.security.Key') is called!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " init Key";
var keyBytes = key.getEncoded();
toUtf8(tag, keyBytes);
toHex(tag, keyBytes);
toBase64(tag, keyBytes);
console.log("=======================================================");
return this.init(key);
}
mac.update.overload('byte').implementation = function (data) {
console.log("Mac.update('byte') is called!");
return this.update(data);
}
mac.update.overload('java.nio.ByteBuffer').implementation = function (data) {
console.log("Mac.update('java.nio.ByteBuffer') is called!");
return this.update(data);
}
mac.update.overload('[B').implementation = function (data) {
console.log("Mac.update('[B') is called!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " update data";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
console.log("=======================================================");
return this.update(data);
}
mac.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {
console.log("Mac.update('[B', 'int', 'int') is called!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " update data";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
console.log("=======================================================", start, length);
return this.update(data, start, length);
}
mac.doFinal.overload().implementation = function () {
console.log("Mac.doFinal() is called!");
var result = this.doFinal();
var algorithm = this.getAlgorithm();
var tag = algorithm + " doFinal result";
toUtf8(tag, result);
toHex(tag, result);
toBase64(tag, result);
console.log("=======================================================");
return result;
}
mac.doFinal.overload('[B').implementation = function (data) {
console.log("Mac.doFinal.overload('[B') is called!");
return this.doFinal(data);
}
mac.doFinal.overload('[B', 'int').implementation = function (output, outOffset) {
console.log("Mac.doFinal.overload('[B', 'int') is called!");
return this.doFinal(output, outOffset);
}
});//这里是对于javax.crypto.Mac的init,update,doFinal的hook
DES加密算法java算法库实现
DES对称加密中的分组加密算法,故名思意,分组加密算法,一组一组的进行加密,前一组的数据不会影响后一组数据的加密,后组也不会影响前组。按照八字节八字节进行分组加密
对于ECB模板下的DES,不需要使用IV向量进行加密
对于CBC对称八字节的密钥,则需要八字节的IV与明文进行异或操作。(这里细节的讲一下CBC模型的iv的用法,由于DES是分组加密算法,每八个字节的进行加密,IV用于与第一个八字节的明文先进行异或操作,然后进行第一组的des加 密,得到的密文结果作为第二组加密时,与明文进行异或的数据,依次每组的结果异或下一组的明文)
同时填充方式:
NOPadding 没有填充,则明文的长度要和IV向量的一样,八字节,或者是八的倍数
PKCS5Padding 这种填充方式,填充的字节长度是1-8个字节的区间,当明文不是八的倍数的字节长度,直接补为八的倍数;当明文是八的倍数则补8个字节的长度。
//encode
try{
SecretKeySpec secretKeySpec = new SecretKeySpec("12345678".getBytes(), "DES");//DES密钥是八个字节
Cipher des = Cipher.getInstance("DES/CBC/PKCS5Padding");//创建对应的传参对象,确定加密信息
byte[] iv = new byte[8];
Arrays.fill(iv,(byte)0);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);//iv
des.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] des_array = des.doFinal("Text_date".getBytes());
System.out.println(hex(des_array));
System.out.println(Base64.getEncoder().encodeToString(des_array));
--------------------------------------------------------------------------------------------------
//decode
des.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decode = des.doFinal(des_array);
System.out.println(new String(decode));
}
catch (Exception e)
{
e.printStackTrace();
}
DESede(3DES)三次进行DES加密:第一次DES加密,第二次DES解密,第三次DES加密
try{
SecretKeySpec secretKeySpec = new SecretKeySpec("123456781234567812345678".getBytes(), "DESede");//这里由于我们是进行的DESede加密,这里的密钥,第一个八字节作为第一次加密的密钥,第二个八字节作为第二次解密的密钥,第三个八字节作为第三次加密的密钥
Cipher des = Cipher.getInstance("DESede/CBC/NOPadding");
IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes());
des.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] des_array = des.doFinal("TextdateTextdate".getBytes());
System.out.println(hex(des_array));
System.out.println(Base64.getEncoder().encodeToString(des_array));
des.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decode = des.doFinal(des_array);
System.out.println(new String(decode));
}
catch (Exception e)
{
e.printStackTrace();
}
//这里由于我们第一个八字节密钥和第二个八字节密钥相等,所以加密又解密等于没加密,所以只进行了第三次的DES加密
利用加密算法库实现AES
同理于DES,不过密钥长度不同,16个字节对应的是AES的AES-128
SecretKeySpec secretKeySpec = new SecretKeySpec("1234567812345678".getBytes(), "AES");
Cipher des = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec("1234567812345678".getBytes());
des.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] des_array = des.doFinal("TextdateTextdate".getBytes());
System.out.println(hex(des_array));
通杀算法,实现DES/AES通杀
通杀就要去hook算法DES和算法AES中的函数也就是
Cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);这一步
所以去HOOK———— >var Cipher = Java.use("javax.crypto.Cipher")中的init()方法
Cipher.init.overload('int', 'java.security.Key').implementation=function()
{
console.log("Cipher.init.overload('int', 'java.security.Key')");
var Algorithm = this.getAlgorithm();
var tag = "Alogorithm:"+Algorithm+"init key";
toHex(tag,arguments[1].getEncoded());
toBase64(tag,arguments[1].getEncoded());
toUtf8(tag,arguments[1].getEncoded());
return this.init.apply(this,arguments);
}
Cipher.init.overload('int', 'java.security.cert.Certificate').implementation=function()
{
console.log("Cipher.init.overload('int', 'java.security.cert.Certificate')");
return this.init.apply(this,arguments);
}
Cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation=function()
{
console.log("overload('int', 'java.security.Key', 'java.security.SecureRandom')");
return this.init.apply(this,arguments);
}
Cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation=function()
{
console.log(".overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom')");
return this.init.apply(this,arguments);
}
Cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation=function()
{
console.log(".overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom'))");
return this.init.apply(this,arguments);
}
Cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation=function()
{
console.log(".overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom')");
return this.init.apply(this,arguments);
}
Cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation=function()
{
var Algorithm = this.getAlgorithm();
var IvParameterSpec = Java.cast(arguments[2],Java.use("javax.crypto.spec.IvParameterSpec"));//这里注意,因为我们hook的时候提示的就是AlgorithmParameterSpec,也就是IvParameterSpec方法的接口函数,所以要向下转型
var iv = IvParameterSpec.getIV();
toHex("iv",iv);
toBase64("iv",iv);
toUtf8("iv",iv);
var tag = "Alogorithm:"+Algorithm+"init iv"+iv+"init key";
toHex(tag,arguments[1].getEncoded());
toBase64(tag,arguments[1].getEncoded());
toUtf8(tag,arguments[1].getEncoded());
return this.init.apply(this,arguments);
}
----------------------------------------------------------------------------------------------------
//上面是自己写的
cipher.init.overload('int', 'java.security.Key').implementation = function () {
console.log("Cipher.init('int', 'java.security.Key') is called!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " init Key";
var className = JSON.stringify(arguments[1]);
if(className.indexOf("OpenSSLRSAPrivateKey") === -1){
var keyBytes = arguments[1].getEncoded();
toUtf8(tag, keyBytes);
toHex(tag, keyBytes);
toBase64(tag, keyBytes);
}
console.log("=======================================================");
return this.init.apply(this, arguments);
}
cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function () {
console.log("Cipher.init('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " init Key";
var keyBytes = arguments[1].getEncoded();
toUtf8(tag, keyBytes);
toHex(tag, keyBytes);
toBase64(tag, keyBytes);
var tags = algorithm + " init iv";
var iv = Java.cast(arguments[2], Java.use("javax.crypto.spec.IvParameterSpec"));
var ivBytes = iv.getIV();
toUtf8(tags, ivBytes);
toHex(tags, ivBytes);
toBase64(tags, ivBytes);
console.log("=======================================================");
return this.init.apply(this, arguments);
}
RSA公钥私钥生成过程中的细节
RSA中分为了PKCS1和PKCS8的两种生成方式,其中PKCS1中的私钥生成时的提示词是
"-----BEGIN RSA PRIVATE KEY-----" 而 PKCS8 则是 "-----BEGIN PRIVATE KEY-----"
同时在生成的RSA密钥对中,大多数是base64编码之后的结果,所以在利用过程中需要base64解密之后在填入对应的对象实例中去
String publickey ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHnsW9mHbbPD1wAZ4unJ2zhTY0" +
"B9HEC+7RFOLKRcRV4uXcRnXboHP9qY9BeTe7DsuVjFph8ErDV+bvrqqGQuAt/GzW" +
"myOH7XfVIW889OSyFMRUY+y1LvX+aU0PtDtug42GVJQ9JEn3EzjVlqqqUxyyRGJx" +
"QgmJZRrt7uPxwD2MOQIDAQAB";
byte[] publickey_byte = Base64.getDecoder().decode(publickey);
X509EncodedKeySpec x509EncodedKeySpec =new X509EncodedKeySpec(publickey_byte);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec);
byte[] public_key_byte = publicKey.getEncoded();
System.out.println(Base64.getEncoder().encodeToString(public_key_byte));
String private_key = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANeg7Zme5y0W42c1" +
"mx6pKE+LZsFiv7e92IAEYo14VwVAtjdEseyK6XRzU7ZVZe2h0cEN9kOxsBHLEacB" +
"v8Gv6vIv7Z+diOrSnG68QM6zz5lJkzZi9ePvh2WpZA2HNhRky+hLvtxBfQEWDl4h" +
"dAnGsg1dKJEUXCRlKfWqs/q9Q9ENAgMBAAECgYEA0C7Vd14NwGC6ySjeTSnwe2wR" +
"l2BpzVKDtoWFSSUIj0+9HXs7dS9g2keGaSHmORnk08lRHGZvoZ43utBbfPsFj2tV" +
"/1Q2pI+cJwCMvO/qA+nl7TEwgbSmfLiPso3zymUUa6byuX3TxnZdCLDtTNDRsH7x" +
"DF4LNFqBPz1+hQkg1NkCQQD+KRGTu+HMYj2yVLgUXOiIOp/4mN/IFmeXB5kNLZ5T" +
"DZW/Zw9MmO62UI9jrAKwP7ybaL8KVQoTCcJgaPiS/yiHAkEA2TB22r06leTo4aRw" +
"CNTmtjAoFFMVf4kykzq1LAbbjZOxIr5arIuQBdPWVvWBLRfqrzZ9SAVL9xIKbodt" +
"fv1iywJAEkMXLTtRxSLF9htaQrROTQORXQZ3BeR+Ov7jw2uktakDOoaINePDOzxc" +
"oTfJ1aouvifvmil0GNwZqF3ChqVo3wJAZeKuXv7WNOsltHSHeh0l/eGpa59uPl4G" +
"M7sesoClDk1yk3Ho53ixH4q4yiJxbs4BciCFtPuHH2h/LEqrux7y8QJBAJwRL+1u" +
"WvSGULXoTC/FYYTa934jBw2FDZbqVlmYEMTXpPl05oipo/mKTNEEU0somOHFxvak" +
"+6XIZY4E06pqORw=";
byte[] private_key_byte = Base64.getDecoder().decode(private_key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(private_key_byte);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
System.out.println(Base64.getEncoder().encodeToString(privateKey.getEncoded()));
同时在安卓端的RSA的公钥和私钥的生成:
public static PublicKey generatePublicKey() throws Exception {
byte[] publicKeyBase64Bytes = ByteString.decodeBase64(publicKeyBase64).toByteArray();
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBase64Bytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(x509EncodedKeySpec);
}
public static PrivateKey generatePrivateKey() throws Exception {
byte[] privateKeyBase64Bytes = ByteString.decodeBase64(privateKeyBase64).toByteArray();
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBase64Bytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey _privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Log.d("xiaojianbang", ByteString.of(_privateKey.getEncoded()).base64());
return _privateKey;
}
//sdk的版本不支持使用ByteString了
java端的RSA加解密
String publickey ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXoO2ZnuctFuNnNZseqShPi2bBYr+3vdiABGKNeFcFQLY3RLHsiul0c1O2VWXtodHBDfZDsbARyxGnAb/Br+ryL+2fnYjq0pxuvEDOs8+ZSZM2YvXj74dlqWQNhzYUZMvoS77cQX0BFg5eIXQJxrINXSiRFFwkZSn1qrP6vUPRDQIDAQAB";
byte[] publickey_byte = Base64.getDecoder().decode(publickey);
X509EncodedKeySpec x509EncodedKeySpec =new X509EncodedKeySpec(publickey_byte);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec);
byte[] public_key_byte = publicKey.getEncoded();
System.out.println("public key to base64 = "+Base64.getEncoder().encodeToString(public_key_byte));
String private_key = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANeg7Zme5y0W42c1" +
"mx6pKE+LZsFiv7e92IAEYo14VwVAtjdEseyK6XRzU7ZVZe2h0cEN9kOxsBHLEacB" +
"v8Gv6vIv7Z+diOrSnG68QM6zz5lJkzZi9ePvh2WpZA2HNhRky+hLvtxBfQEWDl4h" +
"dAnGsg1dKJEUXCRlKfWqs/q9Q9ENAgMBAAECgYEA0C7Vd14NwGC6ySjeTSnwe2wR" +
"l2BpzVKDtoWFSSUIj0+9HXs7dS9g2keGaSHmORnk08lRHGZvoZ43utBbfPsFj2tV" +
"/1Q2pI+cJwCMvO/qA+nl7TEwgbSmfLiPso3zymUUa6byuX3TxnZdCLDtTNDRsH7x" +
"DF4LNFqBPz1+hQkg1NkCQQD+KRGTu+HMYj2yVLgUXOiIOp/4mN/IFmeXB5kNLZ5T" +
"DZW/Zw9MmO62UI9jrAKwP7ybaL8KVQoTCcJgaPiS/yiHAkEA2TB22r06leTo4aRw" +
"CNTmtjAoFFMVf4kykzq1LAbbjZOxIr5arIuQBdPWVvWBLRfqrzZ9SAVL9xIKbodt" +
"fv1iywJAEkMXLTtRxSLF9htaQrROTQORXQZ3BeR+Ov7jw2uktakDOoaINePDOzxc" +
"oTfJ1aouvifvmil0GNwZqF3ChqVo3wJAZeKuXv7WNOsltHSHeh0l/eGpa59uPl4G" +
"M7sesoClDk1yk3Ho53ixH4q4yiJxbs4BciCFtPuHH2h/LEqrux7y8QJBAJwRL+1u" +
"WvSGULXoTC/FYYTa934jBw2FDZbqVlmYEMTXpPl05oipo/mKTNEEU0somOHFxvak" +
"+6XIZY4E06pqORw=";
byte[] private_key_byte = Base64.getDecoder().decode(private_key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(private_key_byte);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
System.out.println("private key to base64 = "+Base64.getEncoder().encodeToString(privateKey.getEncoded()));
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encode_data = cipher.doFinal("Text:chen_chen_chen".getBytes());
System.out.println("encode_data_to_base64 = "+Base64.getEncoder().encodeToString(encode_data));
System.out.println("encode_data_to_Hex = "+hex(encode_data));
//encode
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//decode
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decode_data = cipher.doFinal(encode_data);
System.out.println("decode_data_to_base64 = "+Base64.getEncoder().encodeToString(decode_data));
System.out.println("dncode_data_to_Hex = "+hex(encode_data));
System.out.println(new String(decode_data));
RSA的填充方式:
RSA中的NOPadding的填充方式:
- 明文最多字节数为密钥字节数
- 密文与密钥等长
- 填充字节0(在明文数据前填充0),加密后的密文不变
PKCS1Padding:
- 明文最大字节数为密钥字节数-11(比如128字节的密钥就只能加密117个明文字节)
- 密文与密钥等长
- 每一次的填充不一样,所以每一次加密的密文也不同
多种加密算法结合使用:
通过RSA和AES进行组合加密
- 随机生成的AES的key
- 利用生成的AESkey去加密plainText明文数据得到cipherText
- 将生成的对应的RSA中的密钥对(公钥和私钥)中的公钥去加密AES的key得到cipherkey
- 客户端直接将cipherText和cipherKey都发送给服务器端,服务器端有RSA的私钥可以直接去得到AES的key去解密cipherText
public class Main {
private static final char[] key = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '*', '!'};
public static String hex(byte[] str)
{
StringBuilder hexString = new StringBuilder();
for (byte b : str) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString().trim();
}
public static PublicKey get_PublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
String publickey ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXoO2ZnuctFuNnNZseqShPi2bBYr+3vdiABGKNeFcFQLY3RLHsiul0c1O2VWXtodHBDfZDsbARyxGnAb/Br+ryL+2fnYjq0pxuvEDOs8+ZSZM2YvXj74dlqWQNhzYUZMvoS77cQX0BFg5eIXQJxrINXSiRFFwkZSn1qrP6vUPRDQIDAQAB";
byte[] publickey_byte = Base64.getDecoder().decode(publickey);
X509EncodedKeySpec x509EncodedKeySpec =new X509EncodedKeySpec(publickey_byte);
return KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec);
}
public static PrivateKey get_PrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
String private_key = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANeg7Zme5y0W42c1" +
"mx6pKE+LZsFiv7e92IAEYo14VwVAtjdEseyK6XRzU7ZVZe2h0cEN9kOxsBHLEacB" +
"v8Gv6vIv7Z+diOrSnG68QM6zz5lJkzZi9ePvh2WpZA2HNhRky+hLvtxBfQEWDl4h" +
"dAnGsg1dKJEUXCRlKfWqs/q9Q9ENAgMBAAECgYEA0C7Vd14NwGC6ySjeTSnwe2wR" +
"l2BpzVKDtoWFSSUIj0+9HXs7dS9g2keGaSHmORnk08lRHGZvoZ43utBbfPsFj2tV" +
"/1Q2pI+cJwCMvO/qA+nl7TEwgbSmfLiPso3zymUUa6byuX3TxnZdCLDtTNDRsH7x" +
"DF4LNFqBPz1+hQkg1NkCQQD+KRGTu+HMYj2yVLgUXOiIOp/4mN/IFmeXB5kNLZ5T" +
"DZW/Zw9MmO62UI9jrAKwP7ybaL8KVQoTCcJgaPiS/yiHAkEA2TB22r06leTo4aRw" +
"CNTmtjAoFFMVf4kykzq1LAbbjZOxIr5arIuQBdPWVvWBLRfqrzZ9SAVL9xIKbodt" +
"fv1iywJAEkMXLTtRxSLF9htaQrROTQORXQZ3BeR+Ov7jw2uktakDOoaINePDOzxc" +
"oTfJ1aouvifvmil0GNwZqF3ChqVo3wJAZeKuXv7WNOsltHSHeh0l/eGpa59uPl4G" +
"M7sesoClDk1yk3Ho53ixH4q4yiJxbs4BciCFtPuHH2h/LEqrux7y8QJBAJwRL+1u" +
"WvSGULXoTC/FYYTa934jBw2FDZbqVlmYEMTXpPl05oipo/mKTNEEU0somOHFxvak" +
"+6XIZY4E06pqORw=";
byte[] private_key_byte = Base64.getDecoder().decode(private_key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(private_key_byte);
return KeyFactory.getInstance("RSA").generatePrivate(keySpec);
}
public static String RSA_encode(String plainText, PublicKey publicKey){
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encode_data = cipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(encode_data);
}
catch (Exception ignored) {
}
return "wrong data";
}
public static String RSA_decode(String plainText, PrivateKey privateKey){
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encode_data = cipher.doFinal(Base64.getDecoder().decode(plainText));
System.out.println(Base64.getEncoder().encodeToString(encode_data));
return bytesToString(encode_data);
} catch (NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | BadPaddingException |
InvalidKeyException e) {
throw new RuntimeException(e);
}
}
public static String bytesToString(byte[] bytes) {
// 使用UTF-8字符编码将字节数组转换为字符串
return new String(bytes, StandardCharsets.UTF_8);
}
public static String AES_encode(String plainText,String secretkeySpec) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretkeySpec.getBytes(),"AES");
aes.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] encode_data = aes.doFinal(plainText.getBytes());
return new String(Base64.getEncoder().encode(encode_data));
}
public static String get_random_AESKey() {
Random random = new Random();
StringBuilder stringBuilder = new StringBuilder();
for(int i =0;i<16;i++)
{
int random_data= random.nextInt(100);
String tmp = "0"+Integer.toHexString(random_data);
tmp = tmp.substring(tmp.length()-2);
stringBuilder.append(tmp);
}
return stringBuilder.toString();
}
public static void main(String[] args) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
String plainText = "plainText_encode";
String random_AESkey = get_random_AESKey();
String cipherText = AES_encode(plainText,random_AESkey);
System.out.println("cipherText:"+cipherText);
String AES_encodekey = RSA_encode(random_AESkey,get_PublicKey());
System.out.println("cipherKEY:"+AES_encodekey);
System.out.println("----------------------------------------------------------------");
String AESKEY = RSA_decode(AES_encodekey,get_PrivateKey());
Base64.getDecoder().decode(AESKEY);
System.out.println("AESKEY:"+AESKEY);
数字签名算法
数字签名算法是通过私钥来进行算法签名的
- 我们先将要进行数字签名的数据利用私钥进行加密
- 得到的结果会在服务器端进行验签解密,查看该数据解密之后的数据是否符合规范
加密:利用私钥将原始数据data进行加密
String private_key = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANeg7Zme5y0W42c1" +
"mx6pKE+LZsFiv7e92IAEYo14VwVAtjdEseyK6XRzU7ZVZe2h0cEN9kOxsBHLEacB" +
"v8Gv6vIv7Z+diOrSnG68QM6zz5lJkzZi9ePvh2WpZA2HNhRky+hLvtxBfQEWDl4h" +
"dAnGsg1dKJEUXCRlKfWqs/q9Q9ENAgMBAAECgYEA0C7Vd14NwGC6ySjeTSnwe2wR" +
"l2BpzVKDtoWFSSUIj0+9HXs7dS9g2keGaSHmORnk08lRHGZvoZ43utBbfPsFj2tV" +
"/1Q2pI+cJwCMvO/qA+nl7TEwgbSmfLiPso3zymUUa6byuX3TxnZdCLDtTNDRsH7x" +
"DF4LNFqBPz1+hQkg1NkCQQD+KRGTu+HMYj2yVLgUXOiIOp/4mN/IFmeXB5kNLZ5T" +
"DZW/Zw9MmO62UI9jrAKwP7ybaL8KVQoTCcJgaPiS/yiHAkEA2TB22r06leTo4aRw" +
"CNTmtjAoFFMVf4kykzq1LAbbjZOxIr5arIuQBdPWVvWBLRfqrzZ9SAVL9xIKbodt" +
"fv1iywJAEkMXLTtRxSLF9htaQrROTQORXQZ3BeR+Ov7jw2uktakDOoaINePDOzxc" +
"oTfJ1aouvifvmil0GNwZqF3ChqVo3wJAZeKuXv7WNOsltHSHeh0l/eGpa59uPl4G" +
"M7sesoClDk1yk3Ho53ixH4q4yiJxbs4BciCFtPuHH2h/LEqrux7y8QJBAJwRL+1u" +
"WvSGULXoTC/FYYTa934jBw2FDZbqVlmYEMTXpPl05oipo/mKTNEEU0somOHFxvak" +
"+6XIZY4E06pqORw=";
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] private_key_bytes = Base64.getDecoder().decode(private_key);
PKCS8EncodedKeySpec private_key_spec = new PKCS8EncodedKeySpec(private_key_bytes);
PrivateKey privateKey = keyFactory.generatePrivate(private_key_spec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update("xiaojianbang".getBytes());
byte[] signature_bytes = signature.sign();
System.out.println(Base64.getEncoder().encodeToString(signature_bytes));
验签解密:利用公钥对于私钥加密的数据进行解密,比对与原始值data的区别
byte[] signature_bytes = signature.sign();
String publickey ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXoO2ZnuctFuNnNZseqShPi2bBYr+3vdiABGKNeFcFQLY3RLHsiul0c1O2VWXtodHBDfZDsbARyxGnAb/Br+ryL+2fnYjq0pxuvEDOs8+ZSZM2YvXj74dlqWQNhzYUZMvoS77cQX0BFg5eIXQJxrINXSiRFFwkZSn1qrP6vUPRDQIDAQAB";
byte[] publickey_bytes = Base64.getDecoder().decode(publickey);
X509EncodedKeySpec publickey_spec = new X509EncodedKeySpec(publickey_bytes);
PublicKey publicKey = keyFactory.generatePublic(publickey_spec);
signature.initVerify(publicKey);
signature.update("xiaojianbang".getBytes());
boolean check_sign = signature.verify(signature_bytes);
System.out.println(check_sign);
通杀HOOK:直接去hook--->Signature类下的initSign()和update()
var Signature = Java.use("java.security.Signature")
Signature.update.overload('byte').implementation=function(a)
{
console.log("Signature.update rag'byte': " + a);
var result = this.update.apply(this,arguments);
return result;
}
Signature.update.overload('java.nio.ByteBuffer').implementation=function(A)
{
console.log("Signature.update rag'java.nio.ByteBuffer': " + A);
var result = this.update.apply(this,arguments);
return result;
}
Signature.update.overload('[B').implementation=function(A)
{
var Alogorithm =this.getAlgorithm();
var tag=Alogorithm+"update:"
console.log("Signature.update rag'[B'': " + A);
toBase64(tag,A);
toHex(tag,A);
toUtf8(tag,A);
var result = this.update.apply(this,arguments);
return result;
}
Signature.update.overload('[B', 'int', 'int').implementation=function(a,b,c)
{
var Alogorithm =this.getAlgorithm();
var tag=Alogorithm+"update:"
console.log("Signature.update rag'[B', 'int', 'int': " + a+b+c);
toBase64(tag,a);
toHex(tag,a);
toUtf8(tag,a);
var result = this.update.apply(this,arguments);
return result;
}
Signature.initSign.overload('java.security.PrivateKey').implementation=function(Key)
{
var Alogorithm = this.getAlgorithm();
// console.log("json"+JSON.stringify(Key));
//json"<instance: java.security.PrivateKey, $className: com.android.org.conscrypt.OpenSSLRSAPrivateKey>"
//这里注意向下转型,先获取该参数的类,然后向下转型(不然不能进行getEncoded()方法)
var OpenSSL = Java.use("com.android.org.conscrypt.OpenSSLRSAPrivateKey");
var result = Java.cast(Key, OpenSSL).getEncoded()
var tag="Alogorithm:"+Alogorithm+"key:";
toHex(tag,result);
toBase64(tag,result);
toUtf8(tag,result);
return this.initSign.apply(this,arguments);
}
Signature.initSign.overload('java.security.PrivateKey', 'java.security.SecureRandom').implementation=function(Key,Random)
{
var Alogorithm = this.getAlgorithm();
var OpenSSL = Java.use("com.android.org.conscrypt.OpenSSLRSAPrivateKey");
var result = Java.cast(Key, OpenSSL).getEncoded()
var tag="Alogorithm:"+Alogorithm+"key:";
toHex(tag,result);
toBase64(tag,result);
toUtf8(tag,result);
return this.initSign.apply(this,arguments);
}
CryptoJS消息摘要算法
可以在浏览器端实现 ——>f12——>source——>snippet下去创建文件传入之后直接run
直接运行js代码,需要创建var CryptoJS = module.exports;这样的实例对象
;
(function (root, factory) {
if (typeof exports === "object") {
module.exports = exports = factory();
} else if (typeof define === "function" && define.amd) {
define([], factory);
} else {
root.CryptoJS = factory();
}
}(this, function () {
var CryptoJS = CryptoJS || (function (Math, undefined) {
var create = Object.create || (function () {
function F() {};
return function (obj) {
var subtype;
F.prototype = obj;
subtype = new F();
F.prototype = null;
return subtype;
};
}
());
var C = {};
var C_lib = C.lib = {};
var Base = C_lib.Base = (function () {
return {
extend: function (overrides) {
var subtype = create(this);
if (overrides) {
subtype.mixIn(overrides);
}
if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {
subtype.init = function () {
subtype.$super.init.apply(this, arguments);
};
}
subtype.init.prototype = subtype;
subtype.$super = this;
return subtype;
},
create: function () {
var instance = this.extend();
instance.init.apply(instance, arguments);
return instance;
},
init: function () {},
mixIn: function (properties) {
for (var propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
this[propertyName] = properties[propertyName];
}
}
if (properties.hasOwnProperty('toString')) {
this.toString = properties.toString;
}
},
clone: function () {
return this.init.prototype.extend(this);
}
};
}
());
var WordArray = C_lib.WordArray = Base.extend({
init: function (words, sigBytes) {
words = this.words = words || [];
if (sigBytes != undefined) {
this.sigBytes = sigBytes;
} else {
this.sigBytes = words.length * 4;
}
},
toString: function (encoder) {
return (encoder || Hex).stringify(this);
},
concat: function (wordArray) {
var thisWords = this.words;
var thatWords = wordArray.words;
var thisSigBytes = this.sigBytes;
var thatSigBytes = wordArray.sigBytes;
this.clamp();
if (thisSigBytes % 4) {
for (var i = 0; i < thatSigBytes; i++) {
var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8);
}
} else {
for (var i = 0; i < thatSigBytes; i += 4) {
thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2];
}
}
this.sigBytes += thatSigBytes;
return this;
},
clamp: function () {
var words = this.words;
var sigBytes = this.sigBytes;
words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8);
words.length = Math.ceil(sigBytes / 4);
},
clone: function () {
var clone = Base.clone.call(this);
clone.words = this.words.slice(0);
return clone;
},
random: function (nBytes) {
var words = [];
var r = (function (m_w) {
var m_w = m_w;
var m_z = 0x3ade68b1;
var mask = 0xffffffff;
return function () {
m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask;
m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask;
var result = ((m_z << 0x10) + m_w) & mask;
result /= 0x100000000;
result += 0.5;
return result * (Math.random() > .5 ? 1 : -1);
}
});
for (var i = 0, rcache; i < nBytes; i += 4) {
var _r = r((rcache || Math.random()) * 0x100000000);
rcache = _r() * 0x3ade67b7;
words.push((_r() * 0x100000000) | 0);
}
return new WordArray.init(words, nBytes);
}
});
var C_enc = C.enc = {};
var Hex = C_enc.Hex = {
stringify: function (wordArray) {
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
var hexChars = [];
for (var i = 0; i < sigBytes; i++) {
var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
hexChars.push((bite >>> 4).toString(16));
hexChars.push((bite & 0x0f).toString(16));
}
return hexChars.join('');
},
parse: function (hexStr) {
var hexStrLength = hexStr.length;
var words = [];
for (var i = 0; i < hexStrLength; i += 2) {
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
}
return new WordArray.init(words, hexStrLength / 2);
}
};
var Latin1 = C_enc.Latin1 = {
stringify: function (wordArray) {
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
var latin1Chars = [];
for (var i = 0; i < sigBytes; i++) {
var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
latin1Chars.push(String.fromCharCode(bite));
}
return latin1Chars.join('');
},
parse: function (latin1Str) {
var latin1StrLength = latin1Str.length;
var words = [];
for (var i = 0; i < latin1StrLength; i++) {
words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8);
}
return new WordArray.init(words, latin1StrLength);
}
};
var Utf8 = C_enc.Utf8 = {
stringify: function (wordArray) {
try {
return decodeURIComponent(escape(Latin1.stringify(wordArray)));
} catch (e) {
throw new Error('Malformed UTF-8 data');
}
},
parse: function (utf8Str) {
return Latin1.parse(unescape(encodeURIComponent(utf8Str)));
}
};
var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({
reset: function () {
this._data = new WordArray.init();
this._nDataBytes = 0;
},
_append: function (data) {
if (typeof data == 'string') {
data = Utf8.parse(data);
}
this._data.concat(data);
this._nDataBytes += data.sigBytes;
},
_process: function (doFlush) {
var data = this._data;
var dataWords = data.words;
var dataSigBytes = data.sigBytes;
var blockSize = this.blockSize;
var blockSizeBytes = blockSize * 4;
var nBlocksReady = dataSigBytes / blockSizeBytes;
if (doFlush) {
nBlocksReady = Math.ceil(nBlocksReady);
} else {
nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);
}
var nWordsReady = nBlocksReady * blockSize;
var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);
if (nWordsReady) {
for (var offset = 0; offset < nWordsReady; offset += blockSize) {
this._doProcessBlock(dataWords, offset);
}
var processedWords = dataWords.splice(0, nWordsReady);
data.sigBytes -= nBytesReady;
}
return new WordArray.init(processedWords, nBytesReady);
},
clone: function () {
var clone = Base.clone.call(this);
clone._data = this._data.clone();
return clone;
},
_minBufferSize: 0
});
var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({
cfg: Base.extend(),
init: function (cfg) {
this.cfg = this.cfg.extend(cfg);
this.reset();
},
reset: function () {
BufferedBlockAlgorithm.reset.call(this);
this._doReset();
},
update: function (messageUpdate) {
this._append(messageUpdate);
this._process();
return this;
},
finalize: function (messageUpdate) {
if (messageUpdate) {
this._append(messageUpdate);
}
var hash = this._doFinalize();
return hash;
},
blockSize: 512 / 32,
_createHelper: function (hasher) {
return function (message, cfg) {
return new hasher.init(cfg).finalize(message);
};
},
_createHmacHelper: function (hasher) {
return function (message, key) {
return new C_algo.HMAC.init(hasher, key).finalize(message);
};
}
});
var C_algo = C.algo = {};
return C;
}
(Math));
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var C_enc = C.enc;
var Base64 = C_enc.Base64 = {
stringify: function (wordArray) {
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
var map = this._map;
wordArray.clamp();
var base64Chars = [];
for (var i = 0; i < sigBytes; i += 3) {
var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff;
var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff;
var triplet = (byte1 << 16) | (byte2 << 8) | byte3;
for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) {
base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f));
}
}
var paddingChar = map.charAt(64);
if (paddingChar) {
while (base64Chars.length % 4) {
base64Chars.push(paddingChar);
}
}
return base64Chars.join('');
},
parse: function (base64Str) {
var base64StrLength = base64Str.length;
var map = this._map;
var reverseMap = this._reverseMap;
if (!reverseMap) {
reverseMap = this._reverseMap = [];
for (var j = 0; j < map.length; j++) {
reverseMap[map.charCodeAt(j)] = j;
}
}
var paddingChar = map.charAt(64);
if (paddingChar) {
var paddingIndex = base64Str.indexOf(paddingChar);
if (paddingIndex !== -1) {
base64StrLength = paddingIndex;
}
}
return parseLoop(base64Str, base64StrLength, reverseMap);
},
_map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
};
function parseLoop(base64Str, base64StrLength, reverseMap) {
var words = [];
var nBytes = 0;
for (var i = 0; i < base64StrLength; i++) {
if (i % 4) {
var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2);
var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2);
words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8);
nBytes++;
}
}
return WordArray.create(words, nBytes);
}
}
());
(function (Math) {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var Hasher = C_lib.Hasher;
var C_algo = C.algo;
var T = [];
(function () {
for (var i = 0; i < 64; i++) {
T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0;
}
}
());
var MD5 = C_algo.MD5 = Hasher.extend({
_doReset: function () {
this._hash = new WordArray.init([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]);
},
_doProcessBlock: function (M, offset) {
for (var i = 0; i < 16; i++) {
var offset_i = offset + i;
var M_offset_i = M[offset_i];
M[offset_i] = ((((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00));
}
var H = this._hash.words;
var M_offset_0 = M[offset + 0];
var M_offset_1 = M[offset + 1];
var M_offset_2 = M[offset + 2];
var M_offset_3 = M[offset + 3];
var M_offset_4 = M[offset + 4];
var M_offset_5 = M[offset + 5];
var M_offset_6 = M[offset + 6];
var M_offset_7 = M[offset + 7];
var M_offset_8 = M[offset + 8];
var M_offset_9 = M[offset + 9];
var M_offset_10 = M[offset + 10];
var M_offset_11 = M[offset + 11];
var M_offset_12 = M[offset + 12];
var M_offset_13 = M[offset + 13];
var M_offset_14 = M[offset + 14];
var M_offset_15 = M[offset + 15];
var a = H[0];
var b = H[1];
var c = H[2];
var d = H[3];
a = FF(a, b, c, d, M_offset_0, 7, T[0]);
d = FF(d, a, b, c, M_offset_1, 12, T[1]);
c = FF(c, d, a, b, M_offset_2, 17, T[2]);
b = FF(b, c, d, a, M_offset_3, 22, T[3]);
a = FF(a, b, c, d, M_offset_4, 7, T[4]);
d = FF(d, a, b, c, M_offset_5, 12, T[5]);
c = FF(c, d, a, b, M_offset_6, 17, T[6]);
b = FF(b, c, d, a, M_offset_7, 22, T[7]);
a = FF(a, b, c, d, M_offset_8, 7, T[8]);
d = FF(d, a, b, c, M_offset_9, 12, T[9]);
c = FF(c, d, a, b, M_offset_10, 17, T[10]);
b = FF(b, c, d, a, M_offset_11, 22, T[11]);
a = FF(a, b, c, d, M_offset_12, 7, T[12]);
d = FF(d, a, b, c, M_offset_13, 12, T[13]);
c = FF(c, d, a, b, M_offset_14, 17, T[14]);
b = FF(b, c, d, a, M_offset_15, 22, T[15]);
a = GG(a, b, c, d, M_offset_1, 5, T[16]);
d = GG(d, a, b, c, M_offset_6, 9, T[17]);
c = GG(c, d, a, b, M_offset_11, 14, T[18]);
b = GG(b, c, d, a, M_offset_0, 20, T[19]);
a = GG(a, b, c, d, M_offset_5, 5, T[20]);
d = GG(d, a, b, c, M_offset_10, 9, T[21]);
c = GG(c, d, a, b, M_offset_15, 14, T[22]);
b = GG(b, c, d, a, M_offset_4, 20, T[23]);
a = GG(a, b, c, d, M_offset_9, 5, T[24]);
d = GG(d, a, b, c, M_offset_14, 9, T[25]);
c = GG(c, d, a, b, M_offset_3, 14, T[26]);
b = GG(b, c, d, a, M_offset_8, 20, T[27]);
a = GG(a, b, c, d, M_offset_13, 5, T[28]);
d = GG(d, a, b, c, M_offset_2, 9, T[29]);
c = GG(c, d, a, b, M_offset_7, 14, T[30]);
b = GG(b, c, d, a, M_offset_12, 20, T[31]);
a = HH(a, b, c, d, M_offset_5, 4, T[32]);
d = HH(d, a, b, c, M_offset_8, 11, T[33]);
c = HH(c, d, a, b, M_offset_11, 16, T[34]);
b = HH(b, c, d, a, M_offset_14, 23, T[35]);
a = HH(a, b, c, d, M_offset_1, 4, T[36]);
d = HH(d, a, b, c, M_offset_4, 11, T[37]);
c = HH(c, d, a, b, M_offset_7, 16, T[38]);
b = HH(b, c, d, a, M_offset_10, 23, T[39]);
a = HH(a, b, c, d, M_offset_13, 4, T[40]);
d = HH(d, a, b, c, M_offset_0, 11, T[41]);
c = HH(c, d, a, b, M_offset_3, 16, T[42]);
b = HH(b, c, d, a, M_offset_6, 23, T[43]);
a = HH(a, b, c, d, M_offset_9, 4, T[44]);
d = HH(d, a, b, c, M_offset_12, 11, T[45]);
c = HH(c, d, a, b, M_offset_15, 16, T[46]);
b = HH(b, c, d, a, M_offset_2, 23, T[47]);
a = II(a, b, c, d, M_offset_0, 6, T[48]);
d = II(d, a, b, c, M_offset_7, 10, T[49]);
c = II(c, d, a, b, M_offset_14, 15, T[50]);
b = II(b, c, d, a, M_offset_5, 21, T[51]);
a = II(a, b, c, d, M_offset_12, 6, T[52]);
d = II(d, a, b, c, M_offset_3, 10, T[53]);
c = II(c, d, a, b, M_offset_10, 15, T[54]);
b = II(b, c, d, a, M_offset_1, 21, T[55]);
a = II(a, b, c, d, M_offset_8, 6, T[56]);
d = II(d, a, b, c, M_offset_15, 10, T[57]);
c = II(c, d, a, b, M_offset_6, 15, T[58]);
b = II(b, c, d, a, M_offset_13, 21, T[59]);
a = II(a, b, c, d, M_offset_4, 6, T[60]);
d = II(d, a, b, c, M_offset_11, 10, T[61]);
c = II(c, d, a, b, M_offset_2, 15, T[62]);
b = II(b, c, d, a, M_offset_9, 21, T[63]);
H[0] = (H[0] + a) | 0;
H[1] = (H[1] + b) | 0;
H[2] = (H[2] + c) | 0;
H[3] = (H[3] + d) | 0;
},
_doFinalize: function () {
var data = this._data;
var dataWords = data.words;
var nBitsTotal = this._nDataBytes * 8;
var nBitsLeft = data.sigBytes * 8;
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);
var nBitsTotalL = nBitsTotal;
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = ((((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) | (((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00));
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ((((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) | (((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00));
data.sigBytes = (dataWords.length + 1) * 4;
this._process();
var hash = this._hash;
var H = hash.words;
for (var i = 0; i < 4; i++) {
var H_i = H[i];
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
}
return hash;
},
clone: function () {
var clone = Hasher.clone.call(this);
clone._hash = this._hash.clone();
return clone;
}
});
function FF(a, b, c, d, x, s, t) {
var n = a + ((b & c) | (~b & d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function GG(a, b, c, d, x, s, t) {
var n = a + ((b & d) | (c & ~d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function HH(a, b, c, d, x, s, t) {
var n = a + (b ^ c ^ d) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
function II(a, b, c, d, x, s, t) {
var n = a + (c ^ (b | ~d)) + x + t;
return ((n << s) | (n >>> (32 - s))) + b;
}
C.MD5 = Hasher._createHelper(MD5);
C.HmacMD5 = Hasher._createHmacHelper(MD5);
}
(Math));
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var Hasher = C_lib.Hasher;
var C_algo = C.algo;
var W = [];
var SHA1 = C_algo.SHA1 = Hasher.extend({
_doReset: function () {
this._hash = new WordArray.init([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]);
},
_doProcessBlock: function (M, offset) {
var H = this._hash.words;
var a = H[0];
var b = H[1];
var c = H[2];
var d = H[3];
var e = H[4];
for (var i = 0; i < 80; i++) {
if (i < 16) {
W[i] = M[offset + i] | 0;
} else {
var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
W[i] = (n << 1) | (n >>> 31);
}
var t = ((a << 5) | (a >>> 27)) + e + W[i];
if (i < 20) {
t += ((b & c) | (~b & d)) + 0x5a827999;
} else if (i < 40) {
t += (b ^ c ^ d) + 0x6ed9eba1;
} else if (i < 60) {
t += ((b & c) | (b & d) | (c & d)) - 0x70e44324;
} else {
t += (b ^ c ^ d) - 0x359d3e2a;
}
e = d;
d = c;
c = (b << 30) | (b >>> 2);
b = a;
a = t;
}
H[0] = (H[0] + a) | 0;
H[1] = (H[1] + b) | 0;
H[2] = (H[2] + c) | 0;
H[3] = (H[3] + d) | 0;
H[4] = (H[4] + e) | 0;
},
_doFinalize: function () {
var data = this._data;
var dataWords = data.words;
var nBitsTotal = this._nDataBytes * 8;
var nBitsLeft = data.sigBytes * 8;
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
data.sigBytes = dataWords.length * 4;
this._process();
return this._hash;
},
clone: function () {
var clone = Hasher.clone.call(this);
clone._hash = this._hash.clone();
return clone;
}
});
C.SHA1 = Hasher._createHelper(SHA1);
C.HmacSHA1 = Hasher._createHmacHelper(SHA1);
}
());
(function (Math) {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var Hasher = C_lib.Hasher;
var C_algo = C.algo;
var H = [];
var K = [];
(function () {
function isPrime(n) {
var sqrtN = Math.sqrt(n);
for (var factor = 2; factor <= sqrtN; factor++) {
if (!(n % factor)) {
return false;
}
}
return true;
}
function getFractionalBits(n) {
return ((n - (n | 0)) * 0x100000000) | 0;
}
var n = 2;
var nPrime = 0;
while (nPrime < 64) {
if (isPrime(n)) {
if (nPrime < 8) {
H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
}
K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
nPrime++;
}
n++;
}
}
());
var W = [];
var SHA256 = C_algo.SHA256 = Hasher.extend({
_doReset: function () {
this._hash = new WordArray.init(H.slice(0));
},
_doProcessBlock: function (M, offset) {
var H = this._hash.words;
var a = H[0];
var b = H[1];
var c = H[2];
var d = H[3];
var e = H[4];
var f = H[5];
var g = H[6];
var h = H[7];
for (var i = 0; i < 64; i++) {
if (i < 16) {
W[i] = M[offset + i] | 0;
} else {
var gamma0x = W[i - 15];
var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ ((gamma0x << 14) | (gamma0x >>> 18)) ^ (gamma0x >>> 3);
var gamma1x = W[i - 2];
var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ ((gamma1x << 13) | (gamma1x >>> 19)) ^ (gamma1x >>> 10);
W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
}
var ch = (e & f) ^ (~e & g);
var maj = (a & b) ^ (a & c) ^ (b & c);
var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
var t1 = h + sigma1 + ch + K[i] + W[i];
var t2 = sigma0 + maj;
h = g;
g = f;
f = e;
e = (d + t1) | 0;
d = c;
c = b;
b = a;
a = (t1 + t2) | 0;
}
H[0] = (H[0] + a) | 0;
H[1] = (H[1] + b) | 0;
H[2] = (H[2] + c) | 0;
H[3] = (H[3] + d) | 0;
H[4] = (H[4] + e) | 0;
H[5] = (H[5] + f) | 0;
H[6] = (H[6] + g) | 0;
H[7] = (H[7] + h) | 0;
},
_doFinalize: function () {
var data = this._data;
var dataWords = data.words;
var nBitsTotal = this._nDataBytes * 8;
var nBitsLeft = data.sigBytes * 8;
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
data.sigBytes = dataWords.length * 4;
this._process();
return this._hash;
},
clone: function () {
var clone = Hasher.clone.call(this);
clone._hash = this._hash.clone();
return clone;
}
});
C.SHA256 = Hasher._createHelper(SHA256);
C.HmacSHA256 = Hasher._createHmacHelper(SHA256);
}
(Math));
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var C_enc = C.enc;
var Utf16BE = C_enc.Utf16 = C_enc.Utf16BE = {
stringify: function (wordArray) {
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
var utf16Chars = [];
for (var i = 0; i < sigBytes; i += 2) {
var codePoint = (words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff;
utf16Chars.push(String.fromCharCode(codePoint));
}
return utf16Chars.join('');
},
parse: function (utf16Str) {
var utf16StrLength = utf16Str.length;
var words = [];
for (var i = 0; i < utf16StrLength; i++) {
words[i >>> 1] |= utf16Str.charCodeAt(i) << (16 - (i % 2) * 16);
}
return WordArray.create(words, utf16StrLength * 2);
}
};
C_enc.Utf16LE = {
stringify: function (wordArray) {
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
var utf16Chars = [];
for (var i = 0; i < sigBytes; i += 2) {
var codePoint = swapEndian((words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff);
utf16Chars.push(String.fromCharCode(codePoint));
}
return utf16Chars.join('');
},
parse: function (utf16Str) {
var utf16StrLength = utf16Str.length;
var words = [];
for (var i = 0; i < utf16StrLength; i++) {
words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << (16 - (i % 2) * 16));
}
return WordArray.create(words, utf16StrLength * 2);
}
};
function swapEndian(word) {
return ((word << 8) & 0xff00ff00) | ((word >>> 8) & 0x00ff00ff);
}
}
());
(function () {
if (typeof ArrayBuffer != 'function') {
return;
}
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var superInit = WordArray.init;
var subInit = WordArray.init = function (typedArray) {
if (typedArray instanceof ArrayBuffer) {
typedArray = new Uint8Array(typedArray);
}
if (typedArray instanceof Int8Array || (typeof Uint8ClampedArray !== "undefined" && typedArray instanceof Uint8ClampedArray) || typedArray instanceof Int16Array || typedArray instanceof Uint16Array || typedArray instanceof Int32Array || typedArray instanceof Uint32Array || typedArray instanceof Float32Array || typedArray instanceof Float64Array) {
typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength);
}
if (typedArray instanceof Uint8Array) {
var typedArrayByteLength = typedArray.byteLength;
var words = [];
for (var i = 0; i < typedArrayByteLength; i++) {
words[i >>> 2] |= typedArray[i] << (24 - (i % 4) * 8);
}
superInit.call(this, words, typedArrayByteLength);
} else {
superInit.apply(this, arguments);
}
};
subInit.prototype = WordArray;
}
());
(function (Math) {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var Hasher = C_lib.Hasher;
var C_algo = C.algo;
var _zl = WordArray.create([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]);
var _zr = WordArray.create([5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]);
var _sl = WordArray.create([11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6]);
var _sr = WordArray.create([8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]);
var _hl = WordArray.create([0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]);
var _hr = WordArray.create([0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]);
var RIPEMD160 = C_algo.RIPEMD160 = Hasher.extend({
_doReset: function () {
this._hash = WordArray.create([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]);
},
_doProcessBlock: function (M, offset) {
for (var i = 0; i < 16; i++) {
var offset_i = offset + i;
var M_offset_i = M[offset_i];
M[offset_i] = ((((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00));
}
var H = this._hash.words;
var hl = _hl.words;
var hr = _hr.words;
var zl = _zl.words;
var zr = _zr.words;
var sl = _sl.words;
var sr = _sr.words;
var al,
bl,
cl,
dl,
el;
var ar,
br,
cr,
dr,
er;
ar = al = H[0];
br = bl = H[1];
cr = cl = H[2];
dr = dl = H[3];
er = el = H[4];
var t;
for (var i = 0; i < 80; i += 1) {
t = (al + M[offset + zl[i]]) | 0;
if (i < 16) {
t += f1(bl, cl, dl) + hl[0];
} else if (i < 32) {
t += f2(bl, cl, dl) + hl[1];
} else if (i < 48) {
t += f3(bl, cl, dl) + hl[2];
} else if (i < 64) {
t += f4(bl, cl, dl) + hl[3];
} else {
t += f5(bl, cl, dl) + hl[4];
}
t = t | 0;
t = rotl(t, sl[i]);
t = (t + el) | 0;
al = el;
el = dl;
dl = rotl(cl, 10);
cl = bl;
bl = t;
t = (ar + M[offset + zr[i]]) | 0;
if (i < 16) {
t += f5(br, cr, dr) + hr[0];
} else if (i < 32) {
t += f4(br, cr, dr) + hr[1];
} else if (i < 48) {
t += f3(br, cr, dr) + hr[2];
} else if (i < 64) {
t += f2(br, cr, dr) + hr[3];
} else {
t += f1(br, cr, dr) + hr[4];
}
t = t | 0;
t = rotl(t, sr[i]);
t = (t + er) | 0;
ar = er;
er = dr;
dr = rotl(cr, 10);
cr = br;
br = t;
}
t = (H[1] + cl + dr) | 0;
H[1] = (H[2] + dl + er) | 0;
H[2] = (H[3] + el + ar) | 0;
H[3] = (H[4] + al + br) | 0;
H[4] = (H[0] + bl + cr) | 0;
H[0] = t;
},
_doFinalize: function () {
var data = this._data;
var dataWords = data.words;
var nBitsTotal = this._nDataBytes * 8;
var nBitsLeft = data.sigBytes * 8;
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ((((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) | (((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00));
data.sigBytes = (dataWords.length + 1) * 4;
this._process();
var hash = this._hash;
var H = hash.words;
for (var i = 0; i < 5; i++) {
var H_i = H[i];
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
}
return hash;
},
clone: function () {
var clone = Hasher.clone.call(this);
clone._hash = this._hash.clone();
return clone;
}
});
function f1(x, y, z) {
return ((x) ^ (y) ^ (z));
}
function f2(x, y, z) {
return (((x) & (y)) | ((~x) & (z)));
}
function f3(x, y, z) {
return (((x) | (~(y))) ^ (z));
}
function f4(x, y, z) {
return (((x) & (z)) | ((y) & (~(z))));
}
function f5(x, y, z) {
return ((x) ^ ((y) | (~(z))));
}
function rotl(x, n) {
return (x << n) | (x >>> (32 - n));
}
C.RIPEMD160 = Hasher._createHelper(RIPEMD160);
C.HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160);
}
(Math));
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var Base = C_lib.Base;
var C_enc = C.enc;
var Utf8 = C_enc.Utf8;
var C_algo = C.algo;
var HMAC = C_algo.HMAC = Base.extend({
init: function (hasher, key) {
hasher = this._hasher = new hasher.init();
if (typeof key == 'string') {
key = Utf8.parse(key);
}
var hasherBlockSize = hasher.blockSize;
var hasherBlockSizeBytes = hasherBlockSize * 4;
if (key.sigBytes > hasherBlockSizeBytes) {
key = hasher.finalize(key);
}
key.clamp();
var oKey = this._oKey = key.clone();
var iKey = this._iKey = key.clone();
var oKeyWords = oKey.words;
var iKeyWords = iKey.words;
for (var i = 0; i < hasherBlockSize; i++) {
oKeyWords[i] ^= 0x5c5c5c5c;
iKeyWords[i] ^= 0x36363636;
}
oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes;
this.reset();
},
reset: function () {
var hasher = this._hasher;
hasher.reset();
hasher.update(this._iKey);
},
update: function (messageUpdate) {
this._hasher.update(messageUpdate);
return this;
},
finalize: function (messageUpdate) {
var hasher = this._hasher;
var innerHash = hasher.finalize(messageUpdate);
hasher.reset();
var hmac = hasher.finalize(this._oKey.clone().concat(innerHash));
return hmac;
}
});
}
());
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var Base = C_lib.Base;
var WordArray = C_lib.WordArray;
var C_algo = C.algo;
var SHA1 = C_algo.SHA1;
var HMAC = C_algo.HMAC;
var PBKDF2 = C_algo.PBKDF2 = Base.extend({
cfg: Base.extend({
keySize: 128 / 32,
hasher: SHA1,
iterations: 1
}),
init: function (cfg) {
this.cfg = this.cfg.extend(cfg);
},
compute: function (password, salt) {
var cfg = this.cfg;
var hmac = HMAC.create(cfg.hasher, password);
var derivedKey = WordArray.create();
var blockIndex = WordArray.create([0x00000001]);
var derivedKeyWords = derivedKey.words;
var blockIndexWords = blockIndex.words;
var keySize = cfg.keySize;
var iterations = cfg.iterations;
while (derivedKeyWords.length < keySize) {
var block = hmac.update(salt).finalize(blockIndex);
hmac.reset();
var blockWords = block.words;
var blockWordsLength = blockWords.length;
var intermediate = block;
for (var i = 1; i < iterations; i++) {
intermediate = hmac.finalize(intermediate);
hmac.reset();
var intermediateWords = intermediate.words;
for (var j = 0; j < blockWordsLength; j++) {
blockWords[j] ^= intermediateWords[j];
}
}
derivedKey.concat(block);
blockIndexWords[0]++;
}
derivedKey.sigBytes = keySize * 4;
return derivedKey;
}
});
C.PBKDF2 = function (password, salt, cfg) {
return PBKDF2.create(cfg).compute(password, salt);
};
}
());
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var Base = C_lib.Base;
var WordArray = C_lib.WordArray;
var C_algo = C.algo;
var MD5 = C_algo.MD5;
var EvpKDF = C_algo.EvpKDF = Base.extend({
cfg: Base.extend({
keySize: 128 / 32,
hasher: MD5,
iterations: 1
}),
init: function (cfg) {
this.cfg = this.cfg.extend(cfg);
},
compute: function (password, salt) {
var cfg = this.cfg;
var hasher = cfg.hasher.create();
var derivedKey = WordArray.create();
var derivedKeyWords = derivedKey.words;
var keySize = cfg.keySize;
var iterations = cfg.iterations;
while (derivedKeyWords.length < keySize) {
if (block) {
hasher.update(block);
}
var block = hasher.update(password).finalize(salt);
hasher.reset();
for (var i = 1; i < iterations; i++) {
block = hasher.finalize(block);
hasher.reset();
}
derivedKey.concat(block);
}
derivedKey.sigBytes = keySize * 4;
return derivedKey;
}
});
C.EvpKDF = function (password, salt, cfg) {
return EvpKDF.create(cfg).compute(password, salt);
};
}
());
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var C_algo = C.algo;
var SHA256 = C_algo.SHA256;
var SHA224 = C_algo.SHA224 = SHA256.extend({
_doReset: function () {
this._hash = new WordArray.init([0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4]);
},
_doFinalize: function () {
var hash = SHA256._doFinalize.call(this);
hash.sigBytes -= 4;
return hash;
}
});
C.SHA224 = SHA256._createHelper(SHA224);
C.HmacSHA224 = SHA256._createHmacHelper(SHA224);
}
());
(function (undefined) {
var C = CryptoJS;
var C_lib = C.lib;
var Base = C_lib.Base;
var X32WordArray = C_lib.WordArray;
var C_x64 = C.x64 = {};
var X64Word = C_x64.Word = Base.extend({
init: function (high, low) {
this.high = high;
this.low = low;
}
});
var X64WordArray = C_x64.WordArray = Base.extend({
init: function (words, sigBytes) {
words = this.words = words || [];
if (sigBytes != undefined) {
this.sigBytes = sigBytes;
} else {
this.sigBytes = words.length * 8;
}
},
toX32: function () {
var x64Words = this.words;
var x64WordsLength = x64Words.length;
var x32Words = [];
for (var i = 0; i < x64WordsLength; i++) {
var x64Word = x64Words[i];
x32Words.push(x64Word.high);
x32Words.push(x64Word.low);
}
return X32WordArray.create(x32Words, this.sigBytes);
},
clone: function () {
var clone = Base.clone.call(this);
var words = clone.words = this.words.slice(0);
var wordsLength = words.length;
for (var i = 0; i < wordsLength; i++) {
words[i] = words[i].clone();
}
return clone;
}
});
}
());
(function (Math) {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var Hasher = C_lib.Hasher;
var C_x64 = C.x64;
var X64Word = C_x64.Word;
var C_algo = C.algo;
var RHO_OFFSETS = [];
var PI_INDEXES = [];
var ROUND_CONSTANTS = [];
(function () {
var x = 1,
y = 0;
for (var t = 0; t < 24; t++) {
RHO_OFFSETS[x + 5 * y] = ((t + 1) * (t + 2) / 2) % 64;
var newX = y % 5;
var newY = (2 * x + 3 * y) % 5;
x = newX;
y = newY;
}
for (var x = 0; x < 5; x++) {
for (var y = 0; y < 5; y++) {
PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5;
}
}
var LFSR = 0x01;
for (var i = 0; i < 24; i++) {
var roundConstantMsw = 0;
var roundConstantLsw = 0;
for (var j = 0; j < 7; j++) {
if (LFSR & 0x01) {
var bitPosition = (1 << j) - 1;
if (bitPosition < 32) {
roundConstantLsw ^= 1 << bitPosition;
} else {
roundConstantMsw ^= 1 << (bitPosition - 32);
}
}
if (LFSR & 0x80) {
LFSR = (LFSR << 1) ^ 0x71;
} else {
LFSR <<= 1;
}
}
ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw);
}
}
());
var T = [];
(function () {
for (var i = 0; i < 25; i++) {
T[i] = X64Word.create();
}
}
());
var SHA3 = C_algo.SHA3 = Hasher.extend({
cfg: Hasher.cfg.extend({
outputLength: 512
}),
_doReset: function () {
var state = this._state = [];
for (var i = 0; i < 25; i++) {
state[i] = new X64Word.init();
}
this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32;
},
_doProcessBlock: function (M, offset) {
var state = this._state;
var nBlockSizeLanes = this.blockSize / 2;
for (var i = 0; i < nBlockSizeLanes; i++) {
var M2i = M[offset + 2 * i];
var M2i1 = M[offset + 2 * i + 1];
M2i = ((((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff) | (((M2i << 24) | (M2i >>> 8)) & 0xff00ff00));
M2i1 = ((((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff) | (((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00));
var lane = state[i];
lane.high ^= M2i1;
lane.low ^= M2i;
}
for (var round = 0; round < 24; round++) {
for (var x = 0; x < 5; x++) {
var tMsw = 0,
tLsw = 0;
for (var y = 0; y < 5; y++) {
var lane = state[x + 5 * y];
tMsw ^= lane.high;
tLsw ^= lane.low;
}
var Tx = T[x];
Tx.high = tMsw;
Tx.low = tLsw;
}
for (var x = 0; x < 5; x++) {
var Tx4 = T[(x + 4) % 5];
var Tx1 = T[(x + 1) % 5];
var Tx1Msw = Tx1.high;
var Tx1Lsw = Tx1.low;
var tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31));
var tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31));
for (var y = 0; y < 5; y++) {
var lane = state[x + 5 * y];
lane.high ^= tMsw;
lane.low ^= tLsw;
}
}
for (var laneIndex = 1; laneIndex < 25; laneIndex++) {
var lane = state[laneIndex];
var laneMsw = lane.high;
var laneLsw = lane.low;
var rhoOffset = RHO_OFFSETS[laneIndex];
if (rhoOffset < 32) {
var tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset));
var tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset));
} else {
var tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset));
var tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset));
}
var TPiLane = T[PI_INDEXES[laneIndex]];
TPiLane.high = tMsw;
TPiLane.low = tLsw;
}
var T0 = T[0];
var state0 = state[0];
T0.high = state0.high;
T0.low = state0.low;
for (var x = 0; x < 5; x++) {
for (var y = 0; y < 5; y++) {
var laneIndex = x + 5 * y;
var lane = state[laneIndex];
var TLane = T[laneIndex];
var Tx1Lane = T[((x + 1) % 5) + 5 * y];
var Tx2Lane = T[((x + 2) % 5) + 5 * y];
lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high);
lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low);
}
}
var lane = state[0];
var roundConstant = ROUND_CONSTANTS[round];
lane.high ^= roundConstant.high;
lane.low ^= roundConstant.low; ;
}
},
_doFinalize: function () {
var data = this._data;
var dataWords = data.words;
var nBitsTotal = this._nDataBytes * 8;
var nBitsLeft = data.sigBytes * 8;
var blockSizeBits = this.blockSize * 32;
dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - nBitsLeft % 32);
dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80;
data.sigBytes = dataWords.length * 4;
this._process();
var state = this._state;
var outputLengthBytes = this.cfg.outputLength / 8;
var outputLengthLanes = outputLengthBytes / 8;
var hashWords = [];
for (var i = 0; i < outputLengthLanes; i++) {
var lane = state[i];
var laneMsw = lane.high;
var laneLsw = lane.low;
laneMsw = ((((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff) | (((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00));
laneLsw = ((((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff) | (((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00));
hashWords.push(laneLsw);
hashWords.push(laneMsw);
}
return new WordArray.init(hashWords, outputLengthBytes);
},
clone: function () {
var clone = Hasher.clone.call(this);
var state = clone._state = this._state.slice(0);
for (var i = 0; i < 25; i++) {
state[i] = state[i].clone();
}
return clone;
}
});
C.SHA3 = Hasher._createHelper(SHA3);
C.HmacSHA3 = Hasher._createHmacHelper(SHA3);
}
(Math));
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var Hasher = C_lib.Hasher;
var C_x64 = C.x64;
var X64Word = C_x64.Word;
var X64WordArray = C_x64.WordArray;
var C_algo = C.algo;
function X64Word_create() {
return X64Word.create.apply(X64Word, arguments);
}
var K = [X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd), X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc), X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019), X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118), X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe), X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2), X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1), X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694), X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3), X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65), X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483), X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5), X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210), X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4), X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725), X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70), X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926), X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df), X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8), X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b), X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001), X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30), X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910), X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8), X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53), X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8), X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb), X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3), X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60), X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec), X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9), X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b), X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207), X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178), X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6), X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b), X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493), X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c), X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a), X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817)];
var W = [];
(function () {
for (var i = 0; i < 80; i++) {
W[i] = X64Word_create();
}
}
());
var SHA512 = C_algo.SHA512 = Hasher.extend({
_doReset: function () {
this._hash = new X64WordArray.init([new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b), new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1), new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f), new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179)]);
},
_doProcessBlock: function (M, offset) {
var H = this._hash.words;
var H0 = H[0];
var H1 = H[1];
var H2 = H[2];
var H3 = H[3];
var H4 = H[4];
var H5 = H[5];
var H6 = H[6];
var H7 = H[7];
var H0h = H0.high;
var H0l = H0.low;
var H1h = H1.high;
var H1l = H1.low;
var H2h = H2.high;
var H2l = H2.low;
var H3h = H3.high;
var H3l = H3.low;
var H4h = H4.high;
var H4l = H4.low;
var H5h = H5.high;
var H5l = H5.low;
var H6h = H6.high;
var H6l = H6.low;
var H7h = H7.high;
var H7l = H7.low;
var ah = H0h;
var al = H0l;
var bh = H1h;
var bl = H1l;
var ch = H2h;
var cl = H2l;
var dh = H3h;
var dl = H3l;
var eh = H4h;
var el = H4l;
var fh = H5h;
var fl = H5l;
var gh = H6h;
var gl = H6l;
var hh = H7h;
var hl = H7l;
for (var i = 0; i < 80; i++) {
var Wi = W[i];
if (i < 16) {
var Wih = Wi.high = M[offset + i * 2] | 0;
var Wil = Wi.low = M[offset + i * 2 + 1] | 0;
} else {
var gamma0x = W[i - 15];
var gamma0xh = gamma0x.high;
var gamma0xl = gamma0x.low;
var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7);
var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25));
var gamma1x = W[i - 2];
var gamma1xh = gamma1x.high;
var gamma1xl = gamma1x.low;
var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6);
var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26));
var Wi7 = W[i - 7];
var Wi7h = Wi7.high;
var Wi7l = Wi7.low;
var Wi16 = W[i - 16];
var Wi16h = Wi16.high;
var Wi16l = Wi16.low;
var Wil = gamma0l + Wi7l;
var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0);
var Wil = Wil + gamma1l;
var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0);
var Wil = Wil + Wi16l;
var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0);
Wi.high = Wih;
Wi.low = Wil;
}
var chh = (eh & fh) ^ (~eh & gh);
var chl = (el & fl) ^ (~el & gl);
var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch);
var majl = (al & bl) ^ (al & cl) ^ (bl & cl);
var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7));
var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7));
var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9));
var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9));
var Ki = K[i];
var Kih = Ki.high;
var Kil = Ki.low;
var t1l = hl + sigma1l;
var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0);
var t1l = t1l + chl;
var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);
var t1l = t1l + Kil;
var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0);
var t1l = t1l + Wil;
var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0);
var t2l = sigma0l + majl;
var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0);
hh = gh;
hl = gl;
gh = fh;
gl = fl;
fh = eh;
fl = el;
el = (dl + t1l) | 0;
eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0;
dh = ch;
dl = cl;
ch = bh;
cl = bl;
bh = ah;
bl = al;
al = (t1l + t2l) | 0;
ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0;
}
H0l = H0.low = (H0l + al);
H0.high = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0));
H1l = H1.low = (H1l + bl);
H1.high = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0));
H2l = H2.low = (H2l + cl);
H2.high = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0));
H3l = H3.low = (H3l + dl);
H3.high = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0));
H4l = H4.low = (H4l + el);
H4.high = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0));
H5l = H5.low = (H5l + fl);
H5.high = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0));
H6l = H6.low = (H6l + gl);
H6.high = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0));
H7l = H7.low = (H7l + hl);
H7.high = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0));
},
_doFinalize: function () {
var data = this._data;
var dataWords = data.words;
var nBitsTotal = this._nDataBytes * 8;
var nBitsLeft = data.sigBytes * 8;
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 30] = Math.floor(nBitsTotal / 0x100000000);
dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 31] = nBitsTotal;
data.sigBytes = dataWords.length * 4;
this._process();
var hash = this._hash.toX32();
return hash;
},
clone: function () {
var clone = Hasher.clone.call(this);
clone._hash = this._hash.clone();
return clone;
},
blockSize: 1024 / 32
});
C.SHA512 = Hasher._createHelper(SHA512);
C.HmacSHA512 = Hasher._createHmacHelper(SHA512);
}
());
(function () {
var C = CryptoJS;
var C_x64 = C.x64;
var X64Word = C_x64.Word;
var X64WordArray = C_x64.WordArray;
var C_algo = C.algo;
var SHA512 = C_algo.SHA512;
var SHA384 = C_algo.SHA384 = SHA512.extend({
_doReset: function () {
this._hash = new X64WordArray.init([new X64Word.init(0xcbbb9d5d, 0xc1059ed8), new X64Word.init(0x629a292a, 0x367cd507), new X64Word.init(0x9159015a, 0x3070dd17), new X64Word.init(0x152fecd8, 0xf70e5939), new X64Word.init(0x67332667, 0xffc00b31), new X64Word.init(0x8eb44a87, 0x68581511), new X64Word.init(0xdb0c2e0d, 0x64f98fa7), new X64Word.init(0x47b5481d, 0xbefa4fa4)]);
},
_doFinalize: function () {
var hash = SHA512._doFinalize.call(this);
hash.sigBytes -= 16;
return hash;
}
});
C.SHA384 = SHA512._createHelper(SHA384);
C.HmacSHA384 = SHA512._createHmacHelper(SHA384);
}
());
CryptoJS.lib.Cipher || (function (undefined) {
var C = CryptoJS;
var C_lib = C.lib;
var Base = C_lib.Base;
var WordArray = C_lib.WordArray;
var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm;
var C_enc = C.enc;
var Utf8 = C_enc.Utf8;
var Base64 = C_enc.Base64;
var C_algo = C.algo;
var EvpKDF = C_algo.EvpKDF;
var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({
cfg: Base.extend(),
createEncryptor: function (key, cfg) {
return this.create(this._ENC_XFORM_MODE, key, cfg);
},
createDecryptor: function (key, cfg) {
return this.create(this._DEC_XFORM_MODE, key, cfg);
},
init: function (xformMode, key, cfg) {
this.cfg = this.cfg.extend(cfg);
this._xformMode = xformMode;
this._key = key;
this.reset();
},
reset: function () {
BufferedBlockAlgorithm.reset.call(this);
this._doReset();
},
process: function (dataUpdate) {
this._append(dataUpdate);
return this._process();
},
finalize: function (dataUpdate) {
if (dataUpdate) {
this._append(dataUpdate);
}
var finalProcessedData = this._doFinalize();
return finalProcessedData;
},
keySize: 128 / 32,
ivSize: 128 / 32,
_ENC_XFORM_MODE: 1,
_DEC_XFORM_MODE: 2,
_createHelper: (function () {
function selectCipherStrategy(key) {
if (typeof key == 'string') {
return PasswordBasedCipher;
} else {
return SerializableCipher;
}
}
return function (cipher) {
return {
encrypt: function (message, key, cfg) {
return selectCipherStrategy(key).encrypt(cipher, message, key, cfg);
},
decrypt: function (ciphertext, key, cfg) {
return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg);
}
};
};
}
())
});
var StreamCipher = C_lib.StreamCipher = Cipher.extend({
_doFinalize: function () {
var finalProcessedBlocks = this._process(!!'flush');
return finalProcessedBlocks;
},
blockSize: 1
});
var C_mode = C.mode = {};
var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({
createEncryptor: function (cipher, iv) {
return this.Encryptor.create(cipher, iv);
},
createDecryptor: function (cipher, iv) {
return this.Decryptor.create(cipher, iv);
},
init: function (cipher, iv) {
this._cipher = cipher;
this._iv = iv;
}
});
var CBC = C_mode.CBC = (function () {
var CBC = BlockCipherMode.extend();
CBC.Encryptor = CBC.extend({
processBlock: function (words, offset) {
var cipher = this._cipher;
var blockSize = cipher.blockSize;
xorBlock.call(this, words, offset, blockSize);
cipher.encryptBlock(words, offset);
this._prevBlock = words.slice(offset, offset + blockSize);
}
});
CBC.Decryptor = CBC.extend({
processBlock: function (words, offset) {
var cipher = this._cipher;
var blockSize = cipher.blockSize;
var thisBlock = words.slice(offset, offset + blockSize);
cipher.decryptBlock(words, offset);
xorBlock.call(this, words, offset, blockSize);
this._prevBlock = thisBlock;
}
});
function xorBlock(words, offset, blockSize) {
var iv = this._iv;
if (iv) {
var block = iv;
this._iv = undefined;
} else {
var block = this._prevBlock;
}
for (var i = 0; i < blockSize; i++) {
words[offset + i] ^= block[i];
}
}
return CBC;
}
());
var C_pad = C.pad = {};
var Pkcs7 = C_pad.Pkcs7 = {
pad: function (data, blockSize) {
var blockSizeBytes = blockSize * 4;
var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;
var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes;
var paddingWords = [];
for (var i = 0; i < nPaddingBytes; i += 4) {
paddingWords.push(paddingWord);
}
var padding = WordArray.create(paddingWords, nPaddingBytes);
data.concat(padding);
},
unpad: function (data) {
var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
data.sigBytes -= nPaddingBytes;
}
};
var BlockCipher = C_lib.BlockCipher = Cipher.extend({
cfg: Cipher.cfg.extend({
mode: CBC,
padding: Pkcs7
}),
reset: function () {
Cipher.reset.call(this);
var cfg = this.cfg;
var iv = cfg.iv;
var mode = cfg.mode;
if (this._xformMode == this._ENC_XFORM_MODE) {
var modeCreator = mode.createEncryptor;
} else {
var modeCreator = mode.createDecryptor;
this._minBufferSize = 1;
}
if (this._mode && this._mode.__creator == modeCreator) {
this._mode.init(this, iv && iv.words);
} else {
this._mode = modeCreator.call(mode, this, iv && iv.words);
this._mode.__creator = modeCreator;
}
},
_doProcessBlock: function (words, offset) {
this._mode.processBlock(words, offset);
},
_doFinalize: function () {
var padding = this.cfg.padding;
if (this._xformMode == this._ENC_XFORM_MODE) {
padding.pad(this._data, this.blockSize);
var finalProcessedBlocks = this._process(!!'flush');
} else {
var finalProcessedBlocks = this._process(!!'flush');
padding.unpad(finalProcessedBlocks);
}
return finalProcessedBlocks;
},
blockSize: 128 / 32
});
var CipherParams = C_lib.CipherParams = Base.extend({
init: function (cipherParams) {
this.mixIn(cipherParams);
},
toString: function (formatter) {
return (formatter || this.formatter).stringify(this);
}
});
var C_format = C.format = {};
var OpenSSLFormatter = C_format.OpenSSL = {
stringify: function (cipherParams) {
var ciphertext = cipherParams.ciphertext;
var salt = cipherParams.salt;
if (salt) {
var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);
} else {
var wordArray = ciphertext;
}
return wordArray.toString(Base64);
},
parse: function (openSSLStr) {
var ciphertext = Base64.parse(openSSLStr);
var ciphertextWords = ciphertext.words;
if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) {
var salt = WordArray.create(ciphertextWords.slice(2, 4));
ciphertextWords.splice(0, 4);
ciphertext.sigBytes -= 16;
}
return CipherParams.create({
ciphertext: ciphertext,
salt: salt
});
}
};
var SerializableCipher = C_lib.SerializableCipher = Base.extend({
cfg: Base.extend({
format: OpenSSLFormatter
}),
encrypt: function (cipher, message, key, cfg) {
cfg = this.cfg.extend(cfg);
var encryptor = cipher.createEncryptor(key, cfg);
var ciphertext = encryptor.finalize(message);
var cipherCfg = encryptor.cfg;
return CipherParams.create({
ciphertext: ciphertext,
key: key,
iv: cipherCfg.iv,
algorithm: cipher,
mode: cipherCfg.mode,
padding: cipherCfg.padding,
blockSize: cipher.blockSize,
formatter: cfg.format
});
},
decrypt: function (cipher, ciphertext, key, cfg) {
cfg = this.cfg.extend(cfg);
ciphertext = this._parse(ciphertext, cfg.format);
var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext);
return plaintext;
},
_parse: function (ciphertext, format) {
if (typeof ciphertext == 'string') {
return format.parse(ciphertext, this);
} else {
return ciphertext;
}
}
});
var C_kdf = C.kdf = {};
var OpenSSLKdf = C_kdf.OpenSSL = {
execute: function (password, keySize, ivSize, salt) {
if (!salt) {
salt = WordArray.random(64 / 8);
}
var key = EvpKDF.create({
keySize: keySize + ivSize
}).compute(password, salt);
var iv = WordArray.create(key.words.slice(keySize), ivSize * 4);
key.sigBytes = keySize * 4;
return CipherParams.create({
key: key,
iv: iv,
salt: salt
});
}
};
var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({
cfg: SerializableCipher.cfg.extend({
kdf: OpenSSLKdf
}),
encrypt: function (cipher, message, password, cfg) {
cfg = this.cfg.extend(cfg);
var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize);
cfg.iv = derivedParams.iv;
var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg);
ciphertext.mixIn(derivedParams);
return ciphertext;
},
decrypt: function (cipher, ciphertext, password, cfg) {
cfg = this.cfg.extend(cfg);
ciphertext = this._parse(ciphertext, cfg.format);
var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt);
cfg.iv = derivedParams.iv;
var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg);
return plaintext;
}
});
}
());
CryptoJS.mode.CFB = (function () {
var CFB = CryptoJS.lib.BlockCipherMode.extend();
CFB.Encryptor = CFB.extend({
processBlock: function (words, offset) {
var cipher = this._cipher;
var blockSize = cipher.blockSize;
generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher);
this._prevBlock = words.slice(offset, offset + blockSize);
}
});
CFB.Decryptor = CFB.extend({
processBlock: function (words, offset) {
var cipher = this._cipher;
var blockSize = cipher.blockSize;
var thisBlock = words.slice(offset, offset + blockSize);
generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher);
this._prevBlock = thisBlock;
}
});
function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) {
var iv = this._iv;
if (iv) {
var keystream = iv.slice(0);
this._iv = undefined;
} else {
var keystream = this._prevBlock;
}
cipher.encryptBlock(keystream, 0);
for (var i = 0; i < blockSize; i++) {
words[offset + i] ^= keystream[i];
}
}
return CFB;
}
());
CryptoJS.mode.ECB = (function () {
var ECB = CryptoJS.lib.BlockCipherMode.extend();
ECB.Encryptor = ECB.extend({
processBlock: function (words, offset) {
this._cipher.encryptBlock(words, offset);
}
});
ECB.Decryptor = ECB.extend({
processBlock: function (words, offset) {
this._cipher.decryptBlock(words, offset);
}
});
return ECB;
}
());
CryptoJS.pad.AnsiX923 = {
pad: function (data, blockSize) {
var dataSigBytes = data.sigBytes;
var blockSizeBytes = blockSize * 4;
var nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes;
var lastBytePos = dataSigBytes + nPaddingBytes - 1;
data.clamp();
data.words[lastBytePos >>> 2] |= nPaddingBytes << (24 - (lastBytePos % 4) * 8);
data.sigBytes += nPaddingBytes;
},
unpad: function (data) {
var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
data.sigBytes -= nPaddingBytes;
}
};
CryptoJS.pad.Iso10126 = {
pad: function (data, blockSize) {
var blockSizeBytes = blockSize * 4;
var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;
data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes - 1)).concat(CryptoJS.lib.WordArray.create([nPaddingBytes << 24], 1));
},
unpad: function (data) {
var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
data.sigBytes -= nPaddingBytes;
}
};
CryptoJS.pad.Iso97971 = {
pad: function (data, blockSize) {
data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1));
CryptoJS.pad.ZeroPadding.pad(data, blockSize);
},
unpad: function (data) {
CryptoJS.pad.ZeroPadding.unpad(data);
data.sigBytes--;
}
};
CryptoJS.mode.OFB = (function () {
var OFB = CryptoJS.lib.BlockCipherMode.extend();
var Encryptor = OFB.Encryptor = OFB.extend({
processBlock: function (words, offset) {
var cipher = this._cipher
var blockSize = cipher.blockSize;
var iv = this._iv;
var keystream = this._keystream;
if (iv) {
keystream = this._keystream = iv.slice(0);
this._iv = undefined;
}
cipher.encryptBlock(keystream, 0);
for (var i = 0; i < blockSize; i++) {
words[offset + i] ^= keystream[i];
}
}
});
OFB.Decryptor = Encryptor;
return OFB;
}
());
CryptoJS.pad.NoPadding = {
pad: function () {},
unpad: function () {}
};
(function (undefined) {
var C = CryptoJS;
var C_lib = C.lib;
var CipherParams = C_lib.CipherParams;
var C_enc = C.enc;
var Hex = C_enc.Hex;
var C_format = C.format;
var HexFormatter = C_format.Hex = {
stringify: function (cipherParams) {
return cipherParams.ciphertext.toString(Hex);
},
parse: function (input) {
var ciphertext = Hex.parse(input);
return CipherParams.create({
ciphertext: ciphertext
});
}
};
}
());
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var BlockCipher = C_lib.BlockCipher;
var C_algo = C.algo;
var SBOX = [];
var INV_SBOX = [];
var SUB_MIX_0 = [];
var SUB_MIX_1 = [];
var SUB_MIX_2 = [];
var SUB_MIX_3 = [];
var INV_SUB_MIX_0 = [];
var INV_SUB_MIX_1 = [];
var INV_SUB_MIX_2 = [];
var INV_SUB_MIX_3 = [];
(function () {
var d = [];
for (var i = 0; i < 256; i++) {
if (i < 128) {
d[i] = i << 1;
} else {
d[i] = (i << 1) ^ 0x11b;
}
}
var x = 0;
var xi = 0;
for (var i = 0; i < 256; i++) {
var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4);
sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63;
SBOX[x] = sx;
INV_SBOX[sx] = x;
var x2 = d[x];
var x4 = d[x2];
var x8 = d[x4];
var t = (d[sx] * 0x101) ^ (sx * 0x1010100);
SUB_MIX_0[x] = (t << 24) | (t >>> 8);
SUB_MIX_1[x] = (t << 16) | (t >>> 16);
SUB_MIX_2[x] = (t << 8) | (t >>> 24);
SUB_MIX_3[x] = t;
var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100);
INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8);
INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16);
INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24);
INV_SUB_MIX_3[sx] = t;
if (!x) {
x = xi = 1;
} else {
x = x2 ^ d[d[d[x8 ^ x2]]];
xi ^= d[d[xi]];
}
}
}
());
var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
var AES = C_algo.AES = BlockCipher.extend({
_doReset: function () {
if (this._nRounds && this._keyPriorReset === this._key) {
return;
}
var key = this._keyPriorReset = this._key;
var keyWords = key.words;
var keySize = key.sigBytes / 4;
var nRounds = this._nRounds = keySize + 6;
var ksRows = (nRounds + 1) * 4;
var keySchedule = this._keySchedule = [];
for (var ksRow = 0; ksRow < ksRows; ksRow++) {
if (ksRow < keySize) {
keySchedule[ksRow] = keyWords[ksRow];
} else {
var t = keySchedule[ksRow - 1];
if (!(ksRow % keySize)) {
t = (t << 8) | (t >>> 24);
t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff];
t ^= RCON[(ksRow / keySize) | 0] << 24;
} else if (keySize > 6 && ksRow % keySize == 4) {
t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff];
}
keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t;
}
}
var invKeySchedule = this._invKeySchedule = [];
for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) {
var ksRow = ksRows - invKsRow;
if (invKsRow % 4) {
var t = keySchedule[ksRow];
} else {
var t = keySchedule[ksRow - 4];
}
if (invKsRow < 4 || ksRow <= 4) {
invKeySchedule[invKsRow] = t;
} else {
invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^ INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]];
}
}
},
encryptBlock: function (M, offset) {
this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX);
},
decryptBlock: function (M, offset) {
var t = M[offset + 1];
M[offset + 1] = M[offset + 3];
M[offset + 3] = t;
this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX);
var t = M[offset + 1];
M[offset + 1] = M[offset + 3];
M[offset + 3] = t;
},
_doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) {
var nRounds = this._nRounds;
var s0 = M[offset] ^ keySchedule[0];
var s1 = M[offset + 1] ^ keySchedule[1];
var s2 = M[offset + 2] ^ keySchedule[2];
var s3 = M[offset + 3] ^ keySchedule[3];
var ksRow = 4;
for (var round = 1; round < nRounds; round++) {
var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++];
var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++];
var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++];
var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++];
s0 = t0;
s1 = t1;
s2 = t2;
s3 = t3;
}
var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++];
var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++];
var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++];
var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++];
M[offset] = t0;
M[offset + 1] = t1;
M[offset + 2] = t2;
M[offset + 3] = t3;
},
keySize: 256 / 32
});
C.AES = BlockCipher._createHelper(AES);
}
());
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var BlockCipher = C_lib.BlockCipher;
var C_algo = C.algo;
var PC1 = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4];
var PC2 = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32];
var BIT_SHIFTS = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28];
var SBOX_P = [{
0x0: 0x808200,
0x10000000: 0x8000,
0x20000000: 0x808002,
0x30000000: 0x2,
0x40000000: 0x200,
0x50000000: 0x808202,
0x60000000: 0x800202,
0x70000000: 0x800000,
0x80000000: 0x202,
0x90000000: 0x800200,
0xa0000000: 0x8200,
0xb0000000: 0x808000,
0xc0000000: 0x8002,
0xd0000000: 0x800002,
0xe0000000: 0x0,
0xf0000000: 0x8202,
0x8000000: 0x0,
0x18000000: 0x808202,
0x28000000: 0x8202,
0x38000000: 0x8000,
0x48000000: 0x808200,
0x58000000: 0x200,
0x68000000: 0x808002,
0x78000000: 0x2,
0x88000000: 0x800200,
0x98000000: 0x8200,
0xa8000000: 0x808000,
0xb8000000: 0x800202,
0xc8000000: 0x800002,
0xd8000000: 0x8002,
0xe8000000: 0x202,
0xf8000000: 0x800000,
0x1: 0x8000,
0x10000001: 0x2,
0x20000001: 0x808200,
0x30000001: 0x800000,
0x40000001: 0x808002,
0x50000001: 0x8200,
0x60000001: 0x200,
0x70000001: 0x800202,
0x80000001: 0x808202,
0x90000001: 0x808000,
0xa0000001: 0x800002,
0xb0000001: 0x8202,
0xc0000001: 0x202,
0xd0000001: 0x800200,
0xe0000001: 0x8002,
0xf0000001: 0x0,
0x8000001: 0x808202,
0x18000001: 0x808000,
0x28000001: 0x800000,
0x38000001: 0x200,
0x48000001: 0x8000,
0x58000001: 0x800002,
0x68000001: 0x2,
0x78000001: 0x8202,
0x88000001: 0x8002,
0x98000001: 0x800202,
0xa8000001: 0x202,
0xb8000001: 0x808200,
0xc8000001: 0x800200,
0xd8000001: 0x0,
0xe8000001: 0x8200,
0xf8000001: 0x808002
}, {
0x0: 0x40084010,
0x1000000: 0x4000,
0x2000000: 0x80000,
0x3000000: 0x40080010,
0x4000000: 0x40000010,
0x5000000: 0x40084000,
0x6000000: 0x40004000,
0x7000000: 0x10,
0x8000000: 0x84000,
0x9000000: 0x40004010,
0xa000000: 0x40000000,
0xb000000: 0x84010,
0xc000000: 0x80010,
0xd000000: 0x0,
0xe000000: 0x4010,
0xf000000: 0x40080000,
0x800000: 0x40004000,
0x1800000: 0x84010,
0x2800000: 0x10,
0x3800000: 0x40004010,
0x4800000: 0x40084010,
0x5800000: 0x40000000,
0x6800000: 0x80000,
0x7800000: 0x40080010,
0x8800000: 0x80010,
0x9800000: 0x0,
0xa800000: 0x4000,
0xb800000: 0x40080000,
0xc800000: 0x40000010,
0xd800000: 0x84000,
0xe800000: 0x40084000,
0xf800000: 0x4010,
0x10000000: 0x0,
0x11000000: 0x40080010,
0x12000000: 0x40004010,
0x13000000: 0x40084000,
0x14000000: 0x40080000,
0x15000000: 0x10,
0x16000000: 0x84010,
0x17000000: 0x4000,
0x18000000: 0x4010,
0x19000000: 0x80000,
0x1a000000: 0x80010,
0x1b000000: 0x40000010,
0x1c000000: 0x84000,
0x1d000000: 0x40004000,
0x1e000000: 0x40000000,
0x1f000000: 0x40084010,
0x10800000: 0x84010,
0x11800000: 0x80000,
0x12800000: 0x40080000,
0x13800000: 0x4000,
0x14800000: 0x40004000,
0x15800000: 0x40084010,
0x16800000: 0x10,
0x17800000: 0x40000000,
0x18800000: 0x40084000,
0x19800000: 0x40000010,
0x1a800000: 0x40004010,
0x1b800000: 0x80010,
0x1c800000: 0x0,
0x1d800000: 0x4010,
0x1e800000: 0x40080010,
0x1f800000: 0x84000
}, {
0x0: 0x104,
0x100000: 0x0,
0x200000: 0x4000100,
0x300000: 0x10104,
0x400000: 0x10004,
0x500000: 0x4000004,
0x600000: 0x4010104,
0x700000: 0x4010000,
0x800000: 0x4000000,
0x900000: 0x4010100,
0xa00000: 0x10100,
0xb00000: 0x4010004,
0xc00000: 0x4000104,
0xd00000: 0x10000,
0xe00000: 0x4,
0xf00000: 0x100,
0x80000: 0x4010100,
0x180000: 0x4010004,
0x280000: 0x0,
0x380000: 0x4000100,
0x480000: 0x4000004,
0x580000: 0x10000,
0x680000: 0x10004,
0x780000: 0x104,
0x880000: 0x4,
0x980000: 0x100,
0xa80000: 0x4010000,
0xb80000: 0x10104,
0xc80000: 0x10100,
0xd80000: 0x4000104,
0xe80000: 0x4010104,
0xf80000: 0x4000000,
0x1000000: 0x4010100,
0x1100000: 0x10004,
0x1200000: 0x10000,
0x1300000: 0x4000100,
0x1400000: 0x100,
0x1500000: 0x4010104,
0x1600000: 0x4000004,
0x1700000: 0x0,
0x1800000: 0x4000104,
0x1900000: 0x4000000,
0x1a00000: 0x4,
0x1b00000: 0x10100,
0x1c00000: 0x4010000,
0x1d00000: 0x104,
0x1e00000: 0x10104,
0x1f00000: 0x4010004,
0x1080000: 0x4000000,
0x1180000: 0x104,
0x1280000: 0x4010100,
0x1380000: 0x0,
0x1480000: 0x10004,
0x1580000: 0x4000100,
0x1680000: 0x100,
0x1780000: 0x4010004,
0x1880000: 0x10000,
0x1980000: 0x4010104,
0x1a80000: 0x10104,
0x1b80000: 0x4000004,
0x1c80000: 0x4000104,
0x1d80000: 0x4010000,
0x1e80000: 0x4,
0x1f80000: 0x10100
}, {
0x0: 0x80401000,
0x10000: 0x80001040,
0x20000: 0x401040,
0x30000: 0x80400000,
0x40000: 0x0,
0x50000: 0x401000,
0x60000: 0x80000040,
0x70000: 0x400040,
0x80000: 0x80000000,
0x90000: 0x400000,
0xa0000: 0x40,
0xb0000: 0x80001000,
0xc0000: 0x80400040,
0xd0000: 0x1040,
0xe0000: 0x1000,
0xf0000: 0x80401040,
0x8000: 0x80001040,
0x18000: 0x40,
0x28000: 0x80400040,
0x38000: 0x80001000,
0x48000: 0x401000,
0x58000: 0x80401040,
0x68000: 0x0,
0x78000: 0x80400000,
0x88000: 0x1000,
0x98000: 0x80401000,
0xa8000: 0x400000,
0xb8000: 0x1040,
0xc8000: 0x80000000,
0xd8000: 0x400040,
0xe8000: 0x401040,
0xf8000: 0x80000040,
0x100000: 0x400040,
0x110000: 0x401000,
0x120000: 0x80000040,
0x130000: 0x0,
0x140000: 0x1040,
0x150000: 0x80400040,
0x160000: 0x80401000,
0x170000: 0x80001040,
0x180000: 0x80401040,
0x190000: 0x80000000,
0x1a0000: 0x80400000,
0x1b0000: 0x401040,
0x1c0000: 0x80001000,
0x1d0000: 0x400000,
0x1e0000: 0x40,
0x1f0000: 0x1000,
0x108000: 0x80400000,
0x118000: 0x80401040,
0x128000: 0x0,
0x138000: 0x401000,
0x148000: 0x400040,
0x158000: 0x80000000,
0x168000: 0x80001040,
0x178000: 0x40,
0x188000: 0x80000040,
0x198000: 0x1000,
0x1a8000: 0x80001000,
0x1b8000: 0x80400040,
0x1c8000: 0x1040,
0x1d8000: 0x80401000,
0x1e8000: 0x400000,
0x1f8000: 0x401040
}, {
0x0: 0x80,
0x1000: 0x1040000,
0x2000: 0x40000,
0x3000: 0x20000000,
0x4000: 0x20040080,
0x5000: 0x1000080,
0x6000: 0x21000080,
0x7000: 0x40080,
0x8000: 0x1000000,
0x9000: 0x20040000,
0xa000: 0x20000080,
0xb000: 0x21040080,
0xc000: 0x21040000,
0xd000: 0x0,
0xe000: 0x1040080,
0xf000: 0x21000000,
0x800: 0x1040080,
0x1800: 0x21000080,
0x2800: 0x80,
0x3800: 0x1040000,
0x4800: 0x40000,
0x5800: 0x20040080,
0x6800: 0x21040000,
0x7800: 0x20000000,
0x8800: 0x20040000,
0x9800: 0x0,
0xa800: 0x21040080,
0xb800: 0x1000080,
0xc800: 0x20000080,
0xd800: 0x21000000,
0xe800: 0x1000000,
0xf800: 0x40080,
0x10000: 0x40000,
0x11000: 0x80,
0x12000: 0x20000000,
0x13000: 0x21000080,
0x14000: 0x1000080,
0x15000: 0x21040000,
0x16000: 0x20040080,
0x17000: 0x1000000,
0x18000: 0x21040080,
0x19000: 0x21000000,
0x1a000: 0x1040000,
0x1b000: 0x20040000,
0x1c000: 0x40080,
0x1d000: 0x20000080,
0x1e000: 0x0,
0x1f000: 0x1040080,
0x10800: 0x21000080,
0x11800: 0x1000000,
0x12800: 0x1040000,
0x13800: 0x20040080,
0x14800: 0x20000000,
0x15800: 0x1040080,
0x16800: 0x80,
0x17800: 0x21040000,
0x18800: 0x40080,
0x19800: 0x21040080,
0x1a800: 0x0,
0x1b800: 0x21000000,
0x1c800: 0x1000080,
0x1d800: 0x40000,
0x1e800: 0x20040000,
0x1f800: 0x20000080
}, {
0x0: 0x10000008,
0x100: 0x2000,
0x200: 0x10200000,
0x300: 0x10202008,
0x400: 0x10002000,
0x500: 0x200000,
0x600: 0x200008,
0x700: 0x10000000,
0x800: 0x0,
0x900: 0x10002008,
0xa00: 0x202000,
0xb00: 0x8,
0xc00: 0x10200008,
0xd00: 0x202008,
0xe00: 0x2008,
0xf00: 0x10202000,
0x80: 0x10200000,
0x180: 0x10202008,
0x280: 0x8,
0x380: 0x200000,
0x480: 0x202008,
0x580: 0x10000008,
0x680: 0x10002000,
0x780: 0x2008,
0x880: 0x200008,
0x980: 0x2000,
0xa80: 0x10002008,
0xb80: 0x10200008,
0xc80: 0x0,
0xd80: 0x10202000,
0xe80: 0x202000,
0xf80: 0x10000000,
0x1000: 0x10002000,
0x1100: 0x10200008,
0x1200: 0x10202008,
0x1300: 0x2008,
0x1400: 0x200000,
0x1500: 0x10000000,
0x1600: 0x10000008,
0x1700: 0x202000,
0x1800: 0x202008,
0x1900: 0x0,
0x1a00: 0x8,
0x1b00: 0x10200000,
0x1c00: 0x2000,
0x1d00: 0x10002008,
0x1e00: 0x10202000,
0x1f00: 0x200008,
0x1080: 0x8,
0x1180: 0x202000,
0x1280: 0x200000,
0x1380: 0x10000008,
0x1480: 0x10002000,
0x1580: 0x2008,
0x1680: 0x10202008,
0x1780: 0x10200000,
0x1880: 0x10202000,
0x1980: 0x10200008,
0x1a80: 0x2000,
0x1b80: 0x202008,
0x1c80: 0x200008,
0x1d80: 0x0,
0x1e80: 0x10000000,
0x1f80: 0x10002008
}, {
0x0: 0x100000,
0x10: 0x2000401,
0x20: 0x400,
0x30: 0x100401,
0x40: 0x2100401,
0x50: 0x0,
0x60: 0x1,
0x70: 0x2100001,
0x80: 0x2000400,
0x90: 0x100001,
0xa0: 0x2000001,
0xb0: 0x2100400,
0xc0: 0x2100000,
0xd0: 0x401,
0xe0: 0x100400,
0xf0: 0x2000000,
0x8: 0x2100001,
0x18: 0x0,
0x28: 0x2000401,
0x38: 0x2100400,
0x48: 0x100000,
0x58: 0x2000001,
0x68: 0x2000000,
0x78: 0x401,
0x88: 0x100401,
0x98: 0x2000400,
0xa8: 0x2100000,
0xb8: 0x100001,
0xc8: 0x400,
0xd8: 0x2100401,
0xe8: 0x1,
0xf8: 0x100400,
0x100: 0x2000000,
0x110: 0x100000,
0x120: 0x2000401,
0x130: 0x2100001,
0x140: 0x100001,
0x150: 0x2000400,
0x160: 0x2100400,
0x170: 0x100401,
0x180: 0x401,
0x190: 0x2100401,
0x1a0: 0x100400,
0x1b0: 0x1,
0x1c0: 0x0,
0x1d0: 0x2100000,
0x1e0: 0x2000001,
0x1f0: 0x400,
0x108: 0x100400,
0x118: 0x2000401,
0x128: 0x2100001,
0x138: 0x1,
0x148: 0x2000000,
0x158: 0x100000,
0x168: 0x401,
0x178: 0x2100400,
0x188: 0x2000001,
0x198: 0x2100000,
0x1a8: 0x0,
0x1b8: 0x2100401,
0x1c8: 0x100401,
0x1d8: 0x400,
0x1e8: 0x2000400,
0x1f8: 0x100001
}, {
0x0: 0x8000820,
0x1: 0x20000,
0x2: 0x8000000,
0x3: 0x20,
0x4: 0x20020,
0x5: 0x8020820,
0x6: 0x8020800,
0x7: 0x800,
0x8: 0x8020000,
0x9: 0x8000800,
0xa: 0x20800,
0xb: 0x8020020,
0xc: 0x820,
0xd: 0x0,
0xe: 0x8000020,
0xf: 0x20820,
0x80000000: 0x800,
0x80000001: 0x8020820,
0x80000002: 0x8000820,
0x80000003: 0x8000000,
0x80000004: 0x8020000,
0x80000005: 0x20800,
0x80000006: 0x20820,
0x80000007: 0x20,
0x80000008: 0x8000020,
0x80000009: 0x820,
0x8000000a: 0x20020,
0x8000000b: 0x8020800,
0x8000000c: 0x0,
0x8000000d: 0x8020020,
0x8000000e: 0x8000800,
0x8000000f: 0x20000,
0x10: 0x20820,
0x11: 0x8020800,
0x12: 0x20,
0x13: 0x800,
0x14: 0x8000800,
0x15: 0x8000020,
0x16: 0x8020020,
0x17: 0x20000,
0x18: 0x0,
0x19: 0x20020,
0x1a: 0x8020000,
0x1b: 0x8000820,
0x1c: 0x8020820,
0x1d: 0x20800,
0x1e: 0x820,
0x1f: 0x8000000,
0x80000010: 0x20000,
0x80000011: 0x800,
0x80000012: 0x8020020,
0x80000013: 0x20820,
0x80000014: 0x20,
0x80000015: 0x8020000,
0x80000016: 0x8000000,
0x80000017: 0x8000820,
0x80000018: 0x8020820,
0x80000019: 0x8000020,
0x8000001a: 0x8000800,
0x8000001b: 0x0,
0x8000001c: 0x20800,
0x8000001d: 0x820,
0x8000001e: 0x20020,
0x8000001f: 0x8020800
}
];
var SBOX_MASK = [0xf8000001, 0x1f800000, 0x01f80000, 0x001f8000, 0x0001f800, 0x00001f80, 0x000001f8, 0x8000001f];
var DES = C_algo.DES = BlockCipher.extend({
_doReset: function () {
var key = this._key;
var keyWords = key.words;
var keyBits = [];
for (var i = 0; i < 56; i++) {
var keyBitPos = PC1[i] - 1;
keyBits[i] = (keyWords[keyBitPos >>> 5] >>> (31 - keyBitPos % 32)) & 1;
}
var subKeys = this._subKeys = [];
for (var nSubKey = 0; nSubKey < 16; nSubKey++) {
var subKey = subKeys[nSubKey] = [];
var bitShift = BIT_SHIFTS[nSubKey];
for (var i = 0; i < 24; i++) {
subKey[(i / 6) | 0] |= keyBits[((PC2[i] - 1) + bitShift) % 28] << (31 - i % 6);
subKey[4 + ((i / 6) | 0)] |= keyBits[28 + (((PC2[i + 24] - 1) + bitShift) % 28)] << (31 - i % 6);
}
subKey[0] = (subKey[0] << 1) | (subKey[0] >>> 31);
for (var i = 1; i < 7; i++) {
subKey[i] = subKey[i] >>> ((i - 1) * 4 + 3);
}
subKey[7] = (subKey[7] << 5) | (subKey[7] >>> 27);
}
var invSubKeys = this._invSubKeys = [];
for (var i = 0; i < 16; i++) {
invSubKeys[i] = subKeys[15 - i];
}
},
encryptBlock: function (M, offset) {
this._doCryptBlock(M, offset, this._subKeys);
},
decryptBlock: function (M, offset) {
this._doCryptBlock(M, offset, this._invSubKeys);
},
_doCryptBlock: function (M, offset, subKeys) {
this._lBlock = M[offset];
this._rBlock = M[offset + 1];
exchangeLR.call(this, 4, 0x0f0f0f0f);
exchangeLR.call(this, 16, 0x0000ffff);
exchangeRL.call(this, 2, 0x33333333);
exchangeRL.call(this, 8, 0x00ff00ff);
exchangeLR.call(this, 1, 0x55555555);
for (var round = 0; round < 16; round++) {
var subKey = subKeys[round];
var lBlock = this._lBlock;
var rBlock = this._rBlock;
var f = 0;
for (var i = 0; i < 8; i++) {
f |= SBOX_P[i][((rBlock ^ subKey[i]) & SBOX_MASK[i]) >>> 0];
}
this._lBlock = rBlock;
this._rBlock = lBlock ^ f;
}
var t = this._lBlock;
this._lBlock = this._rBlock;
this._rBlock = t;
exchangeLR.call(this, 1, 0x55555555);
exchangeRL.call(this, 8, 0x00ff00ff);
exchangeRL.call(this, 2, 0x33333333);
exchangeLR.call(this, 16, 0x0000ffff);
exchangeLR.call(this, 4, 0x0f0f0f0f);
M[offset] = this._lBlock;
M[offset + 1] = this._rBlock;
},
keySize: 64 / 32,
ivSize: 64 / 32,
blockSize: 64 / 32
});
function exchangeLR(offset, mask) {
var t = ((this._lBlock >>> offset) ^ this._rBlock) & mask;
this._rBlock ^= t;
this._lBlock ^= t << offset;
}
function exchangeRL(offset, mask) {
var t = ((this._rBlock >>> offset) ^ this._lBlock) & mask;
this._lBlock ^= t;
this._rBlock ^= t << offset;
}
C.DES = BlockCipher._createHelper(DES);
var TripleDES = C_algo.TripleDES = BlockCipher.extend({
_doReset: function () {
var key = this._key;
var keyWords = key.words;
this._des1 = DES.createEncryptor(WordArray.create(keyWords.slice(0, 2)));
this._des2 = DES.createEncryptor(WordArray.create(keyWords.slice(2, 4)));
this._des3 = DES.createEncryptor(WordArray.create(keyWords.slice(4, 6)));
},
encryptBlock: function (M, offset) {
this._des1.encryptBlock(M, offset);
this._des2.decryptBlock(M, offset);
this._des3.encryptBlock(M, offset);
},
decryptBlock: function (M, offset) {
this._des3.decryptBlock(M, offset);
this._des2.encryptBlock(M, offset);
this._des1.decryptBlock(M, offset);
},
keySize: 192 / 32,
ivSize: 64 / 32,
blockSize: 64 / 32
});
C.TripleDES = BlockCipher._createHelper(TripleDES);
}
());
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var StreamCipher = C_lib.StreamCipher;
var C_algo = C.algo;
var RC4 = C_algo.RC4 = StreamCipher.extend({
_doReset: function () {
var key = this._key;
var keyWords = key.words;
var keySigBytes = key.sigBytes;
var S = this._S = [];
for (var i = 0; i < 256; i++) {
S[i] = i;
}
for (var i = 0, j = 0; i < 256; i++) {
var keyByteIndex = i % keySigBytes;
var keyByte = (keyWords[keyByteIndex >>> 2] >>> (24 - (keyByteIndex % 4) * 8)) & 0xff;
j = (j + S[i] + keyByte) % 256;
var t = S[i];
S[i] = S[j];
S[j] = t;
}
this._i = this._j = 0;
},
_doProcessBlock: function (M, offset) {
M[offset] ^= generateKeystreamWord.call(this);
},
keySize: 256 / 32,
ivSize: 0
});
function generateKeystreamWord() {
var S = this._S;
var i = this._i;
var j = this._j;
var keystreamWord = 0;
for (var n = 0; n < 4; n++) {
i = (i + 1) % 256;
j = (j + S[i]) % 256;
var t = S[i];
S[i] = S[j];
S[j] = t;
keystreamWord |= S[(S[i] + S[j]) % 256] << (24 - n * 8);
}
this._i = i;
this._j = j;
return keystreamWord;
}
C.RC4 = StreamCipher._createHelper(RC4);
var RC4Drop = C_algo.RC4Drop = RC4.extend({
cfg: RC4.cfg.extend({
drop: 192
}),
_doReset: function () {
RC4._doReset.call(this);
for (var i = this.cfg.drop; i > 0; i--) {
generateKeystreamWord.call(this);
}
}
});
C.RC4Drop = StreamCipher._createHelper(RC4Drop);
}
());
CryptoJS.mode.CTRGladman = (function () {
var CTRGladman = CryptoJS.lib.BlockCipherMode.extend();
function incWord(word) {
if (((word >> 24) & 0xff) === 0xff) {
var b1 = (word >> 16) & 0xff;
var b2 = (word >> 8) & 0xff;
var b3 = word & 0xff;
if (b1 === 0xff) {
b1 = 0;
if (b2 === 0xff) {
b2 = 0;
if (b3 === 0xff) {
b3 = 0;
} else {
++b3;
}
} else {
++b2;
}
} else {
++b1;
}
word = 0;
word += (b1 << 16);
word += (b2 << 8);
word += b3;
} else {
word += (0x01 << 24);
}
return word;
}
function incCounter(counter) {
if ((counter[0] = incWord(counter[0])) === 0) {
counter[1] = incWord(counter[1]);
}
return counter;
}
var Encryptor = CTRGladman.Encryptor = CTRGladman.extend({
processBlock: function (words, offset) {
var cipher = this._cipher
var blockSize = cipher.blockSize;
var iv = this._iv;
var counter = this._counter;
if (iv) {
counter = this._counter = iv.slice(0);
this._iv = undefined;
}
incCounter(counter);
var keystream = counter.slice(0);
cipher.encryptBlock(keystream, 0);
for (var i = 0; i < blockSize; i++) {
words[offset + i] ^= keystream[i];
}
}
});
CTRGladman.Decryptor = Encryptor;
return CTRGladman;
}
());
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var StreamCipher = C_lib.StreamCipher;
var C_algo = C.algo;
var S = [];
var C_ = [];
var G = [];
var Rabbit = C_algo.Rabbit = StreamCipher.extend({
_doReset: function () {
var K = this._key.words;
var iv = this.cfg.iv;
for (var i = 0; i < 4; i++) {
K[i] = (((K[i] << 8) | (K[i] >>> 24)) & 0x00ff00ff) | (((K[i] << 24) | (K[i] >>> 8)) & 0xff00ff00);
}
var X = this._X = [K[0], (K[3] << 16) | (K[2] >>> 16), K[1], (K[0] << 16) | (K[3] >>> 16), K[2], (K[1] << 16) | (K[0] >>> 16), K[3], (K[2] << 16) | (K[1] >>> 16)];
var C = this._C = [(K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff)];
this._b = 0;
for (var i = 0; i < 4; i++) {
nextState.call(this);
}
for (var i = 0; i < 8; i++) {
C[i] ^= X[(i + 4) & 7];
}
if (iv) {
var IV = iv.words;
var IV_0 = IV[0];
var IV_1 = IV[1];
var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00);
var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00);
var i1 = (i0 >>> 16) | (i2 & 0xffff0000);
var i3 = (i2 << 16) | (i0 & 0x0000ffff);
C[0] ^= i0;
C[1] ^= i1;
C[2] ^= i2;
C[3] ^= i3;
C[4] ^= i0;
C[5] ^= i1;
C[6] ^= i2;
C[7] ^= i3;
for (var i = 0; i < 4; i++) {
nextState.call(this);
}
}
},
_doProcessBlock: function (M, offset) {
var X = this._X;
nextState.call(this);
S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16);
S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16);
S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16);
S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16);
for (var i = 0; i < 4; i++) {
S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00);
M[offset + i] ^= S[i];
}
},
blockSize: 128 / 32,
ivSize: 64 / 32
});
function nextState() {
var X = this._X;
var C = this._C;
for (var i = 0; i < 8; i++) {
C_[i] = C[i];
}
C[0] = (C[0] + 0x4d34d34d + this._b) | 0;
C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0;
C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0;
C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0;
C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0;
C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0;
C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0;
C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0;
this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0;
for (var i = 0; i < 8; i++) {
var gx = X[i] + C[i];
var ga = gx & 0xffff;
var gb = gx >>> 16;
var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb;
var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0);
G[i] = gh ^ gl;
}
X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0;
X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0;
X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0;
X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0;
X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0;
X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0;
X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0;
X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0;
}
C.Rabbit = StreamCipher._createHelper(Rabbit);
}
());
CryptoJS.mode.CTR = (function () {
var CTR = CryptoJS.lib.BlockCipherMode.extend();
var Encryptor = CTR.Encryptor = CTR.extend({
processBlock: function (words, offset) {
var cipher = this._cipher
var blockSize = cipher.blockSize;
var iv = this._iv;
var counter = this._counter;
if (iv) {
counter = this._counter = iv.slice(0);
this._iv = undefined;
}
var keystream = counter.slice(0);
cipher.encryptBlock(keystream, 0);
counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0
for (var i = 0; i < blockSize; i++) {
words[offset + i] ^= keystream[i];
}
}
});
CTR.Decryptor = Encryptor;
return CTR;
}
());
(function () {
var C = CryptoJS;
var C_lib = C.lib;
var StreamCipher = C_lib.StreamCipher;
var C_algo = C.algo;
var S = [];
var C_ = [];
var G = [];
var RabbitLegacy = C_algo.RabbitLegacy = StreamCipher.extend({
_doReset: function () {
var K = this._key.words;
var iv = this.cfg.iv;
var X = this._X = [K[0], (K[3] << 16) | (K[2] >>> 16), K[1], (K[0] << 16) | (K[3] >>> 16), K[2], (K[1] << 16) | (K[0] >>> 16), K[3], (K[2] << 16) | (K[1] >>> 16)];
var C = this._C = [(K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff)];
this._b = 0;
for (var i = 0; i < 4; i++) {
nextState.call(this);
}
for (var i = 0; i < 8; i++) {
C[i] ^= X[(i + 4) & 7];
}
if (iv) {
var IV = iv.words;
var IV_0 = IV[0];
var IV_1 = IV[1];
var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00);
var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00);
var i1 = (i0 >>> 16) | (i2 & 0xffff0000);
var i3 = (i2 << 16) | (i0 & 0x0000ffff);
C[0] ^= i0;
C[1] ^= i1;
C[2] ^= i2;
C[3] ^= i3;
C[4] ^= i0;
C[5] ^= i1;
C[6] ^= i2;
C[7] ^= i3;
for (var i = 0; i < 4; i++) {
nextState.call(this);
}
}
},
_doProcessBlock: function (M, offset) {
var X = this._X;
nextState.call(this);
S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16);
S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16);
S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16);
S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16);
for (var i = 0; i < 4; i++) {
S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00);
M[offset + i] ^= S[i];
}
},
blockSize: 128 / 32,
ivSize: 64 / 32
});
function nextState() {
var X = this._X;
var C = this._C;
for (var i = 0; i < 8; i++) {
C_[i] = C[i];
}
C[0] = (C[0] + 0x4d34d34d + this._b) | 0;
C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0;
C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0;
C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0;
C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0;
C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0;
C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0;
C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0;
this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0;
for (var i = 0; i < 8; i++) {
var gx = X[i] + C[i];
var ga = gx & 0xffff;
var gb = gx >>> 16;
var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb;
var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0);
G[i] = gh ^ gl;
}
X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0;
X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0;
X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0;
X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0;
X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0;
X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0;
X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0;
X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0;
}
C.RabbitLegacy = StreamCipher._createHelper(RabbitLegacy);
}
());
CryptoJS.pad.ZeroPadding = {
pad: function (data, blockSize) {
var blockSizeBytes = blockSize * 4;
data.clamp();
data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes);
},
unpad: function (data) {
var dataWords = data.words;
var i = data.sigBytes - 1;
while (!((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) {
i--;
}
data.sigBytes = i + 1;
}
};
return CryptoJS;
}));
var CryptoJS = module.exports;
//console.log(CryptoJS.MD5("xiaojianbangsaltstr") + '');
//console.log(CryptoJS.SHA256("xiaojianbangsaltstr").toString());
//console.log(CryptoJS.HmacSHA1("xiaojianbangsaltstr", "12345678").toString());
// var hasher = CryptoJS.algo.SHA256.create();
// hasher.update('xiao');
// hasher.reset();
// hasher.update('xiao');
// hasher.update('jianbang');
// var hash = hasher.finalize('saltstr');
// console.log(hash + '');
// var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, "12345678");
// hmacHasher.reset();
// hmacHasher.update('xiaojianbangsaltstr');
// var hmac = hmacHasher.finalize();
// console.log(hmac + '');
// console.log(CryptoJS.enc.Utf8.parse("xiaojianbang"));
// console.log(CryptoJS.enc.Hex.parse("7869616f6a69616e62616e67"));
// console.log(CryptoJS.enc.Base64.parse("eGlhb2ppYW5iYW5n"));
// console.log(CryptoJS.SHA256("xiaojianbangsaltstr") + '');
// console.log(CryptoJS.SHA256("xiaojianbangsaltstr").toString());
// console.log(CryptoJS.SHA256("xiaojianbangsaltstr").toString(CryptoJS.enc.Base64));
//
// var wordArray = CryptoJS.SHA256("xiaojianbangsaltstr");
// // console.log(CryptoJS.enc.Utf8.stringify(wordArray));
// console.log(CryptoJS.enc.Hex.stringify(wordArray));
// console.log(CryptoJS.enc.Base64.stringify(wordArray));
// var HexStr = "3078fff92edf23cd79b3ebff7fb9a5b28daefee9ee3aa5d532b7b4bc7423887e";
// var HexBytes = CryptoJS.enc.Hex.parse(HexStr);
// console.log(CryptoJS.enc.Base64.stringify(HexBytes));
// var md5Str = "xiaojianbangsaltstr";
// console.log(CryptoJS.MD5(md5Str).toString());
//
// var md5Str = "7869616f6a69616e62616e6773616c74737472";
// var HexBytes = CryptoJS.enc.Hex.parse(md5Str);
// console.log(CryptoJS.MD5(HexBytes).toString());
// var plainTextBytes = CryptoJS.enc.Utf8.parse("xiaojianxiaojia");
// var HexKeyBytes = CryptoJS.enc.Hex.parse("0102030405060708");
// var Utf8IVBytes = CryptoJS.enc.Utf8.parse("12345678");
// var cfg = {
// iv: Utf8IVBytes,
// // mode: CryptoJS.mode.CBC,
// // padding: CryptoJS.pad.Pkcs7,
// format: CryptoJS.format.Hex
// };
// var ciphertextObj = CryptoJS.DES.encrypt(plainTextBytes, HexKeyBytes, cfg);
// console.log(ciphertextObj);
// console.log(ciphertextObj.key + '');
// console.log(ciphertextObj.iv + '');
// console.log(ciphertextObj + '');
// console.log(ciphertextObj.ciphertext + '');
// console.log(ciphertextObj.toString(CryptoJS.format.Hex));
// var plainTextObj = CryptoJS.DES.decrypt(ciphertext, HexKeyBytes, cfg);
// console.log(plainTextObj.toString(CryptoJS.enc.Utf8));
// var plainTextBytes = CryptoJS.enc.Utf8.parse("xiaojianxiaojia");
// var HexKeyBytes = CryptoJS.enc.Hex.parse("01020304050607080102030405060708");
// var Utf8IVBytes = CryptoJS.enc.Utf8.parse("1234567812345678");
// var cfg = {
// iv: Utf8IVBytes,
// mode: CryptoJS.mode.CBC,
// padding: CryptoJS.pad.Pkcs7,
// format: {
// stringify: function (data){
// let e = {
// ct: data.ciphertext.toString(),
// miaoshu: "这是我们的自定义输出内容"
// };
// return JSON.stringify(e)
// },
// parse: function (data){
// let json = JSON.parse(data);
// let newVar = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(json.ct)});
// return newVar
// }
// }
// };
// var ciphertextObj = CryptoJS.AES.encrypt(plainTextBytes, HexKeyBytes, cfg);
// var ciphertext = ciphertextObj.toString();
// console.log(ciphertext);
//
// var plainTextObj = CryptoJS.AES.decrypt(ciphertext, HexKeyBytes, cfg);
// console.log(plainTextObj.toString(CryptoJS.enc.Utf8));
// let ripemd160 = CryptoJS.RIPEMD160("xiaojianbang");
// console.log(ripemd160 + '');
// let hmacRIPEMD160 = CryptoJS.HmacRIPEMD160("xiaojianbang", "12345678");
// console.log(hmacRIPEMD160 + '');
//
// let pbkdf2 = CryptoJS.PBKDF2("xiaojianbang", "saltstr", {keySize: 8, iterations: 10000});
// console.log(pbkdf2 + '');
//
// let evpKDF = CryptoJS.EvpKDF("xiaojianbang", "saltstr", {keySize: 8, iterations: 10000});
// console.log(evpKDF + '');
摘要算法使用:
CryptoJS 中消息摘要算法的使用
CryptoJS.MD5(message);
CryptoJS.HmacMD5(message, key);
CryptoJS.SHA1(message);
CryptoJS.HmacSHA1(message, key);
CryptoJS.SHA256(message);
CryptoJS.HmacSHA256(message, key);
CryptoJS.SHA512(message);
CryptoJS.HmacSHA512(message, key);
//CryptoJS.SHA3('xiaojianbang', {outputLength: 256})
SHA256
var hasher = CryptoJS.algo.SHA256.create();
hasher.reset();
hasher.update('message');
hasher.update(wordArray);
var hash = hasher.finalize();
var hash = hasher.finalize('message');
var hash = hasher.finalize(wordArray);
HmacSHA256
var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
hmacHasher.reset();
hmacHasher.update('message');
hmacHasher.update(wordArray);
var hmac = hmacHasher.finalize();
var hmac = hmacHasher.finalize('message');
var hmac = hmacHasher.finalize(wordArray);
-----------------------------------------------------------------------------------------------------
字符串解析
1. string 转 wordArray
CryptoJS.enc.Utf8.parse(utf8String);
CryptoJS.enc.Hex.parse(hexString);
CryptoJS.enc.Base64.parse(base64String);
2. wordArray 转 string
wordArray + '';
wordArray.toString();
wordArray.toString(CryptoJS.enc.Utf8);
wordArray.toString(CryptoJS.enc.Hex);
wordArray.toString(CryptoJS.enc.Base64);
CryptoJS.enc.Utf8.stringify(wordArray);
CryptoJS.enc.Hex.stringify(wordArray);
CryptoJS.enc.Base64.stringify(wordArray);
-----------------------------------------------------------------------------------------------------
对称加密算法
CryptoJS 中对称加密算法的使用
var ciphertext = CryptoJS.DES.encrypt(message, key, cfg);
var plaintext = CryptoJS.DES.decrypt(ciphertext, key, cfg);
var ciphertext = CryptoJS.TripleDES.encrypt(message, key, cfg);
var plaintext = CryptoJS.TripleDES.decrypt(ciphertext, key, cfg);
var ciphertext = CryptoJS.AES.encrypt(message, key, cfg);
var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg);
var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg);
var plaintext = CryptoJS.RC4.decrypt(ciphertext, key, cfg);
2. cfg 的详细含义
var cfg = {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
format: CryptoJS.format.Hex
};
-----------------------------------------------------------------------------------------------------
CryptoJS( 对称加密算法 )
3. cfg 中没有传 mode 和 padding ,默认使用 CBC 的加密模式, Pkcs7 的填充方式
4. 加密结果是 wordArray 对象,调用 toString 默认转 Base64 编码的密文,转 hex 可以使用
var hexString = wordArray.ciphertext.toString();
5. CryptoJS 中提供的加密模式
CBC ECB CFB OFB CTRGladman CTR
6. CryptoJS 中提供的填充方式
NoPadding ZeroPadding Pkcs7(Pkcs5) Iso10126 Iso97971 AnsiX923
7. 对称加密算法的解密
8. 密文 / 明文的自定义输出 / 输入 (cfg 中 format 的指定 )
format: {
stringify: function (data){
let e = {
ct: data.ciphertext.toString(),
miaoshu: " 这是我们的自定义输出内容 "
};
return JSON.stringify(e)
},
parse: function (data){
let json = JSON.parse(data);
let newVar = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(json.ct)});
return newVar
}
}
利用Findhash结合得到的so层自吐算法
function monitor_constants(targetSo) {
let const_array = [];
let const_name = [];
let const_addr = [['padding used in hashing algorithms (0x80 0 ... 0)', '0x6000']];
for (var i = 0; i < const_addr.length; i++) {
const_array.push({base:targetSo.add(const_addr[i][1]),size:0x1});
const_name.push(const_addr[i][0]);
}
MemoryAccessMonitor.enable(const_array, {
onAccess: function (details) {
console.log("\n");
console.log("监控到疑似加密常量的内存访问\n");
console.log(const_name[details.rangeIndex]);
console.log("访问来自:"+details.from.sub(targetSo)+"(可能有误差)");
}
});
}
function hook_suspected_function(targetSo) {
const funcs = [['MD5Init(MD5_CTX *)', '函数MD5Init(MD5_CTX *)疑似哈希函数,包含初始化魔数的代码。', '0x2a1c'], ['MD5Transform(uint *,uchar *)', '函数MD5Transform(uint *,uchar *)疑似哈希函数运算部分。', '0x2d04']];
for (var i in funcs) {
let relativePtr = funcs[i][2];
let funcPtr = targetSo.add(relativePtr);
let describe = funcs[i][1];
let handler = (function() {
return function(args) {
console.log("\n");
console.log(describe);
console.log(Thread.backtrace(this.context,Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n"));
};//Thread.backtrace(this.context, Backtracer.ACCURATE):获取调用栈,this.context 包含当前寄存器状态,Backtracer.ACCURATE 表示精确模式。
})();//DebugSymbol.fromAddress:将每个地址解析成符号信息(例如函数名或文件偏移)
hook_native_addr(funcPtr);
}
}
function main() {
var targetSo = Module.findBaseAddress('libxiaojianbang.so');
// 对疑似哈希算法常量的地址进行监控,使用frida MemoryAccessMonitor API,有几个缺陷,在这里比较鸡肋。
// 1.只监控第一次访问,所以如果此区域被多次访问,后续访问无法获取。可以根据这篇文章做改良和扩展。https://bbs.pediy.com/thread-262104-1.htm
// 2.ARM 64无法使用
// 3.无法查看调用栈
// 在这儿用于验证这些常量是否被访问,访问了就说明可能使用该哈希算法。
// MemoryAccessMonitor在别处可能有较大用处,比如ollvm过的so,或者ida xref失效/过多等情况。
// hook和monitor这两个函数,只能分别注入和测试,两个同时会出错,这可能涉及到frida inline hook的原理
// 除非hook_suspected_function 没结果,否则不建议使用monitor_constants。
// monitor_constants(targetSo);
hook_suspected_function(targetSo);
}
setImmediate(main);
function print_arg(addr){
var module = Process.findRangeByAddress(addr);
if(module != null){
return hexdump(addr) + "\n";
}else{
return ptr(addr) + "\n";
}
}
function hook_native_addr(funcPtr){
var module = Process.findModuleByAddress(funcPtr);
Interceptor.attach(funcPtr, {
onEnter: function(args){
this.args0 = args[0];
this.args1 = args[1];
this.args2 = args[2];
this.args3 = args[3];
this.logs = [];
this.logs.push("call " + module.name + "!" + ptr(funcPtr).sub(module.base) + "\n");
this.logs.push("this.args0 onEnter: " + print_arg(this.args0));
this.logs.push("this.args1 onEnter: " + print_arg(this.args1));
this.logs.push("this.args2 onEnter: " + print_arg(this.args2));
this.logs.push("this.args3 onEnter: " + print_arg(this.args3));
}, onLeave: function(retval){
this.logs.push("this.args0 onLeave: " + print_arg(this.args0));
this.logs.push("this.args1 onLeave: " + print_arg(this.args1));
this.logs.push("this.args2 onLeave: " + print_arg(this.args2));
this.logs.push("this.args3 onLeave: " + print_arg(this.args3));
this.logs.push("retval onLeave: " + retval + "\n");
console.log(this.logs);
}
});
}
结合findhash和traceNatives插件合成的升级自吐算法:
由于我们findhash的js代码需要hook的位置是对应的函数偏移的位置offset的位置,所以我们可以先使用traceNatives先获取函数偏移的位置,再通过修改findhash生成的frida的hook代码实现我们的自吐算法的合成:
修改traceNatives代码:
so_path, so_name = getSoPathAndName()
search_result = [f"'{offset}'" for offset in search_result]
search_result = ",".join(search_result)
search_result = '['+search_result+']'
script_name = so_name.split(".")[0] + "_" + str(int(time.time())) +".txt"
save_path = os.path.join(so_path, script_name)
这里原本是初始的 -a 然后是名字 以及对应的偏移地址,由于我们需要的是偏移地址的数组,所以直接进行修改了
['0x152c','0x16c4','0x16fc','0x1734','0x177c','0x187c','0x18b4','0x18f4','0x193c','0x1974','0x19a8','0x1b60','0x1c5c','0x1e04','0x1ecc','0x1f14','0x1fb0','0x1ff0','0x21c4','0x2380','0x25ac','0x25ec','0x262c','0x2854','0x2a1c','0x2a8c','0x2d04','0x4264','0x4360','0x449c']
得到的数组就是这样的,然后我们直接去修改findhash的代码
修改findhash的frida的hook代码:
function hook_suspected_function(targetSo) {
const funcs = ['0x152c','0x16c4','0x16fc','0x1734','0x177c','0x187c','0x18b4','0x18f4','0x193c','0x1974','0x19a8','0x1b60','0x1c5c','0x1e04','0x1ecc','0x1f14','0x1fb0','0x1ff0','0x21c4','0x2380','0x25ac','0x25ec','0x262c','0x2854','0x2a1c','0x2a8c','0x2d04','0x4264','0x4360','0x449c'];
for (var i in funcs) {
let relativePtr = funcs[i];
let funcPtr = targetSo.add(relativePtr);
hook_native_addr(funcPtr);
}
function hook_native_addr(funcPtr){
var module = Process.findModuleByAddress(funcPtr);
Interceptor.attach(funcPtr, {
onEnter: function(args){
this.args0 = args[0];
this.args1 = args[1];
this.args2 = args[2];
this.args3 = args[3];
this.logs = [];
this.logs.push("call " + module.name + "!" + ptr(funcPtr).sub(module.base) + "\n");
this.logs.push("this.args0 onEnter: " + print_arg(this.args0));
this.logs.push("this.args1 onEnter: " + print_arg(this.args1));
this.logs.push("this.args2 onEnter: " + print_arg(this.args2));
this.logs.push("this.args3 onEnter: " + print_arg(this.args3));
}, onLeave: function(retval){
this.logs.push("this.args0 onLeave: " + print_arg(this.args0));
this.logs.push("this.args1 onLeave: " + print_arg(this.args1));
this.logs.push("this.args2 onLeave: " + print_arg(this.args2));
this.logs.push("this.args3 onLeave: " + print_arg(this.args3));
this.logs.push("retval onLeave: " + retval + "\n");
console.log(this.logs);
}
});
}
hook_native_addr(funcPtr)是我们自定义的函数,为的就是输出对应的参数和函数返回之后的参数
枚举导出表和导入表
先了解一下JSON.stringify()方法,之前一直在使用,但是不了解
JSON.stringify() 是 JavaScript 中的一个方法,用于将 JavaScript 对象或数组转换为 JSON 字符串。它常用于将数据序列化为 JSON 格式,以便传输或存储。
JSON 字符串的特点:
- 结构化数据:JSON字符串通常用来表示结构化的数据,比如对象、数组等。
- 字符串格式:它是一个字符串,符合特定的 JSON 语法规范。
- 通用性:虽然 JSON 起源于 JavaScript,但它已经被广泛应用于几乎所有编程语言中,是网络数据传输的常见格式。
{
"name": "Alice",
"age": 25,
"isStudent": false,
"skills": ["JavaScript", "Python"],
"address": {
"city": "New York",
"zip": "10001"
}
}
导出表和导入表的枚举:
var Imports= Module.enumerateImports("libencryptlib.so");
for(var i=0; i<Imports.length; i++)
{
console.log(JSON.stringify(Imports[i]));//导入表
}
console.log("=================================================================================");
var Exports= Module.enumerateExports("libencryptlib.so");
for(var i=0; i<Exports.length; i++)
{
console.log(JSON.stringify(Exports[i]));//导出表
}
console.log("================================================================================");
var Symbols = Module.enumerateSymbols("libencryptlib.so");
console.log(JSON.stringify(Symbols[0]));//符号表
这里其实可能导出表和符号表出现的信息会有重叠
原因其实是导出表查看的是——>DYNSYM(Dynamic Symbol Table(动态符号表)
符号表查看的是——>SYMTAB(Symbol Table(符号表))
SO层HOOK函数
var MakePassMD5 = Module.findExportByName("libencryptlib.so","_ZN7MD5_CTX11MakePassMD5EPhjS0_");
Interceptor.attach(MakePassMD5,{
onEnter:function(args)
{
var InputChars = args[1];
var InputCharsLen = args[2];
var OutputChars = args[3];
console.log(" InPut: ",hexdump(InputChars));
console.log(InputCharsLen);
console.log(" OtPut: ",hexdump(OutputChars));
this.OutputChars_addr = OutputChars;
},
onLeave:function(result){
console.log("result: ",hexdump(this.OutputChars_addr));
}
});
Interceptor.attach(function_addr,{
onEnter:function(args){
}
onLeave:function(result){
}
}
so库函数基地址获取以及通过偏移hook函数:
假如我们的函数没有出现在导出表和导入表之中,我们要HOOK函数就只能通过利用so的基地址+函数在so中的偏移地址[+1]来实现,得到对应函数的地址
var Module2 = Process.findModuleByName("libencryptlib.so")
console.log("findModuleByName: ",Module2.base);
var Module1 = Process.getModuleByName("libencryptlib.so")
console.log("getModuleByName:",Module1.base);
var module = Process.enumerateModules();
for(var i=0; i<module.length; i++)
{
if(module[i].name=="libencryptlib.so")
{
console.log(JSON.stringify(module[i]));
console.log(module[i].base);
break;
}
}
var base = Module.findBaseAddress("libencryptlib.so")
console.log("findBaseAddress: ",base);
我们可以在ida中查看相应的函数的偏移地址,利用so基地址+偏移地址就可以实现hoo
函数地址的计算
如果是 thumb 指令,函数地址计算方式: so 基址 + 函数在 so 中的偏移 + 1
如果是 arm 指令,函数地址计算方式: so 基址 + 函数在 so 中的偏移
在安卓中, 32 位的 so 中的函数,基本都是 thumb 指令
在安卓中, 64 位的 so 中的函数,基本都是 arm 指令
也可以通过显示汇编指令对应的 opcode bytes ,来判断
options -> general -> Number of opcode bytes (non-graph) 4
arm 指令为 4 个字节,如果函数中有些指令是两个字节,那么函数地址计算需要 + 1
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
var funcAddr = soAddr.add(0x23F4); // 函数地址计算 thumb+1 ARM 不加
var baseaddr = Module.findBaseAddress("libencryptlib.so").add(0x1FA38);
Interceptor.attach(baseaddr,{
onEnter:function(args)
{
var InputChars = args[1];
var InputCharsLen = args[2];
var OutputChars = args[3];
console.log(" InPut: ",hexdump(InputChars));
console.log(InputCharsLen);
console.log(" OtPut: ",hexdump(OutputChars));
this.OutputChars_addr = OutputChars;
},
onLeave:function(result){
console.log("result: ",hexdump(this.OutputChars_addr));
}
so层地址HOOK格式:
Java.perform(function() {
function print_args(args) {
if (args.isNull()) {
return "null";
}
if (args instanceof NativePointer) {
var module = Process.findRangeByAddress(args);
if (module != null) {
return hexdump(args) + "\n";
}
}
return ptr(args)+"\n";
}
function hook_native_add(funPtr, paramNum) {
var Module1 = Process.findModuleByAddress(funPtr);
if (Module1 === null) {
console.log("Module not found for function pointer");
return;
}
Interceptor.attach(funPtr, {
onEnter: function(args) {
this.logs = [];
this.param = [];
for (let i = 0; i < paramNum; i++) {
this.param.push(args[i]);
}
this.logs.push("call " + Module1.name + "!" + ptr(funPtr).sub(Module1.base) + "\n");
for (let i = 0; i < paramNum; i++) {
this.logs.push("this.args" + i + " onEnter: " + print_args(this.param[i]) + "\n");
}
},
onLeave: function(retval) {
for (let i = 0; i < paramNum; i++) {
this.logs.push("this.args" + i + " onLeave: " + print_args(this.param[i]) + "\n");
}
this.logs.push("retval onLeave: " + retval + "\n");
console.log(this.logs.join("\n"));
this.logs = [];
}
});
}
var moduleBase = Module.findBaseAddress("libencryptlib.so");
if (moduleBase !== null) {
var baseaddr = moduleBase.add(0x1FA38);
hook_native_add(baseaddr, 4);
} else {
console.log("Module not found: libencryptlib.so");
}
})
Frida 框架来绕过 Android SSL 钉扎(SSL pinning)机制
创建自定义的 TrustManager:实现一个新的 TrustManager,所有 SSL 证书都被接受,从而绕过 SSL 验证。
checkClientTrusted
和checkServerTrusted
方法被重写以不执行任何检查。
修改 SSLContext 的初始化:重载 SSLContext 的 init()
方法,使用自定义的 TrustManager 进行 SSL 连接。
拦截 OkHttp:针对 OkHttp 库,重载相关方法(如 CertificatePinner.check()
和 hostnameVerifier
),以跳过 SSL 钉扎检查。
处理 WebView:重载 WebViewClient
中的 SSL 错误处理方法,允许所有 SSL 错误,直接调用 sslErrorHandler.proceed()
。
这里是直接对应需要检测的证书,SSLpinning全部去重载了对应的函数,让其检测什么也不做,或者是定义一些变量,直接传入对应的函数,什么也不做,取消对应的功能
Java.perform(function() {
/*
hook list:
1.SSLcontext
2.okhttp
3.webview
4.XUtils
5.httpclientandroidlib
6.JSSE
7.network\_security\_config (android 7.0+)
8.Apache Http client (support partly)
9.OpenSSLSocketImpl
10.TrustKit
11.Cronet
*/
// Attempts to bypass SSL pinning implementations in a number of
// ways. These include implementing a new TrustManager that will
// accept any SSL certificate, overriding OkHTTP v3 check()
// method etc.
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');
var SSLContext = Java.use('javax.net.ssl.SSLContext');
var quiet_output = false;
// Helper method to honor the quiet flag.
function quiet_send(data) {
if (quiet_output) {
return;
}
send(data)
}
// Implement a new TrustManager
// ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8
// Java.registerClass() is only supported on ART for now(201803). 所以android 4.4以下不兼容,4.4要切换成ART使用.
/*
06-07 16:15:38.541 27021-27073/mi.sslpinningdemo W/System.err: java.lang.IllegalArgumentException: Required method checkServerTrusted(X509Certificate[], String, String, String) missing
06-07 16:15:38.542 27021-27073/mi.sslpinningdemo W/System.err: at android.net.http.X509TrustManagerExtensions.<init>(X509TrustManagerExtensions.java:73)
at mi.ssl.MiPinningTrustManger.<init>(MiPinningTrustManger.java:61)
06-07 16:15:38.543 27021-27073/mi.sslpinningdemo W/System.err: at mi.sslpinningdemo.OkHttpUtil.getSecPinningClient(OkHttpUtil.java:112)
at mi.sslpinningdemo.OkHttpUtil.get(OkHttpUtil.java:62)
at mi.sslpinningdemo.MainActivity$1$1.run(MainActivity.java:36)
*/
var X509Certificate = Java.use("java.security.cert.X509Certificate");
var TrustManager;
try {
TrustManager = Java.registerClass({//创建自定义的 TrustManager
name: 'org.wooyun.TrustManager',
implements: [X509TrustManager],
methods: {
checkClientTrusted: function(chain, authType) {},//checkClientTrusted 和 checkServerTrusted 方法被重写以不执行任何检查。
checkServerTrusted: function(chain, authType) {},
getAcceptedIssuers: function() {
// var certs = [X509Certificate.$new()];
// return certs;
return [];
}
}
});
} catch (e) {
quiet_send("registerClass from X509TrustManager >>>>>>>> " + e.message);
}
// Prepare the TrustManagers array to pass to SSLContext.init()
var TrustManagers = [TrustManager.$new()];
try {
// Prepare a Empty SSLFactory修改 SSLContext 的初始化,载入新修改的TrustManagers
var TLS_SSLContext = SSLContext.getInstance("TLS");
TLS_SSLContext.init(null, TrustManagers, null);
var EmptySSLFactory = TLS_SSLContext.getSocketFactory();
} catch (e) {
quiet_send(e.message);
}
send('Custom, Empty TrustManager ready');
// Get a handle on the init() on the SSLContext class
var SSLContext_init = SSLContext.init.overload(
'[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
// Override the init method, specifying our new TrustManager
SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
quiet_send('Overriding SSLContext.init() with the custom TrustManager');
SSLContext_init.call(this, null, TrustManagers, null);
};
/*** okhttp3.x unpinning ***/
// Wrap the logic in a try/catch as not all applications will have
// okhttp as part of the app.
try {
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
quiet_send('OkHTTP 3.x Found');
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {
quiet_send('OkHTTP 3.x check() called. Not throwing an exception.');
}
var OkHttpClient$Builder = Java.use('okhttp3.OkHttpClient$Builder');
quiet_send('OkHttpClient$Builder Found');
console.log("hostnameVerifier", OkHttpClient$Builder.hostnameVerifier);
OkHttpClient$Builder.hostnameVerifier.implementation = function () {
quiet_send('OkHttpClient$Builder hostnameVerifier() called. Not throwing an exception.');
return this;
}
var myHostnameVerifier = Java.registerClass({
name: 'com.xiaojianbang.MyHostnameVerifier',
implements: [HostnameVerifier],
methods: {
verify: function (hostname, session) {
return true;
}
}
});
var OkHttpClient = Java.use('okhttp3.OkHttpClient');
OkHttpClient.hostnameVerifier.implementation = function () {
quiet_send('OkHttpClient hostnameVerifier() called. Not throwing an exception.');
return myHostnameVerifier.$new();
}
} catch (err) {
// If we dont have a ClassNotFoundException exception, raise the
// problem encountered.
if (err.message.indexOf('ClassNotFoundException') === 0) {
throw new Error(err);
}
}
// Appcelerator Titanium PinningTrustManager
// Wrap the logic in a try/catch as not all applications will have
// appcelerator as part of the app.
try {
var PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
send('Appcelerator Titanium Found');
PinningTrustManager.checkServerTrusted.implementation = function() {
quiet_send('Appcelerator checkServerTrusted() called. Not throwing an exception.');
}
} catch (err) {
// If we dont have a ClassNotFoundException exception, raise the
// problem encountered.
if (err.message.indexOf('ClassNotFoundException') === 0) {
throw new Error(err);
}
}
/*** okhttp unpinning ***/
try {
var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient");
OkHttpClient.setCertificatePinner.implementation = function(certificatePinner) {
// do nothing
quiet_send("OkHttpClient.setCertificatePinner Called!");
return this;
};
// Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation)
var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner");
CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(p0, p1) {
// do nothing
quiet_send("okhttp Called! [Certificate]");
return;
};
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(p0, p1) {
// do nothing
quiet_send("okhttp Called! [List]");
return;
};
} catch (e) {
quiet_send("com.squareup.okhttp not found");
}
/*** WebView Hooks ***/
/* frameworks/base/core/java/android/webkit/WebViewClient.java */
/* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */
var WebViewClient = Java.use("android.webkit.WebViewClient");
WebViewClient.onReceivedSslError.implementation = function(webView, sslErrorHandler, sslError) {
quiet_send("WebViewClient onReceivedSslError invoke");
//执行proceed方法
sslErrorHandler.proceed();
return;
};
WebViewClient.onReceivedError.overload('android.webkit.WebView', 'int', 'java.lang.String', 'java.lang.String').implementation = function(a, b, c, d) {
quiet_send("WebViewClient onReceivedError invoked");
return;
};
WebViewClient.onReceivedError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function() {
quiet_send("WebViewClient onReceivedError invoked");
return;
};
/*** JSSE Hooks ***/
/* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */
/* public final TrustManager[] getTrustManager() */
/* TrustManagerFactory.getTrustManagers maybe cause X509TrustManagerExtensions error */
// var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
// TrustManagerFactory.getTrustManagers.implementation = function(){
// quiet_send("TrustManagerFactory getTrustManagers invoked");
// return TrustManagers;
// }
var HttpsURLConnection = Java.use("com.android.okhttp.internal.huc.HttpsURLConnectionImpl");
HttpsURLConnection.setSSLSocketFactory.implementation = function(SSLSocketFactory) {
quiet_send("HttpsURLConnection.setSSLSocketFactory invoked");
};
HttpsURLConnection.setHostnameVerifier.implementation = function(hostnameVerifier) {
quiet_send("HttpsURLConnection.setHostnameVerifier invoked");
};
/*** Xutils3.x hooks ***/
//Implement a new HostnameVerifier
var TrustHostnameVerifier;
try {
TrustHostnameVerifier = Java.registerClass({
name: 'org.wooyun.TrustHostnameVerifier',
implements: [HostnameVerifier],
method: {
verify: function(hostname, session) {
return true;
}
}
});
} catch (e) {
//java.lang.ClassNotFoundException: Didn't find class "org.wooyun.TrustHostnameVerifier"
quiet_send("registerClass from hostnameVerifier >>>>>>>> " + e.message);
}
try {
var RequestParams = Java.use('org.xutils.http.RequestParams');
RequestParams.setSslSocketFactory.implementation = function(sslSocketFactory) {
sslSocketFactory = EmptySSLFactory;
return null;
}
RequestParams.setHostnameVerifier.implementation = function(hostnameVerifier) {
hostnameVerifier = TrustHostnameVerifier.$new();
return null;
}
} catch (e) {
quiet_send("Xutils hooks not Found");
}
/*** httpclientandroidlib Hooks ***/
try {
var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String', '[Ljava.lang.String', 'boolean').implementation = function() {
quiet_send("httpclientandroidlib Hooks");
return null;
}
} catch (e) {
quiet_send("httpclientandroidlib Hooks not found");
}
/***
android 7.0+ network_security_config TrustManagerImpl hook
apache httpclient partly
***/
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
// try {
// var Arrays = Java.use("java.util.Arrays");
// //apache http client pinning maybe baypass
// //https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#471
// TrustManagerImpl.checkTrusted.implementation = function (chain, authType, session, parameters, authType) {
// quiet_send("TrustManagerImpl checkTrusted called");
// //Generics currently result in java.lang.Object
// return Arrays.asList(chain);
// }
//
// } catch (e) {
// quiet_send("TrustManagerImpl checkTrusted nout found");
// }
try {
// Android 7+ TrustManagerImpl
TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
quiet_send("TrustManagerImpl verifyChain called");
// Skip all the logic and just return the chain again :P
//https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/november/bypassing-androids-network-security-configuration/
// https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650
return untrustedChain;
}
} catch (e) {
quiet_send("TrustManagerImpl verifyChain nout found below 7.0");
}
// OpenSSLSocketImpl
try {
var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, authMethod) {
quiet_send('OpenSSLSocketImpl.verifyCertificateChain');
}
quiet_send('OpenSSLSocketImpl pinning')
} catch (err) {
quiet_send('OpenSSLSocketImpl pinner not found');
}
// Trustkit
try {
var Activity = Java.use("com.datatheorem.android.trustkit.pinning.OkHostnameVerifier");
Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(str) {
quiet_send('Trustkit.verify1: ' + str);
return true;
};
Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(str) {
quiet_send('Trustkit.verify2: ' + str);
return true;
};
quiet_send('Trustkit pinning')
} catch (err) {
quiet_send('Trustkit pinner not found')
}
try {
//cronet pinner hook
//weibo don't invoke
var netBuilder = Java.use("org.chromium.net.CronetEngine$Builder");
//https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/CronetEngine.Builder.html#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean)
netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.implementation = function(arg) {
//weibo not invoke
console.log("Enables or disables public key pinning bypass for local trust anchors = " + arg);
//true to enable the bypass, false to disable.
var ret = netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this, true);
return ret;
};
netBuilder.addPublicKeyPins.implementation = function(hostName, pinsSha256, includeSubdomains, expirationDate) {
console.log("cronet addPublicKeyPins hostName = " + hostName);
//var ret = netBuilder.addPublicKeyPins.call(this,hostName, pinsSha256,includeSubdomains, expirationDate);
//this 是调用 addPublicKeyPins 前的对象吗? Yes,CronetEngine.Builder
return this;
};
} catch (err) {
console.log('[-] Cronet pinner not found')
}
});
过Root检测
这里有两个Root检测的函数,
一个是在命令行中执行 which su 找到对应root权限的路径
new BufferedReader(new InputStreamReader(process.getInputStream())).readLine() 读取对应的可能得到的路径
假如能够读出来,就说明有root权限
public static boolean checkSuFile() {
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[]{"which", "su"});
if (new BufferedReader(new InputStreamReader(process.getInputStream())).readLine() != null) {
if (process != null) {
process.destroy();
}
return true;
}
if (process != null) {
process.destroy();
}
return false;
} catch (Throwable th) {
if (process != null) {
process.destroy();
}
throw th;
}
}
这里的ROOT检测就是直接去执行对应路径的命令,查看有权限
public static File checkRootFile() {
String[] strArr = {"/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su"};
int length = strArr.length;
File file = null;
int i = 0;
while (i < length) {
File file2 = new File(strArr[i]);
if (file2.exists()) {
return file2;
}
i++;
file = file2;
}
return file;
}
绕过:
var SystemUtils = Java.use("com.hoge.android.factory.util.system.SystemUtils");
SystemUtils.checkSuFile.implementation=function()
{
var result = this.checkSuFile();
console.log(result);
return false;
}//第二个函数其实也是看第一个函数的返回值
JNI函数HOOK插件jnitrace
https://github.com/chame1eon/jnitrace
使用方法:
-R <host>:<port>- 用于指定远程 Frida 服务器的网络位置。如果未指定 :,则默认使用 localhost:27042。
-m <spawn|attach>- 用于指定要使用的 Frida 附加机制。它可以是 spawn 或 attachment。Spawn 是默认和推荐的选项。
-b <fuzzy|accurate|none>- 用于控制回溯输出。默认情况下,jnitrace将在模式下运行回溯器accurate。可以使用此选项更改为fuzzy模式或使用该none选项停止回溯。有关差异的说明,请参阅 Frida 文档。
-i <regex>- 用于指定应跟踪的方法名称。这有助于减少特别大型 JNI 应用中的噪音。该选项可以多次提供。例如,-i Get -i RegisterNatives将仅包含名称中包含 Get 或 RegisterNatives 的 JNI 方法。
-e <regex>- 用于指定应在跟踪中忽略的方法名称。这有助于减少特别大型 JNI 应用中的噪音。该选项可以多次提供。例如,-e ^Find -e GetEnv将从结果中排除所有以 Find 开头或包含 GetEnv 的 JNI 方法名称。
-I <string>- 用于指定应跟踪的库的导出。这对于您只想跟踪少量方法的库很有用。jnitrace 认为导出的函数是任何可从 Java 端直接调用的函数,例如,包括使用 RegisterNatives 绑定的方法。该选项可以多次提供。例如, -I stringFromJNI -I nativeMethod([B)V可用于包含调用的库的导出Java_com_nativetest_MainActivity_stringFromJNI和使用 RegisterNames 绑定的方法,签名为nativeMethod([B)V。
-E <string>用于指定不应跟踪的库的导出。这对于您想要忽略一组繁忙的本机调用的库很有用。jnitrace 认为导出的函数是任何可从 Java 端直接调用的函数,例如,包括使用 RegisterNatives 绑定的方法。该选项可以多次提供。例如,-E JNI_OnLoad -E nativeMethod将从跟踪中排除JNI_OnLoad函数调用和任何名为 的方法nativeMethod。
-o path/output.json- 用于指定jnitrace存储所有跟踪数据的输出路径。信息以 JSON 格式存储,以便以后对跟踪数据进行后处理。
-p path/to/script.js- 提供的路径用于在jnitrace脚本加载之前将 Frida 脚本加载到目标进程中。这可用于在jnitrace启动之前击败反 Frida 或反调试代码。
-a path/to/script.js- 提供的路径用于在jnitrace加载后将 Frida 脚本加载到目标进程中。
--hide-data- 用于减少控制台中显示的输出量。此选项将隐藏显示为十六进制转储或字符串反引用的其他数据。
--ignore-env- 使用此选项将隐藏应用程序使用 JNIEnv 结构进行的所有调用。
--ignore-vm- 使用此选项将隐藏应用程序使用 JavaVM 结构进行的所有调用。
--aux <name=(string|bool|int)value>- 用于在生成应用程序时传递自定义参数。例如,--aux='uid=(int)10'将为用户 10 而不是默认用户 0 生成应用程序。
最常使用的命令,按照附加的方式来进行:
jnitrace -m attach -l liblogin_encrypt.so com.ximalaya.ting.android -o .\out.json
这个插件是对于JNI函数的HOOK,在SO层的HOOK中,so文件有可能会被混淆,导致关键函数混淆不清,不过在SO层的加密终究需要的是JNI函数将C/C++转为Java的数据类型,这时候就需要JNI函数的使用,我们直接去HOOK了JNI函数,也可以了解到大概的执行流程。
部分JSON的输出:
[
{
"struct": "JNIEnv",
"method": {
"name": "NewStringUTF",
"args": [
"JNIEnv*",
"char*"
],
"ret": "jstring"
},
"thread_id": 17066,
"timestamp": 1565,
"backtrace": [
{
"address": "0xbd1260df",
"module": {
"name": "liblogin_encrypt.so",
"base": "0xbd122000",
"size": 57344,
"path": "/data/app/com.ximalaya.ting.android-h0ODZtd-h4A7yO2l2eCojw==/lib/arm/liblogin_encrypt.so"
},
"symbol": {
"address": "0xbd1260df",
"name": "Java_com_ximalaya_ting_android_loginservice_LoginEncryptUtil_PDuxkguhSq+0x1a",
"moduleName": "liblogin_encrypt.so",
"fileName": "",
"lineNumber": 0
}
}
],
"args": [
{
"value": "0xf3889140"
},
{
"value": "0xbd12f870",
"data": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVhaR3Or7suUlwHUl2Ly36uVmboZ3+HhovogDjLgRE9CbaUokS2eqGaVFfbxAUxFThNDuXq/fBD+SdUgppmcZrIw4HMMP4AtE2qJJQH/KxPWmbXH7Lv+9CisNtPYOlvWJ/GHRqf9x3TBKjjeJ2CjuVxlPBDX63+Ecil2JR9klVawIDAQAB"
}
],
"ret": {
"value": "0x75"
},
"java_params": []
},
{
"struct": "JNIEnv",
"method": {
"name": "NewStringUTF",
"args": [
"JNIEnv*",
"char*"
],
"ret": "jstring"
},
"thread_id": 17066,
"timestamp": 1872,
"backtrace": [
{
"address": "0xbd122b93",
"module": {
"name": "liblogin_encrypt.so",
"base": "0xbd122000",
"size": 57344,
"path": "/data/app/com.ximalaya.ting.android-h0ODZtd-h4A7yO2l2eCojw==/lib/arm/liblogin_encrypt.so"
},
"symbol": {
"address": "0xbd122b93",
"name": "0xb93",
"moduleName": "liblogin_encrypt.so",
"fileName": "",
"lineNumber": 0
}
}
],
"args": [
{
"value": "0xf3889140"
},
{
"value": "0xbd12f055",
"data": "RSA"
}
],
"ret": {
"value": "0x89"
},
"java_params": []
},
{
"struct": "JNIEnv",
"method": {
"name": "FindClass",
"args": [
"JNIEnv*",
"char*"
],
"ret": "jclass"
},
"thread_id": 17066,
"timestamp": 1885,
"backtrace": [
{
"address": "0xbd122ba5",
"module": {
"name": "liblogin_encrypt.so",
"base": "0xbd122000",
"size": 57344,
"path": "/data/app/com.ximalaya.ting.android-h0ODZtd-h4A7yO2l2eCojw==/lib/arm/liblogin_encrypt.so"
},
"symbol": {
"address": "0xbd122ba5",
"name": "0xba5",
"moduleName": "liblogin_encrypt.so",
"fileName": "",
"lineNumber": 0
}
}
],
"args": [
{
"value": "0xf3889140"
},
{
"value": "0xbd12f060",
"data": "java/security/KeyFactory"
}
],
"ret": {
"value": "0x99",
"metadata": "java/security/KeyFactory"
},
"java_params": []
},
部分cmd输出:
/* TID 17812 */
1962 ms [+] JNIEnv->NewStringUTF
1962 ms |- JNIEnv* : 0xf3889140
1962 ms |- char* : 0xb61ea870
1962 ms |: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVhaR3Or7suUlwHUl2Ly36uVmboZ3+HhovogDjLgRE9CbaUokS2eqGaVFfbxAUxFThNDuXq/fBD+SdUgppmcZrIw4HMMP4AtE2qJJQH/KxPWmbXH7Lv+9CisNtPYOlvWJ/GHRqf9x3TBKjjeJ2CjuVxlPBDX63+Ecil2JR9klVawIDAQAB
1962 ms |= jstring : 0x75
1962 ms ----------------------------------------------------------Backtrace----------------------------------------------------------
1962 ms |-> 0xb61e10df: Java_com_ximalaya_ting_android_loginservice_LoginEncryptUtil_PDuxkguhSq+0x1a (liblogin_encrypt.so:0xb61dd000)
/* TID 17812 */
2245 ms [+] JNIEnv->NewStringUTF
2245 ms |- JNIEnv* : 0xf3889140
2245 ms |- char* : 0xb61ea055
2245 ms |: RSA
2245 ms |= jstring : 0x89
2245 ms ---------------------------------Backtrace---------------------------------
2245 ms |-> 0xb61ddb93: liblogin_encrypt.so!0xb93 (liblogin_encrypt.so:0xb61dd000)
/* TID 17812 */
2254 ms [+] JNIEnv->FindClass
2254 ms |- JNIEnv* : 0xf3889140
2254 ms |- char* : 0xb61ea060
2254 ms |: java/security/KeyFactory
2254 ms |= jclass : 0x95 { java/security/KeyFactory }
2254 ms ---------------------------------Backtrace---------------------------------
2254 ms |-> 0xb61ddba5: liblogin_encrypt.so!0xba5 (liblogin_encrypt.so:0xb61dd000)
/* TID 17812 */
2263 ms [+] JNIEnv->GetStaticMethodID
2263 ms |- JNIEnv* : 0xf3889140
2263 ms |- jclass : 0x95 { java/security/KeyFactory }
2263 ms |- char* : 0xb61ea079
2263 ms |: getInstance
2263 ms |- char* : 0xb61ea090
2263 ms |: (Ljava/lang/String;)Ljava/security/KeyFactory;
2263 ms |= jmethodID : 0x7036fae8 { getInstance(Ljava/lang/String;)Ljava/security/KeyFactory; }
2263 ms ---------------------------------Backtrace---------------------------------
2263 ms |-> 0xb61ddbc1: liblogin_encrypt.so!0xbc1 (liblogin_encrypt.so:0xb61dd000)
/* TID 17812 */
2271 ms [+] JNIEnv->CallStaticObjectMethod
2271 ms |- JNIEnv* : 0xf3889140
2271 ms |- jclass : 0x95 { java/security/KeyFactory }
2271 ms |- jmethodID : 0x7036fae8 { getInstance(Ljava/lang/String;)Ljava/security/KeyFactory; }
2271 ms |: jstring : 0x89
2271 ms |= jobject : 0xa9 { java/security/KeyFactory }
2271 ms ---------------------------------Backtrace---------------------------------
2271 ms |-> 0xb61ddbd3: liblogin_encrypt.so!0xbd3 (liblogin_encrypt.so:0xb61dd000)
/* TID 17812 */
2281 ms [+] JNIEnv->ExceptionCheck
2281 ms |- JNIEnv* : 0xf3889140
2281 ms |= jboolean : 0 { false }
这里看来其实Cmd看着更顺应。
修改函数数值参数返回值
var helloAddr = Module.findExportByName("libxiaojianbang.so", "add");
console.log(helloAddr);
if(helloAddr != null){
Interceptor.attach(helloAddr,
{
onEnter: function(args){
args[2] = ptr(1000); //new NativePointer() //这里修改参数要传入的是指针
console.log(args[2].toInt32());
},
onLeave: function(retval)
{
retval.replace(20000);//返回值要使用replace()方法
console.log("retval", retval.toInt32());
}
});
}
修改函数字符串参数(通过创建字符串,写入地址)
缺点:可能会覆盖内存数据
var soAddr = Module.findExportByName("libxiaojianbang.so", "add");
var MD5Update = soAddr.add(0x1D68);
Interceptor.attach(MD5Update, {
onEnter: function (args) {
if(args[1].readCString() == "xiaojianbang"){//这里判断是因为Update有三次,两次填充
var newStr = "xiaojian";//修改字符串参数要内存地址对应值的修改
args[1].writeByteArray(hexToBytes(stringToHex(newStr) + "00"));//所以这里要进行参数的写入,同时,字符串要遇到‘00’截断,所以要补0
console.log(hexdump(args[1]));
args[2] = ptr(newStr.length);
console.log(args[2].toInt32());
}
}, onLeave: function (retval) {
}
});
修改函数字符串参数(通过申请空间来实现参数的赋值,实现修改参数)
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
console.log("soAddr", soAddr);
var MD5Update = soAddr.add(0x1D68);
var newStr = "gdfgdhfgjghjgkhjkh;kl;k;";//这里设置成了全局变量,这样才不会在onLeave的时候被销毁
var newStrAddr = Memory.allocUtf8String(newStr);
Interceptor.attach(MD5Update, {
onEnter: function (args) {
this.args0 = args[0];
this.args1 = args[1];
if(args[1].readCString() == "xiaojianbang"){
args[1] = newStrAddr;
console.log(hexdump(args[1]));
args[2] = ptr(newStr.length);
console.log(args[2].toInt32());
}
}, onLeave: function (retval) {
if(this.args1.readCString() == "xiaojianbang"){
console.log(hexdump(this.args0));
}
}
});
监听dlopen来HOOK已加载的函数
利用dlopen实现so之间的相互使用,这里通过去得到path,利用dlopen实现了so之间的访问和使用
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
Log.d("Tag", "getPath:"+getPath(getApplicationContext()));//调用getApplicationContext()来获取需要的Context类
val path: String? = getPath(getApplicationContext()+"/libchen_chen_chen2.so");//获取path
val stringVar: String = path as String;//转字符串,假如是null就会抛出异常
stringVar+="/libchen_chen_chen2.so"
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Log.d("tag","oncreate");
// Example of a call to a native method
binding.sampleText.text = stringFromJNI(stringVar);
}
private external fun stringFromJNI(string: String): String
------------------------------------------------------------------------------------------------
void* soinfo = dlopen(cpath, RTLD_NOW);//这里去获取对应路径下的so文件的句柄
if (soinfo == nullptr) {
// 处理 dlopen 失败的情况
__android_log_print(ANDROID_LOG_ERROR, "JNI", "Failed to load library: %s", dlerror());
env->ReleaseStringUTFChars(path, cpath);
return nullptr;
}
//由于我们是通过的对应路径去查找的so文件的函数名,所以这里创建了一个函数指针去得到对应的函数句柄,然后直接调用
void (*def)(char*) = reinterpret_cast<void (*)(char*)>(dlsym(soinfo, "_Z7seconedv"));//直接去利用
所以我们HOOK也可以去使用dlopen,实现监测so是否被加载,加载之后就可以直接进行HOOK
同时在程序版本不同时,有两个dlopen可能会去实现so的访问,所以两个都要HOOK
dlopen_HOOK:
function Hook_dlopen(addr,Soname,callback)
{
Interceptor.attach(addr,{
onEnter: function(args){
var pathname =args[0].readCString();
console.log("dlopen args[0]: ", pathname);
if(pathname.indexOf(Soname)!=-1)
{
this.hook=true;
console.log("-------------------HOOK_function_starting--------------------");
}
},
onLeave:function(retval) {
console.log("onLeave retvel :",retval)
if(this.hook)callback()
}
})
}
function hookfunc(){
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
console.log("soAddr", soAddr);
var MD5Final = soAddr.add(0x3540);
Interceptor.attach(MD5Final, {
onEnter: function (args) {
this.args1 = args[1];
}, onLeave: function (retval) {
console.log(hexdump(this.args1));
}
});
console.log("-------------------HOOK_function_END--------------------");
}
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
Hook_dlopen(dlopen, "libxiaojianbang.so", hookfunc);
Hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", hookfunc);
这里的dlopen的HOOKfunc是去HOOK了一个MD5的dofinal的位置
内存读写
- 读取指定地址的字符串
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
console.log(soAddr.add(0x2C00).readCString());
- dump 指定地址的内存
console.log(hexdump(soAddr.add(0x2C00)));
- 读指定地址的内存
console.log(soAddr.add(0x2C00).readByteArray(16));
console.log(Memory.readByteArray(soAddr.add(0x2C00), 16)); // 原先的 API
- 写指定地址的内存
soAddr.add(0x2C00).writeByteArray(stringToBytes("xiaojianbang"));//这里的stringToBytes是直接单字符的传入对应的ASCII码值,所以内存存入的是xiaojianbang这几个字符
console.log(hexdump(soAddr.add(0x2C00)));
-----------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------
soAddr.add(0x2C00).writeByteArray(hexToBytes("0123456789abcdef"));//这里的hex数据是读取两个字符作为一个hex数据,所以传入的数据在内存中是对应的值,01,23,45........
console.log(hexdump(soAddr.add(0x2C00)));
- 申请新内存写入
var memory = Memory.alloc();
memory.writeByteArrary(stringToBytes("xiaojianbang\0"))//这里同理写入是单字符对应ASCII,传入的是对应字符串
Memory.allocUtf8String()
- 修改内存权限
Memory.protect(ptr(libso.base), libso.size, 'rwx');
frida修改so函数代码
-
修改地址对应的指令
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
soAddr.add(0x1684).writeByteArray(hexToBytes("0001094B"));
ARM与Hex在线转换 https://armconverter.com/
-
将对应地址的指令解析成汇编
var ins = Instruction.parse(soAddr.add(0x1684));
console.log(ins.toString());
-
利用frida提供的api来写汇编代码
new Arm64Writer(soAddr.add(0x167C)).putNop();
console.log(Instruction.parse(soAddr.add(0x167C)).toString());
-
利用frida提供的api来写汇编代码
var codeAddr = soAddr.add(0x167C);
Memory.patchCode(codeAddr, 8, function (code) {
var Writer = new Arm64Writer(code, {pc: codeAddr});
Writer.putBytes(hexToBytes("0001094B"));
Writer.putBytes(hexToBytes("FF830091"));
Writer.putRet();
Writer.flush();
});
循环读取HOOK之后的代码:
for(var i =0 ; i<3;i++)
{
var SOaddr = Module.findBaseAddress("libxiaojianbang.so");
var ins = Instruction.parse(SOaddr.add(0x167C+i*4));
console.log(ins.toString());
}
so层的主动调用
原生函数
new NativeFunction(address, returnType, argTypes[, abi])
:创建一个新的 NativeFunction 来调用函数address
(用 指定NativePointer
),其中returnType
指定返回类型,argTypes
数组指定参数类型。abi
如果不是系统默认值,您也可以选择指定。对于可变参数函数,'...'
在固定参数和可变参数之间添加一个条目argTypes
。
var soaddr = Module.findBaseAddress("libxiaojianbang.so");
var funcaddr = soaddr.add(0x124C);
var sofunction = new NativeFunction(funcaddr,"pointer", ["pointer","pointer"]);
var env = Java.vm.tryGetEnv();
console.log("env: ",JSON.stringify(env));
var jString = env.newStringUtf("chen_chen_chen");
var cstring = sofunction(env,jString);
console.log("jstring_2_cstring:",hexdump(cstring));
console.log("jstring_2_cstring:",cstring.readCString());
调用libc库函数,写入文件
function call_libc_writefile()
{
var addr_fopen = Module.findExportByName("libc.so", "fopen");
var addr_fputs = Module.findExportByName("libc.so", "fputs");
var addr_fclose = Module.findExportByName("libc.so", "fclose");
console.log("addr_fopen: ",addr_fopen);
console.log("addr_fputs: ",addr_fputs);
console.log("addr_fclose: ",addr_fclose);
var filename = Memory.allocUtf8String("/data/data/com.xiaojianbang.app/xiaojianbang.txt");
var mode=Memory.allocUtf8String("w");
var str = Memory.allocUtf8String("this is a test\n");
var fopen = new NativeFunction(addr_fopen,"pointer", ["pointer","pointer"]);
var fputs = new NativeFunction(addr_fputs,"int", ["pointer","pointer"]);
var fclose = new NativeFunction(addr_fclose,"int", ["pointer"]);
var file = fopen(filename,mode);
fputs(str,file);
fclose(file);
}
HOOK JNI函数
JNI在libart.so中,所以要去枚举这里的函数
function Hook_JNI()
{
var newStringUTF =null
var Symbols = Process.findModuleByName("libart.so").enumerateSymbols();
for (let i = 0; i < Symbols.length; i++) {
var sym = Symbols[i];
if (sym.name.indexOf("CheckJNI"==-1) && sym.name.indexOf("NewStringUTF")!=-1)
{
console.log("symbols name: " + sym.name);
console.log("symbols addr: " + sym.address);
newStringUTF = sym.address;
console.log("newStringUTF: ",newStringUTF);
break;
}
}
console.log("newStringUTF: ",newStringUTF);
if(newStringUTF != null){
Interceptor.attach(newStringUTF,{
onEnter: function(args){
console.log("jni args[1]: ", args[1].readCString());
},
onLeave:function(retval) {
}
})
}
}
主动调用JNI函数
function Hook_JNI()
{
var env = Java.vm.tryGetEnv().handle;
var newStringUTF =null
var Symbols = Process.findModuleByName("libart.so").enumerateSymbols();
for (let i = 0; i < Symbols.length; i++) {
var sym = Symbols[i];
if (sym.name.indexOf("CheckJNI"==-1) && sym.name.indexOf("NewStringUTF")!=-1)
{
console.log("symbols name: " + sym.name);
console.log("symbols addr: " + sym.address);
newStringUTF = sym.address;
console.log("newStringUTF: ",newStringUTF);
break;
}
}
console.log("newStringUTF: ",newStringUTF);
var newString = new NativeFunction(newStringUTF,"pointer", ["pointer","pointer"]);
var mode=Memory.allocUtf8String("w");
var jString = Memory.allocUtf8String("this is a test\n");
var newStringaddr = newString(env,jString);
console.log(newStringaddr);
So层堆栈打印
console.log(Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n')
二级指针构造
function call_func() {
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
var xiugaiStr = soAddr.add(0x17D0);
var xiugaiStr_func = new NativeFunction(xiugaiStr, 'int64', ['pointer']);
var strAddr = Memory.allocUtf8String("dajianbang");//一级指针里面存放字符串
console.log(hexdump(strAddr));
var finalAddr = Memory.alloc(8).writePointer(strAddr);//二级指针指向一级指针
xiugaiStr_func(finalAddr);//这里需要传入二级指针
console.log(hexdump(strAddr));
}
判断SO层对应函数存在于哪个so文件中
dlsym函数HOOK
dlsym
函数的主要功能是在运行时动态加载共享库(动态链接库)中的符号(函数或变量),并返回该符号的地址。
void (*def)(char*) = reinterpret_cast<void (*)(char*)>(dlsym(soinfo, "_Z7seconedv"));//直接去利用
dlsym(sopath,funcname)
function hook_dlsym()
{
var dlsymAddr = Module.findExportByName("libdl.so","dlsym");
console.log("dlsymAddr: ",dlsymAddr);
Interceptor.attach(dlsymAddr,
{
onEnter: function(args)
{
this.funcname = args[1];
},
onLeave: function(result) {
var module = Process.findModuleByAddress(result);
console.log("dlsym: ",this.funcname);
console.log("dlsym result: ",result);
// console.log(Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') +'\n')
if(module==null) return;
console.log(this.funcname.readCString() + "moduele:"+module.name );
console.log("addr:"+result);
console.log("offset:",result.sub(module.base))
}
})
}
RegisterNatives()函数HOOK
RegisterNatives()在Native中注册函数
auto clazz = env->FindClass("com/xiaojianbang/ndk/NativeHelper");
JNINativeMethod methods[] = {
{"encode", "()Ljava/lang/String;", (void *)_strcat},
};
env->RegisterNatives(clazz, methods, 1);
function hook_RegisterNatives()
{
var symbols = Process.getModuleByName("libart.so").enumerateSymbols();
var RegisterNatives =null;
for (let i = 0; i < symbols.length; i++) {
if(symbols[i].name.indexOf("CheckJNI")==-1&&symbols[i].name.indexOf("RegisterNatives")!=-1)
RegisterNatives = symbols[i].address;
}
console.log("RegisterNatives address:"+RegisterNatives);
Interceptor.attach(RegisterNatives,
{
onEnter: function(args)
{
var env = Java.vm.tryGetEnv();
var classname = env.getClassName(args[1]);
console.log("RegisterNatives classname:"+classname);
var methodnum = args[3].toInt32();
var methodname = args[2].readPointer().readCString();
var signature = args[2].add(Process.pointerSize).readPointer().readCString();
var fnPtr = args[2].add(Process.pointerSize*2).readPointer();
var module = Process.findModuleByAddress(fnPtr);
console.log("RegisterNatives methodname:"+methodname);
console.log("RegisterNatives signature:"+signature);
console.log("RegisterNatives methodnum:"+methodnum);
console.log("module name:"+module.name);
console.log("func:"+ fnPtr+"\n"+ "offset:" +fnPtr.sub(module.base));
},
onLeave: function(result) {
}
})
}
HOOK汇编inlineHOOK
function inline_Hook()
{
var module_base_addr = Module.getBaseAddress("libxiaojianbang.so");
var Hook_addr = module_base_addr.add(0x01D40);
Interceptor.attach(Hook_addr,
{
onEnter: function(args)
{
console.log(this.context.x12.toInt32());
},
onLeave: function(result) {
console.log(this.context.x12.toInt32());
}});
}
安卓loadLibrary源码分析
loadLibrary——>loadLibrary0(classLoader, libname);
部分源码:
998 synchronized void loadLibrary0(ClassLoader loader, String libname) {
999 if (libname.indexOf((int)File.separatorChar) != -1) {
1000 throw new UnsatisfiedLinkError(
1001 "Directory separator should not appear in library name: " + libname);
1002 }
1003 String libraryName = libname;
1004 if (loader != null) {
1005 String filename = loader.findLibrary(libraryName);//这里实现了so文件名的拼接
1006 if (filename == null) {
1007 // It's not necessarily true that the ClassLoader used
1008 // System.mapLibraryName, but the default setup does, and it's
1009 // misleading to say we didn't find "libMyLibrary.so" when we
1010 // actually searched for "liblibMyLibrary.so.so".
1011 throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
1012 System.mapLibraryName(libraryName) + "\"");
1013 }
1014 String error = nativeLoad(filename, loader);//这里开始进行加载
// -----------------------------------------------------------------------------------
1015 if (error != null) {
1016 throw new UnsatisfiedLinkError(error);
1017 }
1018 return;
1019 }
1020
1021 String filename = System.mapLibraryName(libraryName);
1022 List<String> candidates = new ArrayList<String>();
1023 String lastError = null;
1024 for (String directory : getLibPaths()) {
1025 String candidate = directory + filename;
1026 candidates.add(candidate);
1027
1028 if (IoUtils.canOpenReadOnly(candidate)) {
1029 String error = nativeLoad(candidate, loader);
1030 if (error == null) {
1031 return; // We successfully loaded the library. Job done.
1032 }
1033 lastError = error;
1034 }
1035 }
loadLibrary0(classLoader, libname)——>String error = nativeLoad(filename, loader)——>JVM_NativeLoad
部分源码:
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
324 jstring javaFilename,
325 jobject javaLoader) {
326 ScopedUtfChars filename(env, javaFilename);
327 if (filename.c_str() == NULL) {
328 return NULL;
329 }
330
331 std::string error_msg;
332 {
333 art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
334 bool success = vm->LoadNativeLibrary(env,
335 filename.c_str(),
336 javaLoader,
337 &error_msg);
338 if (success) {
339 return nullptr;
340 }
341 }
JVM_NativeLoad ——> LoadNativeLibrary——>OpenNativeLibrary(这里开始dlopen和dlsym)
957 const char* path_str = path.empty() ? nullptr : path.c_str();
958 bool needs_native_bridge = false;
959 void* handle = android::OpenNativeLibrary(env,//从这个函数开始加载dlopen
960 runtime_->GetTargetSdkVersion(),
961 path_str,
962 class_loader,
963 library_path.get(),
964 &needs_native_bridge,
965 error_msg);
这里之后直接去通过dlopen加载so路径去实现so文件的载入了,同时这里加载成功之后就要进行init以及init_array了
JNI_load函数
这里是loadLibrary0之后的代码,也就是说JNI_load也是在so文件加载之后init,init_array初始化之后进行的JNI_load的加载
1001 if (!created_library) {
1002 LOG(INFO) << "WOW: we lost a race to add shared library: "
1003 << "\"" << path << "\" ClassLoader=" << class_loader;
1004 return library->CheckOnLoadResult();
1005 }
1006 VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
1007
1008 bool was_successful = false;
1009 void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
1010 if (sym == nullptr) {
1011 VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
1012 was_successful = true;
1013 }
同时这里的JNI_load是通过dlsym来加载得到的
154void* Agent::FindSymbol(const std::string& name) const {
155 CHECK(dlopen_handle_ != nullptr) << "Cannot find symbols in an unloaded agent library " << this;
156 return dlsym(dlopen_handle_, name.c_str());
157}
function dlopen()
{
init()
init_array()
};
dlsym(JNI_load)
通过这里,我们知道了:要是我们要HOOK init和init_array 需要在dlopen之后,也就是so文件加载之后,但是要在JNI_load之前,这样才能确定我们上述的分析是合理的
所以我们要先利用HOOK dlopen来使得我们知道libxiaojianbang.so被加载了,之后再进行call_constructorsEv的HOOK(因为这里的位置正好是我们上述分析所说的位置)在这里HOOK Onentry的位置,进行HOOK
function main() {
function hook_dlopen(addr, soName, callback) {
Interceptor.attach(addr, {
onEnter: function (args) {
var soPath = args[0].readCString();
if(soPath.indexOf(soName) != -1) callback();
}, onLeave: function (retval) {
}
});
}
var dlopen = Module.findExportByName("libdl.so", "dlopen");
var android_dlopen_ext = Module.findExportByName("libdl.so", "android_dlopen_ext");
hook_dlopen(dlopen, "libxiaojianbang.so", hook_call_constructors);
hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", hook_call_constructors);
//---------------------------------------------------------------------------这里判断是我们的libxiaojianbang.so进行的dlopen操作
var isHooked = false;
function hook_call_constructors() {
var symbols = Process.getModuleByName("linker64").enumerateSymbols();
var call_constructors_addr = null;
for (let i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
if(symbol.name.indexOf("__dl__ZN6soinfo17call_constructorsEv") != -1){
call_constructors_addr = symbol.address;
}
}
console.log("call_constructors_addr: ", call_constructors_addr);
Interceptor.attach(call_constructors_addr, {
onEnter: function (args) {
if(!isHooked) {
hook_initarray();
isHooked = true;
}
}, onLeave: function (retval) {
}
});
}
//-----------------------------------------------------------这里以上是匹配我们的init_array的位置
function hook_initarray(){
var xiaojianbangAddr = Module.findBaseAddress("libxiaojianbang.so");
var func1_addr = xiaojianbangAddr.add(0x1C54);
var func2_addr = xiaojianbangAddr.add(0x1C7C);
var func3_addr = xiaojianbangAddr.add(0x1C2C);
Interceptor.replace(func1_addr, new NativeCallback(function () {
console.log("func1 is replaced!!!");
}, 'void', []));
Interceptor.replace(func2_addr, new NativeCallback(function () {
console.log("func2 is replaced!!!");
}, 'void', []));
Interceptor.replace(func3_addr, new NativeCallback(function () {
console.log("func3 is replaced!!!");
}, 'void', []));
}
}
//----------------------------------------------------------------------这里就直接HOOK了
main();
JNI_Onload的HOOK
我们的JNI_OnLoad也需要通过我们的dlopen来进行HOOK,因为由于我们直接启用就直接HOOK,可能由于so没有被加载,而导致可能并没有HOOK到,假如以附加的形式来进行HOOK,也可能会该JNI_onLoad已经完成了。
var dlopen = Module.findExportByName("libdl.so", "dlopen");
var android_dlopen_ext = Module.findExportByName("libdl.so", "android_dlopen_ext");
Hook_dlopen(dlopen, "libxiaojianbang.so", HOOK_JNIonLoad);
Hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", HOOK_JNIonLoad);
function HOOK_JNIonLoad()
{
var module = Module.getBaseAddress("libxiaojianbang.so");
var addr = module.add(0x1ccc);
Interceptor.replace(addr,new NativeCallback(function(){
console.log("Native");
},"void",[]));
}
线程创建HOOK hook_pthread_create
我们可以去HOOK 创建子线程的一些函数,因为在检测过程中,可能是通过开启子线程来进行检测过程的,所以我们通过HOOK创建线程的函数,可以使得我们得到一些有用的信息,同时由于pthread_create是系统函数,所以不用dlopen检测,直接进行HOOK就可以了
int pthread_create 函数
(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
function hook_pthread_create(){
var pthread_create_addr = Module.findExportByName("libc.so", "pthread_create");
console.log("pthread_create_addr: ", pthread_create_addr);
Interceptor.attach(pthread_create_addr,{
onEnter:function(args){ //args[2]是子线程的函数,假如是检测可以直接删线程
console.log(args[0], args[1], args[2], args[3]);
},onLeave:function(retval){
console.log("retval is =>",retval)
}
})