某司面试三题
0x00 前言前一段时间,向某公司投了简历,我投的不是android逆向,但是我的简历却被转发,于是我只能硬着头皮上了。之前只是零星学过两个月的java,因为当时是大三到大四那个暑假过度期学的,我们那时已经开始找工作了,考研的也奔向自己心仪的学校面试什么的去了,所以很多人都缺勤,我也一样,没办法。因为对java的认识不够深,一直停留在源代码都被反编译出来了,也就无所谓逆向的观念中,而且,说真的,我并不是很喜欢java,感觉它好冗余……但是接触了这3道题之后,观点还是有所改变的,所以才决定抛砖引玉,把我的一些愚见写出来,向大家学习一下!
0x01 smali爆破趁着AVD还在启动中,这里先吐槽一下BlueStacks,妹子的,被这模拟器给坑惨了,先是没有root,然后又是跟ddms各种不配合,搞的我有些问题憋了两三天可能都憋不出来,然后就是android SDK,换了好多个版本,才运行了起来,之前就是一直黑屏,有一个“android”字样在主屏闪动,一直进不到主界面……当然,还有其他很多问题,这里就不多说了,说多都是泪……
在apkIDE中,把apk反编译后,得到了smali代码,其实3道题,都没有对dex进行加密,所以很容易得到smali代码,但是,这题有一个函数出现了error,就是没法解析,如图1:
图1.关键函数反编译错误
Q1.这里到底用什么方式,让此函数反编译错误?
好,先不纠结这一环,来看看MainActivity.class的onCreate()函数,扫描一下,他是如何才能走向成功(感觉这题,很多都是一目了然,但是就是没法做到完美),上代码:
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130903064);
this.meditText =((EditText)findViewById(2131034173));
this.mButton =((Button)findViewById(2131034174));
this.mButton.setOnClickListener(newView.OnClickListener()
{
public void onClick(ViewparamAnonymousView)
{
String str1 =MainActivity.this.meditText.getText().toString();
String str3 =MainActivity.this.cccccc();
String str2 =MainActivity.this.gagagfffafa();
paramAnonymousView = null;
try
{
str1 =MainActivity.bytesToAliSmsCode(str3, str1.getBytes("utf-8"));
paramAnonymousView = str1;
}
catch (UnsupportedEncodingExceptionlocalUnsupportedEncodingException)
{
for (;;)
{
localUnsupportedEncodingException.printStackTrace();
}
Toast.makeText(MainActivity.this,"恭喜你!登录成功", 0).show();
}
if ((str2 == null) ||(str2.equals("")) || (!str2.equals(paramAnonymousView)))
{
Toast.makeText(MainActivity.this,"密码错误!登录失败", 0).show();
return;
}
}
});
}
验证放在异常中,而不是在if判断,确实让人耳目一新,把思路整理一下:
1.str1 = MainActivity.bytesToAliSmsCode(str3,str1.getBytes("utf-8"));这里我们知道,只要计算出str1就ok了2.但是他要和str3做相关计算,而String str3 = MainActivity.this.cccccc();,cccccc()就是这个没有反编译的出来的函数就是关键了
3.if ((str2 == null) ||(str2.equals("")) || (!str2.equals(paramAnonymousView))),看到这句代码,验证好像是双线的,就是说String str2 = MainActivity.this.gagagfffafa();通过gagagfffafa()计算出str2也可以登陆成功!
接着,我们来看看如何爆破了,关键地方是line 41,如图2:
图2.爆破关键点
Btw,52pojie论坛里好像都没有smali语法的汇总,这里粘贴一个:http://www.blogjava.net/midea0978/archive/2012/01/04/367847.html,虽然是英文,但是汇总得还是很好的,改完以后,编译生成apk,然后再安装,试试效果:
图3.爆破成功
0x02 smali注入在非虫的《Android软件安全与逆向分析》曾提到smali注入,就是一个程序可以通过Log()打印出真正的注册码,我们这里也来尝试一下,通过图2的注释,已经告诉大家,v1就是真正的注册码,那么我们只需要打印出v1的值,也就得到注册码了,好,看下面的语句: const-stringv5, "SN" invoke-static{v5, v1}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
把上面两句添加进MainActivity$1.smali中,如图4:
图4.添加注入代码
但是,我遇到了问题,起初,我以为是gb2312格式编码的问题,然后我换成了utf-8,如图5:
图5.改变格式编码为utf-8
重新编译并且安装,测试效果如图6:
图6.乱码
Q2.smali注入后,为什么是乱码?
0x03 java keygenOK,这道题,其实要求写注册机的,开头也说了,我只零星学过两个月java,所以……注册机我写了,但是没编译通过,这里也把我的代码贴上,不怕献丑,只想求教!importjava.io.BufferedReader;
importjava.io.ByteArrayInputStream;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.InputStreamReader;
importjava.io.UnsupportedEncodingException;
importjavax.annotation.Resources;
importjavax.naming.Context;
//importandroid.content.Context;
publicclass Keygen
{
public static Context context;
public static void main(String[] args)throws UnsupportedEncodingException
{
// TODO Auto-generated method stub
String one = cccccc(); //略显蛋疼
String two = "Terrorblade";
String result = null;
result = bytesToAliSmsCode(one,two.getBytes("utf-8"));
System.out.println(result);
}
public static StringbytesToAliSmsCode(String paramString, byte[] paramArrayOfByte)
{
StringBuilder localStringBuilder = newStringBuilder();
int i = 0;
for (;;)
{
if (i >= paramArrayOfByte.length)
{
return localStringBuilder.toString();
}
//直接复制,却有下面的异常!
//Exception in thread"main" java.lang.NullPointerException
localStringBuilder.append(paramString.charAt(paramArrayOfByte& 0xFF));
i += 1;
}
}
protected static String cccccc()
{
int v3 = 0x0;
//Resourcs p0 = getResources(); //这里不知道怎么回事
String v11 = "abcdefghddddd";
try
{
//InputStream is =context.getResources().getAssets().open(v11);//getResources()什么鸡吧?前面得有context!
InputStream is = newByteArrayInputStream(v11.getBytes());
v3 = is.available();
byte[] v1 = new byte ;
is.read(v1, 0, v3);
byte[] v2 = new byte;
//java.lang.ArrayIndexOutOfBoundsException
System.arraycopy(v1, 0x15d81, v2,0x0, 0x300);//89473, 768
//下面这些实在不懂什么意思了
//new-instance v7,Ljava/lang/String; #新实例
// invoke-direct {v7, v2, v10},Ljava/lang/String;-><init>([BLjava/lang/String;)V
//遂强制转换
String v7 = new String(v2,"utf-8");
return v7;
}
catch (Exception e)
{
e.printStackTrace();
}
//try 取自http://www.cnblogs.com/greatverve/archive/2012/03/08/android-assets.html
return null;
}
}
Q3.第48行,getResources()到底是怎么用的?
Q4. 直接贴出来,数组越界&空指针?
java.lang.ArrayIndexOutOfBoundsException at java.lang.System.arraycopy(Native Method)
at Keygen.cccccc(Keygen.java:60) at Keygen.main(Keygen.java:19)
Exceptionin thread "main" java.lang.NullPointerException at Keygen.bytesToAliSmsCode(Keygen.java:39)
at Keygen.main(Keygen.java:22)
Q5.注册机可以参照gagagfffafa()的,开头也说了,可能是双线认证,我试过照着gagagfffafa()写,麻烦也不小,那gagagfffafa()是否也是算法函数呢?
0x04 后序第一篇,感觉有点虎头蛇尾,不过,精彩的永远在后面,请大家继续支持!
传送门:
某司面试题二:so调试 & 汉诺塔:
http://www.52pojie.cn/thread-403063-1-1.html
某司面试题三:fork & ptrace:
http://www.52pojie.cn/thread-403089-1-1.html
本题所有文件:
某司面试题二:so调试 & 汉诺塔
本帖最后由 Terrorblade 于 2015-8-20 01:10 编辑0x00 前言终于摆脱了冗余的java,来到了与ARM汇编正面交锋的第二题,验证代码在libclacSn.so文件的Java_com_ucweb_crackme140522_MainActivity_clacSnFuntion中,这里不得不说一句,计算的英文单词是calculate,能把calc写成clac,还有就是Function,这也能写成Funtion ,一个大公司,如此不严谨,看着也是有点醉了……
0x01 so调试前奏本来这部分不应该赘述的,网上已经有很多了,我再说,也觉得是浪费我自己的时间,我当时看的是乌云知识库的一篇文章,还有就是52pojie的http://www.52pojie.cn/thread-293648-1-1.html,但是这两篇文章,有些细节都没有说清楚,至少,我在参照这两篇文章调戏cm2的时候,还是遇到不少问题的,另外,为了整个系列的完整性,最终,还是决定把so调试的过程加入文章!
1.以调试模式启动程序,命令:amstart -D -n com.ucweb.crackme140522/.MainActivity
图1.启动程序
2.启动android_server,这个文件在ida的dbgsrv文件夹中,需要上传到avd中,这里不多说,这些细节那两篇绝对说过的,命令:/data/local/tmp/android_server
图2.已经在监听23946端口了
3.启动ddms,就是sdk目录中的ddms.bat
图3.启动ddms
4.端口转发,另外启动一个cmd,执行命令:
图4.端口转发
5.此时可以启动ida了,debugger->attach ->remote arm linux/android debugger, 然后:
图5. 勾上3项
6.选中要调试的进程后,千万记得在debugger->debugger options中再次选中,那3项:
图6.再次选中3项
7.回到cmd,输入jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700,回车
8.然后在ida中F9,接着就是选择本地文件映射:
图7.选择映射
9.计算出Java_com_ucweb_crackme140522_MainActivity_clacSnFuntion的地址,F2,再F9运行,期间会出现一个不相关的so文件,cancel就是了
0x02 算法因为已经调试过了,所以我站在一个已知者角度调试,不多废话,看到起始处,F7进去0000267C018 BL loc_21B4 ; 起始处
然后直接F4到loc_23E8:libclacSn.so:AD9F83E8loc_AD9F83E8 ; CODE XREF:libclacSn.so:JNI_OnLoad+598j
libclacSn.so:AD9F83E8LDRB R2, ; imei
libclacSn.so:AD9F83ECLDRB R1, ; un,username
libclacSn.so:AD9F83F0EOR R2, R1, R2 ; 逻辑异或
libclacSn.so:AD9F83F4STRB R2, ; 计算结果,保存在R6中
libclacSn.so:AD9F83F8ADD R3, R3, #1 ; Rd = Op1 + Op2
libclacSn.so:AD9F83FCCMP R3, R4 ; R4为un长度
libclacSn.so:AD9F8400BNE loc_AD9F83E8 ; Branch
F4到loc_2404,此时先看看R6的计算结果::B89C69D8DCB 0x64 ; d
:B89C69D9DCB 0x55 ; U
:B89C69DADCB 0x42 ; B
:B89C69DBDCB 0x42 ; B
:B89C69DCDCB 0x5F ; _
:B89C69DDDCB 0x42 ; B
:B89C69DEDCB 0x52 ; R
:B89C69DFDCB 0x5C ; \
:B89C69E0DCB 0x51 ; Q
:B89C69E1DCB 0x54 ; T
:B89C69E2DCB 0x55 ; U
:B89C69E3DCB 0x30 ; 0
:B89C69E4DCB 0x30 ; 0
:B89C69E5DCB 0x30 ; 0
:B89C69E6DCB 0x30 ; 0这里只有15位,后面会有0x80补全16位!
看loc_2404代码:libclacSn.so:AD9F8404loc_AD9F8404 ;CODE XREF: libclacSn.so:JNI_OnLoad+578j
libclacSn.so:AD9F8404MOV R0, R9 ; Rd = Op2
libclacSn.so:AD9F8408BLX R11 ; Branch with Link andExchange (register indirect)
libclacSn.so:AD9F840CLDR R3, ; Load from Memory
libclacSn.so:AD9F8410LDR R0, =0x66666667 ; Load from Memory
libclacSn.so:AD9F8414ADD R4, SP, #0x1AC ; Rd = Op1 + Op2
libclacSn.so:AD9F8418MOV R2, R3,ASR#31 ; Rd = Op2
libclacSn.so:AD9F841CSMULL R12, R1, R0, R3 ; Signed Multiply long
libclacSn.so:AD9F8420LDR R0, ; Load from Memory
libclacSn.so:AD9F8424RSB R2, R2, R1,ASR#3 ; Rd = Op2 - Op1
libclacSn.so:AD9F8428LDR R1, ; Load from Memory
libclacSn.so:AD9F842CMOV R3, #0 ; Rd = Op2
libclacSn.so:AD9F8430ADD R12, R4, #4 ; Rd = Op1 + Op2
libclacSn.so:AD9F8434STR R3, ,#4 ; Store to Memory
libclacSn.so:AD9F8438STR R3, ,#4 ; Store to Memory
libclacSn.so:AD9F843CEOR LR, R2, R1 ; Rd = Op1 ^ Op2
libclacSn.so:AD9F8440EOR LR, R0, LR ; Rd = Op1 ^ Op2
libclacSn.so:AD9F8444STR R3, ,#4 ; Store to Memory
libclacSn.so:AD9F8448STR LR, ; Store to Memory
libclacSn.so:AD9F844CSTR R3, ; Store to Memory
libclacSn.so:AD9F8450LDR R12, =0x67452301 ; Load from Memory
libclacSn.so:AD9F8454ADD R5, SP, #0x154 ; Rd = Op1 + Op2
libclacSn.so:AD9F8458LDR R2, ; Load from Memory
libclacSn.so:AD9F845CSTR R12, ; Store to Memory
libclacSn.so:AD9F8460ADD R12, R12,#0x88000000 ; Rd = Op1 + Op2
libclacSn.so:AD9F8464ADD R12, R12, #0x880000 ; Rd = Op1 + Op2
libclacSn.so:AD9F8468ADD R12, R12, #0x8800 ; Rd = Op1 + Op2
libclacSn.so:AD9F846CADD R12, R12, #0x88 ; Rd = Op1 + Op2
libclacSn.so:AD9F8470STR R12, ; Store to Memory
libclacSn.so:AD9F8474LDR R12, =0x98BADCFE ; Load from Memory
libclacSn.so:AD9F8478MOV R0, R5 ; Rd = Op2
libclacSn.so:AD9F847CMOV R1, R6 ; Rd = Op2
libclacSn.so:AD9F8480STR R12, ; Store to Memory
libclacSn.so:AD9F8484LDR R12, =0x10325476 ; Load from Memory
libclacSn.so:AD9F8488ADD R10, SP, #0x1D4 ; Rd = Op1 + Op2
libclacSn.so:AD9F848CSTR R3, ; Store to Memory
libclacSn.so:AD9F8490STR R12, ; Store to Memory
libclacSn.so:AD9F8494STR R3, ; Store to Memory
libclacSn.so:AD9F8498STR R3, ; Store to Memory
libclacSn.so:AD9F849CBL unk_AD9F7B78 ; 通过上面的计算,得到R5,虽说是通过计算,但是R5的内容是固定的:
libclacSn.so:AD9F849C ; :BEE8D42CDCB 1
libclacSn.so:AD9F849C ;:BEE8D42D DCB 0x23 ; #
libclacSn.so:AD9F849C ;:BEE8D42E DCB 0x45 ; E
libclacSn.so:AD9F849C ;:BEE8D42F DCB 0x67 ; g
libclacSn.so:AD9F849C ;:BEE8D430 DCB 0x89 ;
libclacSn.so:AD9F849C ;:BEE8D431 DCB 0xAB ;
libclacSn.so:AD9F849C ;:BEE8D432 DCB 0xCD ;
libclacSn.so:AD9F849C ;:BEE8D433 DCB 0xEF ;
libclacSn.so:AD9F849C ; :BEE8D434DCB 0xFE ;
libclacSn.so:AD9F849C ;:BEE8D435 DCB 0xDC ;
libclacSn.so:AD9F849C ;:BEE8D436 DCB 0xBA ;
libclacSn.so:AD9F849C ; :BEE8D437 DCB 0x98 ;
libclacSn.so:AD9F849C ;:BEE8D438 DCB 0x76 ; v
libclacSn.so:AD9F849C ;:BEE8D439 DCB 0x54 ; T
libclacSn.so:AD9F849C ;:BEE8D43A DCB 0x32 ; 2
libclacSn.so:AD9F849C ;:BEE8D43B DCB 0x10
libclacSn.so:AD9F849C ; 一个byte数组的逆序
libclacSn.so:AD9F84A0MOV R0, R10 ; Rd = Op2
libclacSn.so:AD9F84A4ADD R1, R5, #0x10 ; Rd = Op1 + Op2
libclacSn.so:AD9F84A8MOV R2, #8 ; Rd = Op2
libclacSn.so:AD9F84ACBL unk_AD9F7C64 ; Branch with Link
libclacSn.so:AD9F84B0LDR R2, ; Load from Memory
libclacSn.so:AD9F84B4LDR R3, =0x4A70 ; Load from Memory
libclacSn.so:AD9F84B8MOV R0, R5 ; Rd = Op2
libclacSn.so:AD9F84BCMOV R2, R2,LSR#3 ; Rd = Op2
libclacSn.so:AD9F84C0AND R2, R2, #0x3F ; Rd = Op1 & Op2
libclacSn.so:AD9F84C4ADD R3, PC, R3 ; Rd = Op1 + Op2
libclacSn.so:AD9F84C8CMP R2, #0x37 ; Set cond. codes on Op1 - Op2
libclacSn.so:AD9F84CCSUB R3, R3, #0xFF0 ; Rd = Op1 - Op2
libclacSn.so:AD9F84D0SUB R1, R3, #8 ; Rd = Op1 - Op2
libclacSn.so:AD9F84D4RSBLS R2, R2, #0x38 ; Rd = Op2 - Op1
libclacSn.so:AD9F84D8RSBHI R2, R2, #0x78 ; Rd = Op2 - Op1
libclacSn.so:AD9F84DCBL unk_AD9F7B78 ; Branch with Link
libclacSn.so:AD9F84E0MOV R0, R5 ; Rd = Op2
libclacSn.so:AD9F84E4MOV R1, R10 ; Rd = Op2
libclacSn.so:AD9F84E8MOV R2, #8 ; Rd = Op2
libclacSn.so:AD9F84ECBL unk_AD9F7B78 ; 这里R5发生改变,F7一探究竟
libclacSn.so:AD9F84F0MOV R2, #0x10 ; Rd = Op2
libclacSn.so:AD9F84F4MOV R0, R4 ; Rd = Op2
libclacSn.so:AD9F84F8MOV R1, R5 ; Rd = Op2
libclacSn.so:AD9F84FCBL unk_AD9F7C64 ; R5的内容复制给R4
libclacSn.so:AD9F8500ADD R2, R5, #0x58 ; Rd = Op1 + Op2
libclacSn.so:AD9F8504MOV R3, #0 ; Rd = Op2
F7进入loc_1B78之后,直接F4来到loc_1BF0,这里是算法所在:libclacSn.so:AD9F712CLDRB R7, ; iu的第3位,iu即imei^un
libclacSn.so:AD9F7130LDRB R8, ; iu第2位
libclacSn.so:AD9F7134LDRB R6, ; iu的第1位
libclacSn.so:AD9F7138LDRB R5, ; iu第4位
libclacSn.so:AD9F713CMOV R7, R7,LSL#16 ; lsl为逻辑左移
libclacSn.so:AD9F7140ORR R7, R7, R8,LSL#8 ; Rd = Op1 | Op2
libclacSn.so:AD9F7144ORR R6, R7, R6 ; Rd = Op1 | Op2
libclacSn.so:AD9F7148ADD R1, R1, #4 ; Rd = Op1 + Op2
libclacSn.so:AD9F714CORR R5, R6, R5,LSL#24 ; Rd = Op1 | Op2
libclacSn.so:AD9F7150CMP R1, R10 ; 16位比较完为止
libclacSn.so:AD9F7154STR R5, ,#4 ; Store to Memory
libclacSn.so:AD9F7158BNE loc_AD9F712C ; Branch
libclacSn.so:AD9F715CLDR R7, ; iu前四位
libclacSn.so:AD9F7160LDR R6, ; 16byte的后四位,即76,54,32,10
libclacSn.so:AD9F7164LDR R1, =0xD76AA478 ; Load from Memory
libclacSn.so:AD9F7168LDR R8, ; iu后四位
libclacSn.so:AD9F716CBIC R5, R6, R3 ; Rd = Op1 & ~Op2
libclacSn.so:AD9F7170AND R4, R2, R3 ; Rd = Op1 & Op2
libclacSn.so:AD9F7174ADD R1, R7, R1 ; Rd = Op1 + Op2
libclacSn.so:AD9F7178ADD R1, R1, R8 ; Rd = Op1 + Op2
libclacSn.so:AD9F717CLDR R9, ; iu的四分之二
libclacSn.so:AD9F7180ORR R4, R5, R4 ; Rd = Op1 | Op2
libclacSn.so:AD9F7184LDR R5, =0xE8C7B756 ; Load from Memory
libclacSn.so:AD9F7188LDR R10, ; Load from Memory
libclacSn.so:AD9F718CADD R4, R1, R4 ; 16byte的后4位
libclacSn.so:AD9F7190ADD R4, R3, R4,ROR#25 ; Rd = Op1 + Op2
libclacSn.so:AD9F7194BIC R6, R2, R4 ; Rd = Op1 & ~Op2
libclacSn.so:AD9F7198AND R1, R4, R3 ; Rd = Op1 & Op2
libclacSn.so:AD9F719CADD R5, R9, R5 ; Rd = Op1 + Op2
libclacSn.so:AD9F71A0ADD R5, R5, R10 ; Rd = Op1 + Op2
libclacSn.so:AD9F71A4LDR R11, ; iu的四分之三
libclacSn.so:AD9F71A8ORR R1, R6, R1 ; Rd = Op1 | Op2
libclacSn.so:AD9F71ACLDR R8, =0x242070DB ; Load from Memory
libclacSn.so:AD9F71B0ADD R1, R5, R1 ; Rd = Op1 + Op2
libclacSn.so:AD9F71B4ADD R1, R4, R1,ROR#20 ; Rd = Op1 + Op2
libclacSn.so:AD9F71B8AND R5, R1, R4 ; Rd = Op1 & Op2
libclacSn.so:AD9F71BCBIC R6, R3, R1 ; Rd = Op1 & ~Op2
libclacSn.so:AD9F71C0ADD R8, R11, R8 ; Rd = Op1 + Op2
libclacSn.so:AD9F71C4ADD R8, R8, R2 ; Rd = Op1 + Op2
libclacSn.so:AD9F71C8ORR R6, R6, R5 ; Rd = Op1 | Op2
libclacSn.so:AD9F71CCADD R6, R8, R6 ; Rd = Op1 + Op2
libclacSn.so:AD9F71D0LDR R5, =0xC1BDCEEE ; Load from Memory
libclacSn.so:AD9F71D4LDR R8, ; iu后4位
.
.
.
中间是个汉诺塔计算过程,最终计算结果会保存在r6,r2,r3,r4,所以去到下面代码,待会再回头分析这里
.
.
.
libclacSn.so:AD9F7A80STR R6, ; Store to Memory
libclacSn.so:AD9F7A84STR R3, ; Store to Memory
libclacSn.so:AD9F7A88STR R2, ; Store to Memory
libclacSn.so:AD9F7A8CSTR R4, ; Store to Memory
libclacSn.so:AD9F7A90ADD R2, R12, #0x40 ; Rd = Op1 + Op2
libclacSn.so:AD9F7A94MOV R3, #0 ; Rd = Op2
存储后,R0的内容: :BEE8D454DCB 0xB2 ;
:BEE8D455DCB 0x52 ; R
:BEE8D456DCB 0x7A ; z
:BEE8D457DCB 0x16
:BEE8D458DCB 0
:BEE8D459DCB 0xD5 ;
:BEE8D45ADCB 0x8A ;
:BEE8D45BDCB 0x99 ;
:BEE8D45CDCB 0x5F ; _
:BEE8D45DDCB 0x21 ; !
:BEE8D45EDCB 0x1C
:BEE8D45FDCB 0x96 ;
:BEE8D460DCB 0xA2 ;
:BEE8D461DCB 0x48 ; H
:BEE8D462DCB 0xB8 ;
:BEE8D463DCB 0x3C ; <
跳出算法的关键部分以后,后续还有一些运算:libclacSn.so:AD9F851Cloc_AD9F851C ;CODE XREF: libclacSn.so:JNI_OnLoad+6D0j
libclacSn.so:AD9F851CLDRB R0, ; 注册码第一运算
libclacSn.so:AD9F8520ADD R1, R2, #1 ; Rd = Op1 + Op2
libclacSn.so:AD9F8524EOR R2, R2, R0 ; Rd = Op1 ^ Op2
libclacSn.so:AD9F8528STRB R2, ; 结果存储R7中
libclacSn.so:AD9F852CADD R3, R3, #1 ; Rd = Op1 + Op2
libclacSn.so:AD9F8530CMP R3, #0x10 ; 比较16次
libclacSn.so:AD9F8534AND R2, R1, #0xFF ; Rd = Op1 & Op2
libclacSn.so:AD9F8538BNE loc_AD9F851C ; Branch
libclacSn.so:AD9F853CNOP ;No Operation
libclacSn.so:AD9F8540LDR R10, =(unk_AD9F9C58 -0xAD9F8554) ; Load from Memory
libclacSn.so:AD9F8544LDR R5, ; Load from Memory
libclacSn.so:AD9F8548MOV R4, #0 ; Rd = Op2
libclacSn.so:AD9F854CADD R10, PC, R10 ;unk_AD9F9C58 ; Rd = Op1 + Op2
libclacSn.so:AD9F8550
libclacSn.so:AD9F8550loc_AD9F8550 ;CODE XREF: libclacSn.so:JNI_OnLoad+70Cj
libclacSn.so:AD9F8550LDRB R2, ; 注册码第二次运算
libclacSn.so:AD9F8554ADD R3, R4, #0x10 ; Rd = Op1 + Op2
libclacSn.so:AD9F8558MOV R0, R5 ; Rd = Op2
libclacSn.so:AD9F855CADD R4, R4, #1 ; Rd = Op1 + Op2
libclacSn.so:AD9F8560EOR R2, R2, R3 ; Rd = Op1 ^ Op2
libclacSn.so:AD9F8564MOV R1, R10 ; Rd = Op2
libclacSn.so:AD9F8568BL sprintf ; 将计算得出的16byte数据,直接拼接成string
libclacSn.so:AD9F856CCMP R4, #0x10 ; Set cond. codes on Op1 - Op2
libclacSn.so:AD9F8570ADD R5, R5, #2 ; Rd = Op1 + Op2
libclacSn.so:AD9F8574BNE loc_AD9F8550 ; Branch
libclacSn.so:AD9F8578MOV R0, R9 ; Rd = Op2
libclacSn.so:AD9F857CBLX R11 ; Branch with Link andExchange (register indirect)
libclacSn.so:AD9F8580LDR R1, ; Load from Memory
libclacSn.so:AD9F8584LDR R0, =0x66666667 ; Load from Memory
libclacSn.so:AD9F8588LDR R12, ; Load from Memory
libclacSn.so:AD9F858CMOV R3, R1,ASR#31 ; Rd = Op2
libclacSn.so:AD9F8590SMULL R7, R2, R0, R1 ; Signed Multiply long
libclacSn.so:AD9F8594MOV R0, R6 ; Rd = Op2
libclacSn.so:AD9F8598RSB R3, R3, R2,ASR#3 ;Rd = Op2 - Op1
libclacSn.so:AD9F859CLDR R2, ; Load from Memory
libclacSn.so:AD9F85A0EOR R3, R12, R3 ; Rd = Op1 ^ Op2
libclacSn.so:AD9F85A4EOR R3, R2, R3 ; Rd = Op1 ^ Op2
libclacSn.so:AD9F85A8STR R3, ; Store to Memory
libclacSn.so:AD9F85ACBL free ; Branch with Link
libclacSn.so:AD9F85B0LDR R0, ; Load from Memory
libclacSn.so:AD9F85B4LDR R1, ; Load from Memory
libclacSn.so:AD9F85B8MOV R2, R4 ; Rd = Op2
libclacSn.so:AD9F85BCBL memcmp ; 这里进行比较,R1存储着假码
libclacSn.so:AD9F85C0CMP R0, #0 ; Set cond. codes on Op1 -Op2
libclacSn.so:AD9F85C4BNE loc_AD9F8340 ; Branch
libclacSn.so:AD9F85C8NOP ;No Operation
libclacSn.so:AD9F85CCMOV R0, #1 ; Rd = Op2
libclacSn.so:AD9F85D0B loc_AD9F8344 ; Branch
来看看loc_25B0中,R0的内容::BEE8D354DCB 0x42 ; B
:BEE8D355DCB 0x32 ; 2
:BEE8D356DCB 0x35 ; 5
:BEE8D357DCB 0x32 ; 2
:BEE8D358DCB 0x37 ; 7
:BEE8D359DCB 0x41 ; A
:BEE8D35ADCB 0x31 ; 1
:BEE8D35BDCB 0x36 ; 6
:BEE8D35CDCB 0x30 ; 0
:BEE8D35DDCB 0x30 ; 0
:BEE8D35EDCB 0x44 ; D
:BEE8D35FDCB 0x35 ; 5
:BEE8D360DCB 0x38 ; 8
:BEE8D361DCB 0x41 ; A
:BEE8D362DCB 0x39 ; 9
:BEE8D363DCB 0x39 ; 9
:BEE8D364DCB 0x35 ; 5
:BEE8D365DCB 0x46 ; F
:BEE8D366DCB 0x32 ; 2
:BEE8D367DCB 0x31 ; 1
:BEE8D368DCB 0x31 ; 1
:BEE8D369DCB 0x43 ; C
:BEE8D36ADCB 0x39 ; 9
:BEE8D36BDCB 0x36 ; 6
:BEE8D36CDCB 0x41 ; A
:BEE8D36DDCB 0x32 ; 2
:BEE8D36EDCB 0x34 ; 4
:BEE8D36FDCB 0x38 ; 8
:BEE8D370DCB 0x42 ; B
:BEE8D371DCB 0x38 ; 8
:BEE8D372DCB 0x33 ; 3
:BEE8D373DCB 0x43 ; C
至此,我们可以试一试,我们找到的注册码B2527A1600D58A995F211C96A248B83C,取前16位,验证结果如下:
图8.验证成功!
这里确实验证成功了,但是这个过程中会有彩蛋,只有自己走过一遍,看看能不能得到真正的注册码,才能知道彩蛋到底是什么!
最后,我们来说算法的关键部分sub_1100,先在ida中看看F5后的类C代码:int__fastcall sub_1100(int result, int a2)
{
int v2; // r2@1
int v3; // r3@1
int *v4; // r12@1
int v5; // r10@1
int *v6; // r4@1
int v7; // r5@2
int v8; // r6@2
int v9; // r4@3
int v10; // off@3
int v11; // r1@3
int v12; // off@3
int v13; // r6@3
int v14; // off@3
int v15; // r5@3
int v16; // off@3
int v17; // r4@3
int v18; // off@3
int v19; // r1@3
int v20; // off@3
int v21; // r6@3
int v22; // off@3
int v23; // r5@3
int v24; // off@3
int v25; // r4@3
int v26; // off@3
int v27; // r1@3
int v28; // off@3
int v29; // r6@3
int v30; // off@3
int v31; // r5@3
int v32; // off@3
int v33; // r4@3
int v34; // off@3
int v35; // r1@3
int v36; // off@3
int v37; // r10@3
int v38; // r6@3
int v39; // off@3
int v40; // r8@3
int v41; // r5@3
int v42; // off@3
int v43; // r8@3
int v44; // r10@3
int v45; // off@3
int v46; // r9@3
int v47; // off@3
int v48; // r8@3
int v49; // off@3
int v50; // r4@3
int v51; // off@3
int v52; // r1@3
int v53; // off@3
int v54; // r6@3
int v55; // off@3
int v56; // r5@3
int v57; // off@3
int v58; // r4@3
int v59; // off@3
int v60; // r1@3
int v61; // off@3
int v62; // r6@3
int v63; // off@3
int v64; // r5@3
int v65; // off@3
int v66; // r4@3
int v67; // off@3
int v68; // r1@3
int v69; // off@3
int v70; // r8@3
int v71; // off@3
int v72; // r6@3
int v73; // off@3
int v74; // r5@3
int v75; // off@3
int v76; // r4@3
int v77; // off@3
int v78; // r1@3
int v79; // off@3
int v80; // r6@3
int v81; // off@3
int v82; // r5@3
int v83; // off@3
int v84; // r4@3
int v85; // off@3
int v86; // r1@3
int v87; // off@3
int v88; // r6@3
int v89; // off@3
int v90; // r5@3
int v91; // off@3
int v92; // r4@3
int v93; // off@3
int v94; // r1@3
int v95; // off@3
int v96; // r6@3
int v97; // off@3
int v98; // r5@3
int v99; // off@3
int v100; // r4@3
int v101; // off@3
int v102; // r1@3
int v103; // off@3
int v104; // r6@3
int v105; // off@3
int v106; // r5@3
int v107; // off@3
int v108; // r4@3
int v109; // off@3
int v110; // r1@3
int v111; // off@3
int v112; // r6@3
int v113; // off@3
int v114; // r5@3
int v115; // off@3
int v116; // r4@3
int v117; // off@3
int v118; // r7@3
int v119; // off@3
int v120; // r1@3
int v121; // off@3
int v122; // r5@3
int v123; // off@3
int v124; // r4@3
int v125; // off@3
int v126; // r6@3
int v127; // off@3
int v128; // r1@3
int v129; // off@3
int v130; // r5@3
int v131; // off@3
int v132; // r4@3
int v133; // off@3
int v134; // r6@3
int v135; // off@3
int v136; // off@3
int v137; // r2@3
int v138; // r3@3
int v139; // off@3
int v140; // @1
int v141; // @1
int v142; // @1
int v143; // @3
int v144; // @3
int v145; // @3
int v146; // @3
int v147; // @3
int v148; // @3
int v149; // @3
int v150; // @3
int v151; // @3
int v152; // @3
int v153; // @3
int v154; // @3
int v155; // @3
int v156; // @3
int v157; // @3
int v158; // @4
v141 = *(_DWORD *)result; // R4
v2 = *(_DWORD *)(result + 8); // R2
v3 = *(_DWORD *)(result + 4); // R3
v4 = &v142;
v140 = *(_DWORD *)(result + 12); // R6
v5 = a2 + 64; // R2, R12,#0x40
v6 = &v142;
do
{
v7 = *(_BYTE *)(a2 + 3); // LDRB R5,
v8 = (*(_BYTE *)(a2 + 2) << 16) |(*(_BYTE *)(a2 + 1) << 8) | *(_BYTE *)a2;
a2 += 4;
*v6 = v8 | (v7 << 24); // ORR R5, R6, R5,LSL#24
++v6;
}
while ( a2 != v5 );
v10 = __ROR4__(v142 - 680876936 + v141 +(v140 & ~v3 | v2 & v3), 25);
v9 = v3 + v10;
v12 = __ROR4__(v143 - 389564586 + v140 + (v2& ~(v3 + v10) | (v3 + v10) & v3), 20);
v11 = v9 + v12;
v14 = __ROR4__(v144 + 606105819 + v2 + (v3& ~(v9 + v12) | (v9 + v12) & v9), 15);
v13 = v11 + v14;
v16 = __ROR4__(v145 - 1044525330 + v3 + (v9& ~(v11 + v14) | (v11 + v14) & v11), 10);
v15 = v13 + v16;
v18 = __ROR4__(v146 - 176418897 + v9 + (v11 &~(v13 + v16) | (v13 + v16) & v13), 25);
v17 = v15 + v18;
v20 = __ROR4__(v147 + 1200080426 + v11 + (v13& ~(v15 + v18) | (v15 + v18) & v15), 20);
v19 = v17 + v20;
v22 = __ROR4__(v148 - 1473231341 + v13 + (v15& ~(v17 + v20) | (v17 + v20) & v17), 15);
v21 = v19 + v22;
v24 = __ROR4__(v149 - 45705983 + v15 + (v17& ~(v19 + v22) | (v19 + v22) & v19), 10);
v23 = v21 + v24;
v26 = __ROR4__(v150 + 1770035416 + v17 + (v19& ~(v21 + v24) | (v21 + v24) & v21), 25);
v25 = v23 + v26;
v28 = __ROR4__(v151 - 1958414417 + v19 + (v21& ~(v23 + v26) | (v23 + v26) & v23), 20);
v27 = v25 + v28;
v30 = __ROR4__(v152 - 42063 + v21 + (v23& ~(v25 + v28) | (v25 + v28) & v25), 15);
v29 = v27 + v30;
v32 = __ROR4__(v153 - 1990404162 + v23 + (v25& ~(v27 + v30) | (v27 + v30) & v27), 10);
v31 = v29 + v32;
v34 = __ROR4__(v154 + 1804603682 + v25 + (v27& ~(v29 + v32) | (v29 + v32) & v29), 25);
v33 = v31 + v34;
v36 = __ROR4__(v155 - 40341101 + v27 + (v29& ~(v31 + v34) | (v31 + v34) & v31), 20);
v35 = v33 + v36;
v37 = ~(v33 + v36);
v39 = __ROR4__(v156 - 1502002290 + v29 + (v37& v31 | (v33 + v36) & v33), 15);
v38 = v35 + v39;
v40 = ~(v35 + v39);
v42 = __ROR4__(v157 + 1236535329 + v31 + (v40& v33 | (v35 + v39) & v35), 10);
v41 = v38 + v42;
v43 = (v38 + v42) & v40;
v45 = __ROR4__(v143 - 165796510 + v33 + ((v38+ v42) & v35 | v38 & v37), 27);
v44 = v41 + v45;
v47 = __ROR4__(v148 - 1069501632 + v35 +((v41 + v45) & v38 | v43), 23);
v46 = v44 + v47;
v49 = __ROR4__(v153 + 643717713 + v38 + ((v44+ v47) & v41 | v44 & ~v41), 18);
v48 = v46 + v49;
v51 = __ROR4__(v142 - 373897302 + v41 + ((v46+ v49) & v44 | v46 & ~v44), 12);
v50 = v48 + v51;
v53 = __ROR4__(v147 - 701558691 + v44 + ((v48+ v51) & v46 | v48 & ~v46), 27);
v52 = v50 + v53;
v55 = __ROR4__(v152 + 38016083 + v46 + ((v50+ v53) & v48 | v50 & ~v48), 23);
v54 = v52 + v55;
v57 = __ROR4__(v157 - 660478335 + v48 + ((v52+ v55) & v50 | v52 & ~v50), 18);
v56 = v54 + v57;
v59 = __ROR4__(v146 - 405537848 + v50 + ((v54+ v57) & v52 | v54 & ~v52), 12);
v58 = v56 + v59;
v61 = __ROR4__(v151 + 568446438 + v52 + ((v56+ v59) & v54 | v56 & ~v54), 27);
v60 = v58 + v61;
v63 = __ROR4__(v156 - 1019803690 + v54 +((v58 + v61) & v56 | v58 & ~v56), 23);
v62 = v60 + v63;
v65 = __ROR4__(v145 - 187363961 + v56 + ((v60+ v63) & v58 | v60 & ~v58), 18);
v64 = v62 + v65;
v67 = __ROR4__(v150 + 1163531501 + v58 +((v62 + v65) & v60 | v62 & ~v60), 12);
v66 = v64 + v67;
v69 = __ROR4__(v155 - 1444681467 + v60 +((v64 + v67) & v62 | v64 & ~v62), 27);
v68 = v66 + v69;
v71 = __ROR4__(v144 - 51403784 + v62 + ((v66+ v69) & v64 | v66 & ~v64), 23);
v70 = v68 + v71;
v73 = __ROR4__(v149 + 1735328473 + v64 +((v68 + v71) & v66 | v68 & ~v66), 18);
v72 = v70 + v73;
v75 = __ROR4__(v154 - 1926607734 + v66 +((v70 + v73) & v68 | v70 & ~v68), 12);
v74 = v72 + v75;
v77 = __ROR4__(v147 - 378558 + v68 + (v72 ^v70 ^ (v72 + v75)), 28);
v76 = v74 + v77;
v79 = __ROR4__(v150 - 2022574463 + v70 + (v74^ v72 ^ (v74 + v77)), 21);
v78 = v76 + v79;
v81 = __ROR4__(v153 + 1839030562 + v72 + (v76^ v74 ^ (v76 + v79)), 16);
v80 = v78 + v81;
v83 = __ROR4__(v156 - 35309556 + v74 + (v78 ^v76 ^ (v78 + v81)), 9);
v82 = v80 + v83;
v85 = __ROR4__(v143 - 1530992060 + v76 + (v80^ v78 ^ (v80 + v83)), 28);
v84 = v82 + v85;
v87 = __ROR4__(v146 + 1272893353 + v78 + (v82^ v80 ^ (v82 + v85)), 21);
v86 = v84 + v87;
v89 = __ROR4__(v149 - 155497632 + v80 + (v84^ v82 ^ (v84 + v87)), 16);
v88 = v86 + v89;
v91 = __ROR4__(v152 - 1094730640 + v82 + (v86^ v84 ^ (v86 + v89)), 9);
v90 = v88 + v91;
v93 = __ROR4__(v155 + 681279174 + v84 + (v88^ v86 ^ (v88 + v91)), 28);
v92 = v90 + v93;
v95 = __ROR4__(v142 - 358537222 + v86 + (v90^ v88 ^ (v90 + v93)), 21);
v94 = v92 + v95;
v97 = __ROR4__(v145 - 722521979 + v88 + (v92^ v90 ^ (v92 + v95)), 16);
v96 = v94 + v97;
v99 = __ROR4__(v148 + 76029189 + v90 + (v94 ^v92 ^ (v94 + v97)), 9);
v98 = v96 + v99;
v101 = __ROR4__(v151 - 640364487 + v92 + (v96^ v94 ^ (v96 + v99)), 28);
v100 = v98 + v101;
v103 = __ROR4__(v154 - 421815835 + v94 + (v98^ v96 ^ (v98 + v101)), 21);
v102 = v100 + v103;
v105 = __ROR4__(v157 + 530742520 + v96 +(v100 ^ v98 ^ (v100 + v103)), 16);
v104 = v102 + v105;
v107 = __ROR4__(v144 - 995338651 + v98 +(v102 ^ v100 ^ (v102 + v105)), 9);
v106 = v104 + v107;
v109 = __ROR4__(v142 - 198630844 + v100 +(((v104 + v107) | ~v102) ^ v104), 26);
v108 = v106 + v109;
v111 = __ROR4__(v149 + 1126891415 + v102 +(((v106 + v109) | ~v104) ^ v106), 22);
v110 = v108 + v111;
v113 = __ROR4__(v156 - 1416354905 + v104 +(((v108 + v111) | ~v106) ^ v108), 17);
v112 = v110 + v113;
v115 = __ROR4__(v147 - 57434055 + v106 +(((v110 + v113) | ~v108) ^ v110), 11);
v114 = v112 + v115;
v117 = __ROR4__(v154 + 1700485571 + v108 +(((v112 + v115) | ~v110) ^ v112), 26);
v116 = v114 + v117;
v119 = __ROR4__(v145 - 1894986606 + v110 + (((v114+ v117) | ~v112) ^ v114), 22);
v118 = v116 + v119;
v121 = __ROR4__(v152 - 1051523 + v112 +(((v116 + v119) | ~v114) ^ v116), 17);
v120 = v118 + v121;
v123 = __ROR4__(v143 - 2054922799 + v114 +(((v118 + v121) | ~v116) ^ v118), 11);
v122 = v120 + v123;
v125 = __ROR4__(v150 + 1873313359 + v116 +(((v120 + v123) | ~v118) ^ v120), 26);
v124 = v122 + v125;
v127 = __ROR4__(v157 - 30611744 + v118 +(((v122 + v125) | ~v120) ^ v122), 22);
v126 = v124 + v127;
v129 = __ROR4__(v148 - 1560198380 + v120 +(((v124 + v127) | ~v122) ^ v124), 17);
v128 = v126 + v129;
v131 = __ROR4__(v155 + 1309151649 + v122 +(((v126 + v129) | ~v124) ^ v126), 11);
v130 = v128 + v131;
v133 = __ROR4__(v146 - 145523070 + v124 +(((v128 + v131) | ~v126) ^ v128), 26);
v132 = v130 + v133;
v135 = __ROR4__(v153 - 1120210379 + v126 +(((v130 + v133) | ~v128) ^ v130), 22);
v134 = v132 + v135;
v136 = __ROR4__(v144 + 718787259 + v128 +(((v132 + v135) | ~v130) ^ v132), 17);
v137 = v2 + v134 + v136;
v138 = v134 + v136 + v3;
v139 = __ROR4__(v151 - 343485551 + v130 +(((v134 + v136) | ~v132) ^ v134), 11);
*(_DWORD *)(result + 12) = v140 + v134; // STR R6,
*(_DWORD *)(result + 4) = v138 + v139; // STR R3,
*(_DWORD *)(result + 8) = v137; // STR R2,
*(_DWORD *)result = v141 + v132; // STR R4,
do
{
*(_BYTE *)v4 = 0;
v4 = (int *)((char *)v4 + 1);
}
while ( v4 != &v158 );
return result;
}
算法不难,但是很冗余!!我们以计算R6的地址为例:R6 =v140 + v134 = *(_DWORD *)(result + 12) + v132 + v135 = *(_DWORD *)(result + 12) + v130 + v133 +__ROR4__(v153 - 1120210379 + v126 + (((v130 + v133) | ~v128) ^ v130), 22) = ..要计算R6,要递归向上,这就有点汉诺塔一样,得逐级向上找到比自己小那一块,如果人肉这样逆推的话,非要弄到猴年马月,实在是没这个毅力……若说想要走一点小捷径,那还是有的,就是利用脚本,监控每个参与运算的变量,但仍然耗时巨大,所以,干脆直接进入下一题算了……
0x03 后序传送门:
某司面试题一:smali & java keygen:
http://www.52pojie.cn/thread-403032-1-1.html
某司面试题三:fork & ptrace:
http://www.52pojie.cn/thread-403089-1-1.html
本题所有文件:
某司面试三题
本帖最后由 PoJie_小雨 于 2015-8-20 13:06 编辑0x00 前言
第三题终于不用再写注册机,只要追到注册码。但是这道题,从头到尾,都是一个巨大的骗局,它充满了迷惑和各种anti_debug,不明所以者,一旦深陷其中,就如同中了幻术一般,我也一样,水平不高,致使这道题没有得到完美的解答……
0x01 IDA中的静态注解废话不多说,我们先来看看cm的关键函数--Java_com_ucweb_crackme3_MainActivity_checkPasswd,下面是通过ida F5后得到的类C代码:
int __fastcall Java_com_ucweb_crackme3_MainActivity_checkPasswd(int a1, int a2,int a3)
{
int v3; // r7@1
int v4; // r10@1
void *v5; // r7@1
__pid_t v6; // r0@3
int v7; // r6@3
__int32 v8; // r7@6
__int32 v9; // r10@7
int result; // r0@11
int v11; // r5@14
int v12; // r0@14
bool v13; // cf@14
int v14; // @1
int v15; // @1
int v16; // @1
int v17; // @1
int v18; // @1
int v19; // @1
int v20; // @1
int v21; // @1
int v22; // @1
int v23; // @1
int v24; // @1
int v25; // @1
int v26; // @1
int v27; // @1
int v28; // @1
char *argv; // @17
void *v30; // @17
int v31; // @17
signed int v32; // @1
signed int v33; // @1
signedint v34; // @1
signed int v35; // @1
signed int v36; // @1
signed int v37; // @1
signed int v38; // @1
signed int v39; // @1
signed int v40; // @1
signed int v41; // @1
char v42; // @1
_BYTE v43; // @1
int v44; // @1
int v45; // @1
int v46; // @1
int v47; // @1
int v48; // @1
int v49; // @1
int v50; // @1
int v51; // @1
int v52; // @1
int v53; // @1
int v54; // @1
int v55; // @1
int v56; // @1
signed int v57; // @1
signed int v58; // @1
signed int v59; // @1
int v60; // @1
signed int v61; // @1
signed int v62; // @1
signed int v63; // @1
int v64; // @1
v3 = a1;
v4 = a3;
v60 = -390266736;
v61 = -508559360;
v62 = 1362099998;
v63 = -369094003;
v56 = -1382015835;
v57 = 694420221;
v58 = -2030879789;
v59 = 1685670803;
v32 = 1952539695;
v33 = 1633955681;
v34 = 1664049524;
v35 = 1965976943;
v25 = 0;
v26 = 0;
v27 = 0;
v64 = _stack_chk_guard;
v28 = 0;
v36 = 1650816867;
v37 = 1634886446;
v38 = 1701669731;
v39 = 1919102771;
v42 = 0;
v40 = 1835754337;
v41 = 1651864421;
v24 = 0;
memset(v43, 0, 0x1D7u);
v15 = 0;
v16 = 0;
v17 = 0;
v45 = 0;
v18 = 0;
v46 = 0;
v19 = 0;
v47 = 0;
v20 = 0;
v48 = 0;
v21 = 0;
v49 = 0;
v53 = 0;
v22 = 0;
v50 = 0;
v54 = 0;
v55 = 0;
v23 = 0;
v51 = 0;
v14 = 0;
v44 = 0;
v52 = 0;
v5 = sub_B0C(v3, v4); //"getBytes",
// "(Ljava/lang/String;)[B");
// memcpy 等
if ( v5 && !sub_1280((const char*)&v32, &_data_start, (size_t)&unk_13CFC) )// fopen, fwrite,fclose, chmod
{
LABEL_11:
result = 0;
goto LABEL_12;
}
v6 = fork(); // fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,
// 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量
// 不同,两个进程也可以做不同的事
v7 = v6;
if ( v6 )
{
if ( v6 > 0 )
{
sleep(1u);
if ( ptrace(PTRACE_ATTACH, v7) < 0) // 跟踪指定pid 进程。pid表示被跟踪进程。被跟踪进程将成为当前进程
// 的子进程,并进入中止状态
// 成功返回0。错误返回-1。errno被设置
exit(-1); // 无论在程序中的什么位置,只要执行到exit系统调用,进程就会停止剩
// 下的所有操作,清除包括PCB在内的各种数据结构,并终止本进程的运
// 行
waitpid(v7, 0, 2); // pid_t waitpid(pid_tpid,int * status,int options)
v8 = sub_AA4(v7,(int)&unk_163AC); // returnptrace(PTRACE_GETREGS, a1, 0, a2)
if ( !v8 )
{
while ( 2 )
{
sub_A4C(v7, dword_163E8,(int)&v44, 32);// result = ptrace(PTRACE_PEEKTEXT, v5, v8 + v6, 0)
// 从内存地址中读取一个字节,pid表示被跟踪的子进程,内存地址由
// addr给出,data为用户变量地址用于返回读到的数据
v9 = v8;
do
{
if ( !memcmp((char *)&v44 + v8,&v60, 0x10u) )
{
sub_A4C(v7, dword_163E0,(int)&v14, 40);
v19 += 388;
sub_D50(v7, dword_163E0,(int)&v14, 40);// dest = ptrace(PTRACE_POKETEXT, v5, v8 + v6, dest)
// 往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由
// addr给出,data为所要写入的数据
sub_A4C(v7, v19 + 74996,(int)&v24, 20);
v11 = (int)malloc(v25 + 1);
sub_A4C(v7, v24, v11, v25);
sub_A20(v11, v25); // 一个循环,偏就是走到这里segmentation 异常
// SIGSEGV 11 C 无效的内存引用,或发生段错误时发送给它的信号
sub_D50(v7, v24, v11, v25);
sub_DB8(v7); // result =ptrace(PTRACE_CONT, a1)
// 继续执行
// 这里有sig trap
sub_AA4(v7, (int)&unk_163AC);
sub_A4C(v7, v27, (int)&v52,16);
ptrace(PTRACE_KILL, v7, 0,0); // 杀掉子进程,使它退出。pid表示被跟踪的子进程
wait(0);
unlink((const char *)&v32); // 删除一个文件的目录项并减少它的链接数,若成功则返回0,否则返
// 回-1,调用了unlink以后仍然可以对文件进行读写操作, 删除目录相并
// 减少一个连接数,如果链接数为0并且没有任何进程打开该文件,该文
// 件内容才能被真正删除,但是若又进程打开了该文件,则文件暂时不删
// 除直到所有打开该文件的进程都结束时文件才能被删除
v12 = memcmp(&v56, &v52,0x10u);
v13 = (unsigned int)v12 <= 1;
result = 1 - v12;
if ( !v13 )
result = 0;
goto LABEL_12;
}
v9 += 4;
v8 = v9;
}
while ( v9 != 16 );
sub_DB8(v7);
v8 = sub_AA4(v7, (int)&unk_163AC);
if ( !v8 )
continue;
break;
}
}
}
goto LABEL_11;
}
v30 = v5;
argv = (char *)&v32;
v31 = 0;
execv((const char *)&v32,&argv); // 装入并运行其它程序的函数
result = v7;
LABEL_12:
if ( v64 != _stack_chk_guard )
_stack_chk_fail(result);
return result;
}
接着我们回到标题—fork& ptrace,先来了解一下fork(),见上面代码的125行:
图1.fork()的注解
关于fork(),远没有那么简单,一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。说到这,大家应该已经意识到了,这个cm是双进程,因为有子进程(child process)的存在,在接下来的调试中,会时常遇到SIGCHLD这个信号,给调试带来很大的干扰!关于fork(),大家可以参考:http://blog.csdn.net/jason314/article/details/5640969。
接着,再来说说ptrace(),按照见名知义,各位同学应该已经知道他就是跟踪进程的函数,ptrace 提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image。它主要用于实现断点调试。一个被跟踪的进程运行中,直到发生一个信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否是否忽略引起中止的信号。关于ptrace()的各个参数详细参见:http://blog.sina.com.cn/s/blog_4ac74e9a0100n7w1.html
可以说,fork() 配合上ptrace()给调试带来很多让人头疼的异常,他们一般都是以信号signal的形式出现,关于linux的信号,大家可以参考:http://www.blogjava.net/wayne/archive/2011/10/09/360255.html。所以说,要调戏这个cm,需要很深的基础,我基础不好,因此,不懂的地方,基本都加了注释!
0x02 CM的初探以及修改在真正进入cm3的调戏之前,播个小插曲,因为cm2的成功经验,致使在破解cm3的时候,形成一种思维定势,那就是假的注册码通过memcmp()与真的注册码进行比较,那么memcmp的三个参数中,其中之一就是真的注册码。经验虽好,但有时,人们太过于仰赖自己的经验的话,经验会害死人!
进入调试,如何调戏so文件,在cm2的分析过程中已经赘述过了,这里不再赘述。先来看看一个奇怪的string,在没有选择 Auto comments的情况下,仍然会出现:
图2.crackmesub
这个文件是个elf文件,但是他没有后缀,到后面会用到这个文件,我多次调试之后,在“Choose process to attach to”窗口会看到:图3.crackmesub还带着我输入的假SN,应该就是进程的参数了
单步至F7C的时候,有个函数,进去看看:void *__fastcall sub_B0C(inta1, int a2)
{
int v2; // r5@1
int v3; // r4@1
int v4; // r7@1
int v5; // r6@1
int v6; // r0@1
int v7; // r6@1
size_t v8; // r7@1
const void *v9; // r8@1
void *v10; // r5@2
v2 = a2;
v3 = a1;
v4 = (*(int (**)(void))(*(_DWORD *)a1 +24))();
v5 = (*(int (__fastcall **)(int,_DWORD))(*(_DWORD *)v3 + 668))(v3, "utf-8");
v6 = (*(int (__fastcall **)(int, int, _DWORD,_DWORD))(*(_DWORD *)v3 + 132))(
v3,
v4,
"getBytes",
"(Ljava/lang/String;)[B");
v7 = (*(int (__fastcall **)(int, int, int,int))(*(_DWORD *)v3 + 136))(v3, v2, v6, v5);
v8 = (*(int (__fastcall **)(int,int))(*(_DWORD *)v3 + 684))(v3, v7);
v9 = (const void *)(*(int (__fastcall**)(int, int, _DWORD))(*(_DWORD *)v3 + 736))(v3, v7, 0);
if ( (signed int)v8 > 0 )
{
v10 = malloc(v8 + 1);
memcpy(v10, v9, v8);
*((_BYTE *)v10 + v8) = 0;
}
else
{
v10 = 0;
}
(*(void (__fastcall **)(int, int, const void*, _DWORD))(*(_DWORD *)v3 + 768))(v3, v7, v9, 0);
return v10;
}走过,发现R0已经存着我们输入的假注册码了,通过上面的类C代码也可以发现,他就是用于读取我们输入的注册码!
接着来到FA0,就是sub_1280,这个函数就是往crackmesub里写入了80K的垃圾数据!
到FC0的时候,弹出一个信号框:
图4.sigchld 子进程发生变化
因为在FAC,就有一句bl fork了,这里形成了子进程!继续单步,这时候,异常出现了:
图5.异常
无视之后,我们居然跳到了libc.so(这个相当于OD里所说的系统领空,此时不能按crtl+F7--run until return,按这个组合键会跑飞,要按F9才能回到程序领空,即libcheck),不管他,F9回到libcheck.so!
到FDC之后,有一个跳转,这里先给大家,看关于跳转的语法汇总:
图6.条件码表
因为,我是一边调试,一边跟进写文章,这样很麻烦,所以在这里声明,动态调试的是左图,即我已经修改过的so文件,右边的ida为原始的so文件。好,我们继续看FDC这个跳转:
图7. 把blt改成bgt
通过图6,我们知道,blt的相反就是bgt,所以,我们把blt改成bgt,问题是,怎么改呢,ida不像od灵活,可以直接修改汇编代码,我们需要把汇编代码转换成HEX形式的opcode才能修改,所以,我们调出opcode:
图8.调出opcode
好了,可以看到bltloc_11B8的opcode是750000BA,我们可以搜索bgt,如图9:
图9.搜索bgt
因为是littleendian,所以我们把750000BA改成750000CA,然后,就是回到apkide,“编译生成apk”->“安装生成的apk”,步骤非常麻烦,而且,后面还有要改的地方,我们这里暂时不在apkide里操作……
接着要把1008的BNE改成BEQ,因为默认跳到loc_1068后,离程序结束就不远,我们需要看到他的流程:
图10.修改1008
接着来到loc_1030,终于看到翘首以盼的memcmp()了,然而这里是个死循环,我们需要跳刀真正的验证地址,所以把loc_1038改成BNE,之后,就来关键的loc_108C了
在loc_108C中,引用了sub_A4C、sub_D50、sub_A20、sub_DB8、sub_AA4,见下图:
图11. loc_108C的内部结构
其中sub_A20会触发 SIGSEGV 异常,sub_DB8会触发SIGTRAP,异常我不再重现截图了,可以直接nop掉,然后我们看这行:v12 = memcmp(&v56,&v52, 0x10u);如果这里再不比较注册码真假的话, Java_com_ucweb_crackme3_MainActivity_checkPasswd这个函数就要走到尽头了,鼠标点击v56 ,发现v56 = -1382015835,v52 = 0,两个参数与被nop的sub_A20和sub_DB8并没有交叉,所以nop掉这两函数,并不影响注册码的最终计算,下面是单步到loc_1180时,两个字符串的比较:
图12. 最终比较
寄存器R0和R1都不是0123456789abcdef,而且二者也不像阿里移动安全挑战赛的第二题那样,会出现“aiyou,bucuoo”这样acsii值,不急,我接着往下走,btw,loc_1190也需要nop掉。
往下走到loc_1194:
图13 execv
然后,F8过execv之后,我就可以看到如图3的crackmesub的进程了,没什么,继续F8直到走出libcheck.so,进入到libdvm.so,此时,我们已经走出Java_com_ucweb_crackme3_MainActivity_checkPasswd,可以肆无忌惮地F9了,因为,我们已经跳过程序验证注册码的范围了,一直F9,然后把视线转移到avd里面:
图14.success!
这一系列调试过程,可以参见已经录制好的视频—“没有SN.exe”,我明明没有爆破,但是为什么会突兀的出现了“Success!”?上高中的时候,我生化学的不错,在做化学推断题的时候,老师经常挂在嘴边的一句话就是:大胆假设,小心求证。这里我们可以大胆地假设,这道题没有严格意义的密码,就是说,密码可以被绕过!
0x03 小心求证
1.MainActivity.class package com.ucweb.crackme3;
import android.app.Activity;
import android.os.Bundle;
importandroid.text.Editable;
import android.view.View;
importandroid.view.View.OnClickListener;
import android.widget.Button;
importandroid.widget.EditText;
import android.widget.Toast;
public class MainActivity
extends Activity
{
public Button btn_enter;
public EditText edit;
static
{
System.loadLibrary("check");
}
public native boolean checkPasswd(StringparamString);
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130903040);
this.edit =((EditText)findViewById(2131099648));
this.btn_enter =((Button)findViewById(2131099649));
this.btn_enter.setOnClickListener(newView.OnClickListener()
{
public void onClick(ViewparamAnonymousView)
{
MainActivity.this.btn_enter.setClickable(false);
if (MainActivity.this.edit.getText().toString().length()<= 0)
{
Toast.makeText(MainActivity.this,"Please enter the password", 1).show();
return;
}
if(!MainActivity.this.checkPasswd(MainActivity.this.edit.getText().toString())) {
Toast.makeText(MainActivity.this,"Error!", 0).show();
}
for (;;)
{
MainActivity.this.btn_enter.setClickable(true);
return;
Toast.makeText(MainActivity.this,"Success!", 0).show();
}
}
});
}
}注意最后那个无限循环,弹出"Success!"与否,与checkPasswd无关!这样的代码是很不严谨的,就像那种后台验证不严格的登陆验证程序,可以用 'or'='or' 绕过认证,即可登陆后台!
2.smali代码爆破
图15.smali爆破点
3.crackmesub 之前有提过crackmesub,那么这个crackmesub到底是什么,我们用adb pull/data/data/com.ucweb.crackme3/crackmesub,把这个文件下载到pc中,这个文件没有后缀,用ida打开如下图:
图16.打开出错
图17.重定位到一个非法符号!
图18.包含非标准重定位
Skipping之后,什么都没有:
图19.什么都没有
因为在前面也说过,是在sub_1280中写入了80K的垃圾数据进入crackmesub中的,文件应该是没有加过密的,因为sub_1280的分析如下:
// (文件路径,获取数据地址,个数)
size_t __fastcall sub_1280(const char *a1, const void *a2, size_t a3)
{
const void *v3; // r6@1
size_t v4; // r7@1
const char *v5; // r5@1
FILE *v6; // r4@1
size_t v7; // r6@2
v3 = a2;
v4 = a3;
v5 = a1;
v6 = fopen(a1, (const char*)&off_1C10);
if ( v6 )
{
// v6是写入位置
v7 = fwrite(v3, 1u, v4, v6);
fclose(v6);
chmod(v5, 0x1EDu);
}
else
{
v7 = 0;
}
return v7;
}
可以看到,写进去都是垃圾!
当时我有一个想法,就是注册码可能存在于crackmesub中,于是用010 editor打开crackmesub:
图20.没有,什么都没有!
至此,不知该如何继续了,我觉得这道题没有严格意义的注册码,那他到底怎样才会出现“success!”呢,我觉得是把流程完全走一遍,3个需要修改的跳转都要改,还有3个需要nop的sub,能出现“success!”,应该是要判断crackmesub文件的存在,并且有crackmesub这个进程的存在,因为,先看如下代码:
图21. 杀掉crackmesub进程&删除crackmesub文件
unlink()不一定能删掉crackmesub,但是crackmesub进程是会被ptrace(PTRACE_KILL, …)干掉的,所以,要通过execv()使crackmesub进程重新复活,让流程顺利进入libdvm.so中,让他执行classes.dex中指令即可成功!
0x04 后序
终于完结了,学习那么久,终于有一点成果,虽然果实并不丰满,但毕竟见证一段学习经历,从开始搭建环境起,就遇到了各种莫名其妙的error,让人摸不着头脑,不知道大家有没有过一种错觉,那就是遇到问题,但是却不知道如何具体的阐述问题,然后再百度,因为问题描述的不够精确,所以度娘没法给出solution,在做这三道题过程中,我就经常有这种感觉。也是因为非虫的《Android软件安全与逆向分析》给了帮助,才勉强对付完这3题……晚安!
本题所有文件:
链接:http://pan.baidu.com/s/1o6lABpg 密码:x5st
完全看不懂啊 非常厉害啊,学到了 helius 发表于 2015-8-19 23:43
现在公司的面试题都那么霸道吗
如果你眼尖的话,你就知道这家公司是什么公司了(字里行间已透漏)! 谢谢楼主的无私奉献!!!!!!{:17_1058:} 好东西必须要顶起来啊{:1_900:}