一次还原算法的另类途径
一般来说我们还原算法,就要老老实实的静态分析,动态分析,有反调试就国反调试。不过我现在发现,现在的反调试越来越不好过了,加上还有部分签名验证等乱七八糟的东西。就真的让人苦闷了。本次我们另辟蹊径,我们不过反调试和签名验证。直接把关键算法的库给他扒出来,在我们自己的应用中分析。你这回没辙了吧。
关键算法位置
这个比较容易,静态分析直接上,没啥好说的,直接上代码
int __fastcall Java_com_xxxxxxxx_generateCheckToken(int a1, int a2, int a3, int a4)
{
int v4; // r7
int v5; // r4
int v6; // r6
int result; // r0
int v8; // r5
int v9; // ST00_4
int v10; // [sp+4h] [bp-1Ch]
v10 = a4;
v4 = a3;
v5 = a1;
v6 = (*(int (**)(void))(*(_DWORD *)a1 + 676))();
result = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v5 + 676))(v5, v10, 0);
v8 = result;
if ( !v6 )
return 0;
if ( result )
{
v9 = generate_checktoken(v6, result);
(*(void (__fastcall **)(int, int, int))(*(_DWORD *)v5 + 680))(v5, v4, v6);
(*(void (__fastcall **)(int, int, int))(*(_DWORD *)v5 + 680))(v5, v10, v8);
result = (*(int (__fastcall **)(int, int))(*(_DWORD *)v5 + 668))(v5, v9);
}
return result;
}
中间过程
这里省略了我是如果寻找签名验证和反调试过程的,一句话总结,千万不要一股脑的静态分析。先要把思路理清,让我的程序吐出终端程序相关信息。
柳暗花明
正当我苦苦寻找绕过限制方法的时候,转念一向。既然关键算法都在so的库里,那我自己造个小程序来分析算法不久行了。干嘛非要在源程序里。于是有了一下的分析。
我们开始了工程,如图把动态库放到libs文件夹,这里我还造过jniLibs文件夹,在java目录下,但没有成功,所以就不说了。
app目录build.gradle改成这样
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.example.administrator.testcso"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
ndk{
moduleName "MathKit"
ldLibs "log"
abiFilters "armeabi","armeabi-v7a","x86"
}
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
sourceSets{
main{
jniLibs.srcDirs = ['libs']
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
gradle.properties添加一句
org.gradle.jvmargs=-Xmx1536m
android.useDeprecatedNdk=true
上面一句本来就有。
package com.sxxx.wxxxxx;
/**
* Created by Administrator on 2017/12/12.
*/
public class Wxxxxxxxxtion {
static {
System.loadLibrary("utility");
}
public native String generateCheckToken(String paramString1, String paramString2);
public String getchecktoken() {
String result = "";
return generateCheckToken("6140757656", "3C734D407E22A7B7DB0EBAFC8755D647A071CC80");
}
}
自己造个类如图,要符合ndk调用规则。
主类很简单,
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView calview;
Button calbutton;
calbutton=(Button)findViewById(R.id.bt_calc);
calview=(TextView)findViewById(R.id.tv_calc);
calbutton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
String str="点击事件";
WeiboApplication tokenob=new WeiboApplication();
str=tokenob.getchecktoken();
calview.setText(str);
}
});
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
添加成这样。
好编译,安装,成功。别急,还没完。
修改库文件的出事化函数
虽然似乎大功告成,但事实告诉我,我的程序在点击了按钮时间后,并没有成功运行。
这里是调试信息,经过这次我感悟到,凡是异常退出,必须要找类似这种信息,瞎猜根本没用。
我们看下:其实这里是因为加载不到动态库,我们的动态库函数还没调用,也就是肯定在jni_load函数开始的地方就发生了异常。
其实这里的异常信息并没有截取全面,因为是后来截取的:
.text:0000306C 20 1C MOVS R0, R4
.text:0000306E 79 44 ADD R1, PC ; "com/sina/weibo/WeiboApplication"
.text:00003070 2A 1C MOVS R2, R5
.text:00003072 06 23 MOVS R3, #6
.text:00003074 FF F7 CE FF BL registerWeiboNativeMethods
.text:00003078 00 28 CMP R0, #0
.text:0000307A 0B D0 BEQ loc_3094
.text:0000307C 0A 49 LDR R1, =(aComSinaWeiboDa - 0x3088)
.text:0000307E 2A 1C MOVS R2, R5
.text:00003080 48 32 ADDS R2, #0x48 ; 'H'
.text:00003082 20 1C MOVS R0, R4
.text:00003084 79 44 ADD R1, PC ; "com/sina/weibo/data/sp/EncryptSharedPre"...
.text:00003086 02 23 MOVS R3, #2
.text:00003088 FF F7 C4 FF BL registerWeiboNativeMethods
.text:0000308C 00 28 CMP R0, #0
.text:0000308E 01 D0
signed int __fastcall JNI_OnLoad(int a1, int a2, int a3)
{
int v3; // r4
signed int result; // r0
int v5; // [sp+4h] [bp-14h]
int v6; // [sp+8h] [bp-10h]
v6 = a3;
v5 = 0;
if ( !(*(int (**)(void))(*(_DWORD *)a1 + 24))()
&& (v3 = v5, registerWeiboNativeMethods(v5, "com/sina/weibo/WeiboApplication", off_9044, 6))
&& registerWeiboNativeMethods(v3, "com/sina/weibo/data/sp/EncryptSharedPreferences", off_908C, 2) )
{
result = 65540;
}
else
{
result = -1;
}
return result;
}
进过不断的实验,发现有两点可疑,函数的返回值。可能错误返回-1就没法加载了。
用010edit把这里改一下,其实这里分析了半天不知怎么改:
ext:00003080 48 32 ADDS R2, #0x48 ; 'H'
.text:00003082 20 1C MOVS R0, R4
.text:00003084 79 44 ADD R1, PC ; "com/sina/weibo/data/sp/EncryptSharedPre"...
.text:00003086 02 23 MOVS R3, #2
.text:00003088 FF F7 C4 FF BL registerWeiboNativeMethods
.text:0000308C 00 28 CMP R0, #0
.text:0000308E 01 D0 BEQ loc_3094
.text:00003090 02 48 LDR R0, =0x10004
.text:00003092 01 E0 B locret_3098
.text:00003094 ; ---------------------------------------------------------------------------
.text:00003094
.text:00003094 loc_3094 ; CODE XREF: JNI_OnLoad+12↑j
.text:00003094 ; JNI_OnLoad+2A↑j ...
.text:00003094 01 20 MOVS R0, #1
.text:00003096 40 42 NEGS R0, R0
.text:00003098
.text:00003098 locret_3098 ; CODE XREF: JNI_OnLoad+42↑j
.text:00003098 3E BD POP {R1-R5,PC}
.text:00003098 ; End of function JNI_OnLoad
这个函数错误会往下跳到loc_3094,如何才能不让它执行。把他改成movs r0,#1改成ldr r0,=0x10004
事实证明,汇编不是我想怎么改都可以的,这里的代码很难直接改成其他复制语句,偶然发现0000连续4个零,是mov r0,r0
来点暴力的,把所有B loc_3094 都改成mov r0,r0;
哈哈。保存。
运行,哈市不行。这次的提示不贴了,但大致是说找不到库com.sixx.xxxx.wxxxxxxxplication里面的scalcutes函数,这是啥函数,我保证我没掉过。后来发现这个函数是jni_load里面的registerWeiboNativeMethods(v5, “com/sina/weibo/WeiboApplication”, off_9044, 6)里调用的,而这个off_9044就是这个函数的名字,反编译没直接替换过来。
带着很多的无望,我把调用registerWeiboNativeMethods的函数全部,全部换成mov r0,r0
经过更改,最终编程了这样
signed int __fastcall JNI_OnLoad(int a1, int a2, int a3)
{
int v4; // [sp+4h] [bp-14h]
int v5; // [sp+8h] [bp-10h]
v5 = a3;
v4 = 0;
(*(void (**)(void))(*(_DWORD *)a1 + 24))();
return 65540;
}
010保存一下,放到libs下每个文件夹里,ok.
重编译,成功。算法运行。