本帖最后由 S18 于 2019-5-7 14:44 编辑
一直以来都想提高一下逆向能力,但工作比较忙。最近正好有点时间,逆向一个小程序。这个是看雪CTF中的一道题,难度不大,比较适合我这种逆向渣来练手。论坛里面如果有想提高SO逆向的新手朋友,可以看看这道题,我觉得比较适合新人练手。
样本APK在附件中。
反编译APK,可以看到在com.zhuotong.crackme.MainActivity的onCreate函数和android.support.v7.app.AppCompiatActivity的onStart函数中都设置了登陆按钮的响应函数。但是MainActivity继承AppCompiatActivity,MainActivity没有重写onStart,并且Activity的生命周期函数onStart在onCreate之后执行,所以只要看AppCompiatActivity的onStart函数就可以了。
[Java] 纯文本查看 复制代码 public class AppCompiatActivity extends AppCompatActivity {
public static final int MSG_LOGIN = 0;
private Handler handler;
private Button login;
private String mName;
private String mPassword;
private EditText name;
private EditText password;
public native boolean eq(String str);
static {
System.loadLibrary("oo000oo");
}
/* Access modifiers changed, original: protected */
public void onStart() {
super.onStart();
this.login = (Button) findViewById(R.id.login_button);
this.login.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
AppCompiatActivity.this.mName = AppCompiatActivity.this.name.getText().toString();
AppCompiatActivity.this.mPassword = AppCompiatActivity.this.password.getText().toString();
if (TextUtils.isEmpty(AppCompiatActivity.this.mName) || TextUtils.isEmpty(AppCompiatActivity.this.mPassword)) {
Toast.makeText(AppCompiatActivity.this, "用户名或密码为空", 1).show();
return;
}
int i = 0;
AppCompiatActivity.this.login.setEnabled(false);
if (AppCompiatActivity.this.eq(AppCompiatActivity.this.mPassword)) {
...
} else {
Toast.makeText(AppCompiatActivity.this, "error", 1).show();
}
}
});
this.name = (EditText) findViewById(R.id.name);
this.name.setEnabled(false);
this.password = (EditText) findViewById(R.id.password);
}
...
}
核心逻辑在liboo000oo.so的eq函数里。
liboo000oo.so有一个init函数.datadiv_decode5009363700628197108,在该so被加载时会首先执行,作用是解密几个字符串,包括:
- (1) "650f909c-7217-3647-9331-c82df8b98e98"
- (2) "!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789';"
- (3) "android/support/v7/app/AppCompiatActivity"
- (4) "eq"
- (5) "(Ljava/lang/String;)Z"
在后面的分析中可以看到:
- 字符串(1)经过一系列变换后,作为rc4的key。
- 字符串(2)作为base64的字符表。
- 字符串(3)、(4)、(5)在注册android.support.v7.app.AppCompiatActivity的eq函数时用到。
下面贴一下.datadiv_decode5009363700628197108函数的代码,具体看注释:
[Asm] 纯文本查看 复制代码 .text:00000B4C EXPORT .datadiv_decode5009363700628197108
.text:00000B4C .datadiv_decode5009363700628197108 ; CODE XREF: j_.datadiv_decode5009363700628197108+8↑j
.text:00000B4C ; DATA XREF: LOAD:00000220↑o ...
.text:00000B4C LDR R1, =(byte_4020 - 0xB54)
.text:00000B4E MOVS R0, #0
.text:00000B50 ADD R1, PC ; byte_4020
.text:00000B52
.text:00000B52 loc_B52 ; CODE XREF: .datadiv_decode5009363700628197108+12↓j
.text:00000B52 LDRB R2, [R1,R0]
.text:00000B54 EOR.W R2, R2, #0xA5 ; 循环异或解密,最后:byte_4020="650f909c-7217-3647-9331-c82df8b98e98"
.text:00000B58 STRB R2, [R1,R0]
.text:00000B5A ADDS R0, #1
.text:00000B5C CMP R0, #0x25 ; '%'
.text:00000B5E BNE loc_B52
.text:00000B60 LDR R1, =(byte_4050 - 0xB68)
.text:00000B62 MOVS R0, #0
.text:00000B64 ADD R1, PC ; byte_4050
.text:00000B66
.text:00000B66 loc_B66 ; CODE XREF: .datadiv_decode5009363700628197108+26↓j
.text:00000B66 LDRB R2, [R1,R0]
.text:00000B68 EOR.W R2, R2, #0xA5 ; 循环异或解密,最后:byte_4050="!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
.text:00000B6C STRB R2, [R1,R0]
.text:00000B6E ADDS R0, #1
.text:00000B70 CMP R0, #0x42 ; 'B'
.text:00000B72 BNE loc_B66
.text:00000B74 LDR R1, =(byte_40A0 - 0xB7C)
.text:00000B76 MOVS R0, #0
.text:00000B78 ADD R1, PC ; byte_40A0
.text:00000B7A
.text:00000B7A loc_B7A ; CODE XREF: .datadiv_decode5009363700628197108+3A↓j
.text:00000B7A LDRB R2, [R1,R0]
.text:00000B7C EOR.W R2, R2, #0x84 ; 循环异或解密,最后:byte_40A0="android/support/v7/app/AppCompiatActivity"
.text:00000B80 STRB R2, [R1,R0]
.text:00000B82 ADDS R0, #1
.text:00000B84 CMP R0, #0x2A ; '*'
.text:00000B86 BNE loc_B7A
.text:00000B88 PUSH {R4-R6,LR}
.text:00000B8A LDR R1, =(byte_40CA - 0xB92)
.text:00000B8C LDR R0, =(byte_40D0 - 0xB94)
.text:00000B8E ADD R1, PC ; byte_40CA ; 异或解密byte_40CA开始的3个字节,结果:byte_40CA="eq"
.text:00000B90 ADD R0, PC ; byte_40D0 ; 异或解密byte_40D0开始的22个字节,结果:byte_40D0="(Ljava/lang/String;)Z"
.text:00000B92 LDRB R2, [R1]
.text:00000B94 LDRB R3, [R1,#(byte_40CB - 0x40CA)]
.text:00000B96 EOR.W R2, R2, #0xFC
.text:00000B9A LDRB.W R12, [R1,#(byte_40CC - 0x40CA)]
.text:00000B9E STRB R2, [R1]
.text:00000BA0 EOR.W R2, R3, #0xFC
.text:00000BA4 LDRB R3, [R0]
.text:00000BA6 LDRB.W LR, [R0,#(byte_40D1 - 0x40D0)]
.text:00000BAA STRB R2, [R1,#(byte_40CB - 0x40CA)]
.text:00000BAC EOR.W R2, R12, #0xFC
.text:00000BB0 STRB R2, [R1,#(byte_40CC - 0x40CA)]
.text:00000BB2 EOR.W R1, R3, #0x62
.text:00000BB6 LDRB R4, [R0,#(byte_40D2 - 0x40D0)]
.text:00000BB8 LDRB R2, [R0,#(word_40D6 - 0x40D0)]
.text:00000BBA LDRB R6, [R0,#(byte_40D3 - 0x40D0)]
.text:00000BBC LDRB R5, [R0,#(byte_40D4 - 0x40D0)]
.text:00000BBE EOR.W R2, R2, #0x62
.text:00000BC2 LDRB.W R12, [R0,#(byte_40D5 - 0x40D0)]
.text:00000BC6 STRB R1, [R0]
.text:00000BC8 EOR.W R1, LR, #0x62
.text:00000BCC STRB R1, [R0,#(byte_40D1 - 0x40D0)]
.text:00000BCE EOR.W R1, R4, #0x62
.text:00000BD2 STRB R1, [R0,#(byte_40D2 - 0x40D0)]
.text:00000BD4 EOR.W R1, R6, #0x62
.text:00000BD8 STRB R1, [R0,#(byte_40D3 - 0x40D0)]
.text:00000BDA EOR.W R1, R5, #0x62
.text:00000BDE STRB R1, [R0,#(byte_40D4 - 0x40D0)]
.text:00000BE0 EOR.W R1, R12, #0x62
.text:00000BE4 STRB R1, [R0,#(byte_40D5 - 0x40D0)]
.text:00000BE6 LDRB R1, [R0,#(word_40D6+1 - 0x40D0)]
.text:00000BE8 LDRB R3, [R0,#(byte_40D8 - 0x40D0)]
.text:00000BEA EOR.W R1, R1, #0x62
.text:00000BEE STRB R2, [R0,#(word_40D6 - 0x40D0)]
.text:00000BF0 STRB R1, [R0,#(word_40D6+1 - 0x40D0)]
.text:00000BF2 EOR.W R1, R3, #0x62
.text:00000BF6 STRB R1, [R0,#(byte_40D8 - 0x40D0)]
.text:00000BF8 LDRB R1, [R0,#(byte_40D9 - 0x40D0)]
.text:00000BFA EOR.W R1, R1, #0x62
.text:00000BFE STRB R1, [R0,#(byte_40D9 - 0x40D0)]
.text:00000C00 LDRB R1, [R0,#(byte_40DA - 0x40D0)]
.text:00000C02 EOR.W R1, R1, #0x62
.text:00000C06 STRB R1, [R0,#(byte_40DA - 0x40D0)]
.text:00000C08 LDRB R1, [R0,#(byte_40DB - 0x40D0)]
.text:00000C0A EOR.W R1, R1, #0x62
.text:00000C0E STRB R1, [R0,#(byte_40DB - 0x40D0)]
.text:00000C10 LDRB R1, [R0,#(byte_40DC - 0x40D0)]
.text:00000C12 EOR.W R1, R1, #0x62
.text:00000C16 STRB R1, [R0,#(byte_40DC - 0x40D0)]
.text:00000C18 LDRB R1, [R0,#(byte_40DD - 0x40D0)]
.text:00000C1A EOR.W R1, R1, #0x62
.text:00000C1E STRB R1, [R0,#(byte_40DD - 0x40D0)]
.text:00000C20 LDRB R1, [R0,#(byte_40DE - 0x40D0)]
.text:00000C22 EOR.W R1, R1, #0x62
.text:00000C26 STRB R1, [R0,#(byte_40DE - 0x40D0)]
.text:00000C28 LDRB R1, [R0,#(byte_40DF - 0x40D0)]
.text:00000C2A EOR.W R1, R1, #0x62
.text:00000C2E STRB R1, [R0,#(byte_40DF - 0x40D0)]
.text:00000C30 LDRB R1, [R0,#(byte_40E0 - 0x40D0)]
.text:00000C32 EOR.W R1, R1, #0x62
.text:00000C36 STRB R1, [R0,#(byte_40E0 - 0x40D0)]
.text:00000C38 LDRB R1, [R0,#(byte_40E1 - 0x40D0)]
.text:00000C3A EOR.W R1, R1, #0x62
.text:00000C3E STRB R1, [R0,#(byte_40E1 - 0x40D0)]
.text:00000C40 LDRB R1, [R0,#(byte_40E2 - 0x40D0)]
.text:00000C42 EOR.W R1, R1, #0x62
.text:00000C46 STRB R1, [R0,#(byte_40E2 - 0x40D0)]
.text:00000C48 LDRB R1, [R0,#(byte_40E3 - 0x40D0)]
.text:00000C4A EOR.W R1, R1, #0x62
.text:00000C4E STRB R1, [R0,#(byte_40E3 - 0x40D0)]
.text:00000C50 LDRB R1, [R0,#(byte_40E4 - 0x40D0)]
.text:00000C52 EOR.W R1, R1, #0x62
.text:00000C56 STRB R1, [R0,#(byte_40E4 - 0x40D0)]
.text:00000C58 LDRB R1, [R0,#(byte_40E5 - 0x40D0)]
.text:00000C5A EOR.W R1, R1, #0x62
.text:00000C5E STRB R1, [R0,#(byte_40E5 - 0x40D0)]
.text:00000C60 POP {R4-R6,PC}
.text:00000C60 ; End of function .datadiv_decode5009363700628197108
.datadiv_decode5009363700628197108函数执行完之后,会执行JNI_Onload。JNI_Onload函数的代码很简单,就是注册android.support.v7.app.AppCompiatActivity的eq函数。
[Asm] 纯文本查看 复制代码 .text:00000AB4 ; signed int __fastcall JNI_OnLoad(JavaVM *vm)
.text:00000AB4 EXPORT JNI_OnLoad
.text:00000AB4 JNI_OnLoad ; DATA XREF: LOAD:00000230↑o
.text:00000AB4
.text:00000AB4 env = -0x18
.text:00000AB4 var_14 = -0x14
.text:00000AB4 var_10 = -0x10
.text:00000AB4
.text:00000AB4 ; __unwind {
.text:00000AB4 PUSH {R4-R7,LR}
.text:00000AB6 ADD R7, SP, #0xC
.text:00000AB8 STR.W R8, [SP,#0xC+var_10]!
.text:00000ABC SUB SP, SP, #8
.text:00000ABE LDR R1, =(__stack_chk_guard_ptr - 0xACC)
.text:00000AC0 MOV R8, #0x10004
.text:00000AC8 ADD R1, PC ; __stack_chk_guard_ptr
.text:00000ACA MOV R2, R8
.text:00000ACC LDR R4, [R1] ; __stack_chk_guard
.text:00000ACE LDR R1, [R4]
.text:00000AD0 STR R1, [SP,#0x18+var_14]
.text:00000AD2 MOVS R1, #0
.text:00000AD4 STR R1, [SP,#0x18+env]
.text:00000AD6 LDR R1, [R0]
.text:00000AD8 LDR R3, [R1,#0x18] ; R3=GetEnv
.text:00000ADA MOV R1, SP
.text:00000ADC BLX R3
.text:00000ADE CBZ R0, loc_AF8
.text:00000AE0
.text:00000AE0 loc_AE0 ; CODE XREF: JNI_OnLoad+68↓j
.text:00000AE0 ; JNI_OnLoad+80↓j
.text:00000AE0 MOV.W R0, #0xFFFFFFFF
.text:00000AE4
.text:00000AE4 loc_AE4 ; CODE XREF: JNI_OnLoad+86↓j
.text:00000AE4 LDR R1, [SP,#0x18+var_14]
.text:00000AE6 LDR R2, [R4]
.text:00000AE8 SUBS R1, R2, R1
.text:00000AEA ITTT EQ
.text:00000AEC ADDEQ SP, SP, #8
.text:00000AEE LDREQ.W R8, [SP+0x10+var_10],#4
.text:00000AF2 POPEQ {R4-R7,PC}
.text:00000AF4 BLX __stack_chk_fail
.text:00000AF8 ; ---------------------------------------------------------------------------
.text:00000AF8
.text:00000AF8 loc_AF8 ; CODE XREF: JNI_OnLoad+2A↑j
.text:00000AF8 LDR R5, [SP,#0x18+env]
.text:00000AFA LDR R0, =(off_4010 - 0xB02)
.text:00000AFC LDR R2, [R5]
.text:00000AFE ADD R0, PC ; off_4010
.text:00000B00 LDR R1, [R0] ; byte_40A0 ; R1="android/support/v7/app/AppCompiatActivity"
.text:00000B02 MOV R0, R5 ; R0=env
.text:00000B04 LDR R2, [R2,#0x18] ; R2=FindClass
.text:00000B06 BLX R2
.text:00000B08 MOV R6, R0 ; R6=AppCompiatActivity_clazz
.text:00000B0A LDR R0, [R5]
.text:00000B0C MOV R1, R6 ; R1=AppCompiatActivity_clazz
.text:00000B0E LDR R2, [R0,#0x54] ; R2=NewGlobalRef
.text:00000B10 MOV R0, R5 ; R0=env
.text:00000B12 BLX R2
.text:00000B14 LDR R1, =(dword_4110 - 0xB1C)
.text:00000B16 CMP R6, #0
.text:00000B18 ADD R1, PC ; dword_4110
.text:00000B1A STR R0, [R1]
.text:00000B1C BEQ loc_AE0
.text:00000B1E LDR R0, [R5]
.text:00000B20 MOV R1, R6 ; R1=AppCompiatActivity_clazz
.text:00000B22 MOVS R3, #1
.text:00000B24 LDR.W R12, [R0,#0x35C] ; R12=RegisterNatives
.text:00000B28 MOV R0, R5
.text:00000B2A LDR R2, =(off_4014 - 0xB30)
.text:00000B2C ADD R2, PC ; off_4014
.text:00000B2E BLX R12 ; 注册android.support.v7.app.AppCompiatActivity->eq
.text:00000B30 CMP.W R0, #0xFFFFFFFF
.text:00000B34 BLE loc_AE0
.text:00000B36 ADD.W R0, R8, #2
.text:00000B3A B loc_AE4
.text:00000B3A ; End of function JNI_OnLoad
去off_4014看一下eq函数对应liboo000oo.so里的sub_784。
[Asm] 纯文本查看 复制代码 .data:00004014 off_4014 DCD byte_40CA ; DATA XREF: JNI_OnLoad+78↑o
.data:00004014 ; .text:off_B48↑o
.data:00004014 ; 指向字符串:"eq"(解密后)
.data:00004018 DCD byte_40D0 ; 指向字符串:"(Ljava/lang/String;)Z"(解密后)
.data:0000401C DCD sub_784+1
剩下的就是分析核心函数sub_784了。sub_784的逻辑如下:
1. 对之前解密出来的字符串"650f909c-7217-3647-9331-c82df8b98e98"进行逆序,得到"89e89b8f-d28c-1339-7463-7127c909f056"
2. 继续对字符串"89e89b8f-d28c-1339-7463-7127c909f056"进行变换,算法如下:
[Plain Text] 纯文本查看 复制代码 表1是"dbeafc",表2是"2409715836"
输入是"89e89b8f-d28c-1339-7463-7127c909f056"
依次从字符串中取出一个字符:
如果是数字,则减去0x30('0'),得到的值作为索引,查表"2409715836"
如果是字母,则减去0x61('a'),得到的值作为索引,查表"dbeafc"
如果是'-',则原样存储。
第1个字符'8',减去0x30,得到8,查表"2409715836",得到'3'
第2个字符'9',减去0x30,得到9,查表"2409715836",得到'6'
第3个字符'e',减去0x61,得到4,查表"dbeafc",得到'f'
...
最后变换的结果是:"36f36b3c-a03e-4996-8759-8408e626c215",这个字符串作为后面rc4算法的key。
3. 初始化rc4的状态向量S,内容为:
[Plain Text] 纯文本查看 复制代码 D7 DF 02 D4 FE 6F 53 3C 25 6C 99 97 06 56 8F DE
40 11 64 07 36 15 70 CA 18 17 7D 6A DB 13 30 37
29 60 E1 23 28 8A 50 8C AC 2F 88 20 27 0F 7C 52
A2 AB FC A1 CC 21 14 1F C2 B2 8B 2C B0 3A 66 46
3D BB 42 A5 0C 75 22 D8 C3 76 1E 83 74 F0 F6 1C
26 D1 4F 0B FF 4C 4D C1 87 03 5A EE A4 5D 9E F4
C8 0D 62 63 3E 44 7B A3 68 32 1B AA 2D 05 F3 F7
16 61 94 E0 D0 D3 98 69 78 E9 0A 65 91 8E 35 85
7A 51 86 10 3F 7F 82 DD B5 1A 95 E7 43 FD 9B 24
45 EF 92 5C E4 96 A9 9C 55 89 9A EA F9 90 5F B8
04 84 CF 67 93 00 A6 39 A8 4E 59 31 6B AD 5E 5B
77 B1 54 DC 38 41 B6 47 9F 73 BA F8 AE C4 BE 34
01 4B 2A 8D BD C5 C6 E8 AF C9 F5 CB FB CD 79 CE
12 71 D2 FA 09 D5 BC 58 19 80 DA 49 1D E6 2E E3
7E B7 3B B3 A0 B9 E5 57 6E D9 08 EB C7 ED 81 F1
F2 BF C0 A7 4A D6 2B B4 72 9D 0E 6D EC 48 E2 33
4. 以"36f36b3c-a03e-4996-8759-8408e626c215"为key,初始化rc4的临时向量T,算法如下:
[Plain Text] 纯文本查看 复制代码 for i = 0 to 255 do
T[i] = K[i mod keylen];
临时向量T初始化后的结果为:
[Plain Text] 纯文本查看 复制代码 "36f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408"
"e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8"
"759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03"
"e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f3"
5. 结合前面的临时向量T,重新排列一下状态向量S,算法如下:
[Plain Text] 纯文本查看 复制代码 j=0;
for i = 0 to 255 do
j = (j + S[i] + T[i]) mod 256;
swap(S[i], S[j]);
6. 接下来就是计算rc4密钥,对输入字符进行rc4加密+base64转码。
计算rc4密钥流的算法如下:
[Plain Text] 纯文本查看 复制代码 i, j = 0;
for r = 0 to len do //len为明文长度
i = (i + 1) mod 256;
j = (j + S[i]) mod 256;
swap(S[i], S[j]);
t = (S[i] + S[j]) mod 256;
k[r] = S[t];
base64原理:
[Plain Text] 纯文本查看 复制代码 base64(3个字节有24个bit,对应4个base64字符):
M a n
01001101 01100001 01101110
{010011}{01 0110}{0001 01}{101110}
索引1 索引2 索引3 索引4
根据4个索引,去查base64字符表
要注意,作者对base64进行了修改。索引1对应的字符异或了0x7,索引3对应的字符异或了0xf。并且要注意最后补的垫字符是';'。
base64字符表也与标准的不同:"!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
7. 最后,将得到的base64字符串与" {98gal!Tn?@#fj'j$\g;;"进行比对。
下面贴一下sub_784的代码,里面写了详细的注释:
[Asm] 纯文本查看 复制代码 .text:00000784 ; =============== S U B R O U T I N E =======================================
.text:00000784
.text:00000784 ; Attributes: bp-based frame
.text:00000784
.text:00000784 ; int __fastcall sub_784(JNIEnv *env, jobject obj, void *str)
.text:00000784 sub_784 ; DATA XREF: .data:0000401C↓o
.text:00000784
.text:00000784 var_234 = -0x234
.text:00000784 var_230 = -0x230
.text:00000784 var_22C = -0x22C
.text:00000784 var_228 = -0x228
.text:00000784 INPUT = -0x224
.text:00000784 var_S = -0x220
.text:00000784 var_T = -0x120
.text:00000784 var_20 = -0x20
.text:00000784
.text:00000784 ; __unwind {
.text:00000784 PUSH {R4-R7,LR}
.text:00000786 ADD R7, SP, #0xC
.text:00000788 PUSH.W {R8-R11}
.text:0000078C SUB.W SP, SP, #0x21C
.text:00000790 LDR R1, =(__stack_chk_guard_ptr - 0x796)
.text:00000792 ADD R1, PC ; __stack_chk_guard_ptr
.text:00000794 LDR R1, [R1] ; __stack_chk_guard
.text:00000796 STR R1, [SP,#0x238+var_22C]
.text:00000798 LDR R1, [R1]
.text:0000079A STR R1, [SP,#0x238+var_20]
.text:0000079C LDR R1, [R0]
.text:0000079E LDR.W R3, [R1,#0x2A4] ; R3=GetStringUTFChars
.text:000007A2 MOV R1, R2
.text:000007A4 MOVS R2, #0
.text:000007A6 BLX R3 ; 获取指向输入字符串的char*指针
.text:000007A8 STR R0, [SP,#0x238+INPUT]
.text:000007AA LDR R0, =(byte_4020 - 0x7B0)
.text:000007AC ADD R0, PC ; byte_4020 ; R0="650f909c-7217-3647-9331-c82df8b98e98"
.text:000007AE BLX strlen
.text:000007B2 MOV R10, R0 ; R10=24h(R0指向字符串的长度)
.text:000007B4 BLX malloc ; 调用malloc分配3块长度为24h字节的内存,地址分别保存到R6、R8、R11
.text:000007B8 MOV R6, R0
.text:000007BA MOV R0, R10 ; size
.text:000007BC BLX malloc
.text:000007C0 MOV R8, R0
.text:000007C2 MOV R0, R10 ; size
.text:000007C4 BLX malloc
.text:000007C8 MOV R11, R0
.text:000007CA MOV R0, R6
.text:000007CC MOV R1, R10
.text:000007CE BLX __aeabi_memclr ; 调用__aeabi_memclr,将3块新分配的内存清0
.text:000007D2 MOV R0, R8
.text:000007D4 MOV R1, R10
.text:000007D6 BLX __aeabi_memclr
.text:000007DA MOV R0, R11
.text:000007DC MOV R1, R10
.text:000007DE BLX __aeabi_memclr
.text:000007E2 CMP.W R10, #0
.text:000007E6 BEQ loc_876
.text:000007E8 LDR R1, =(byte_4020 - 0x7F2)
.text:000007EA MOVS R0, #0 ; R0=0
.text:000007EC MOV R2, R10 ; R2=24h
.text:000007EE ADD R1, PC ; byte_4020 ; R1="650f909c-7217-3647-9331-c82df8b98e98"
.text:000007F0
.text:000007F0 loc_7F0 ; CODE XREF: sub_784+7C↓j
.text:000007F0 LDRB R3, [R1] ; 通过一个小循环,去掉R1指向的字符串中的'-',
.text:000007F0 ; 得到"650f909c721736479331c82df8b98e98",保存到R8指向的内存中
.text:000007F2 ADDS R1, #1
.text:000007F4 CMP R3, #0x2D ; '-'
.text:000007F6 ITT NE
.text:000007F8 STRNEB.W R3, [R8,R0]
.text:000007FC ADDNE R0, #1
.text:000007FE SUBS R2, #1
.text:00000800 BNE loc_7F0
.text:00000802 CMP R0, #1 ; R0=20h(字符串"650f909c721736479331c82df8b98e98"的长度)
.text:00000804 BLT loc_876
.text:00000806 SUBS R1, R0, #1 ; R1=R0-1=1fh
.text:00000808 MOV R2, #0xFFFFFFF8
.text:0000080C MOVS R3, #0
.text:0000080E MOV.W R12, #0x2D ; '-'
.text:00000812 MOVS R0, #0
.text:00000814
.text:00000814 loc_814 ; CODE XREF: sub_784+B4↓j
.text:00000814 ORR.W R4, R3, R2,LSR#2
.text:00000818 CMP R4, #3
.text:0000081A BHI loc_824
.text:0000081C ADDS R4, R0, #1
.text:0000081E STRB.W R12, [R6,R0]
.text:00000822 B loc_826
.text:00000824 ; ---------------------------------------------------------------------------
.text:00000824
.text:00000824 loc_824 ; CODE XREF: sub_784+96↑j
.text:00000824 MOV R4, R0
.text:00000826
.text:00000826 loc_826 ; CODE XREF: sub_784+9E↑j
.text:00000826 LDRB.W R0, [R8,R1] ; R8="650f909c721736479331c82df8b98e98"
.text:00000826 ; R1初始值为1fh,然后减1,也就是依次从此字符串由后向前取字符
.text:0000082A SUBS R1, #1
.text:0000082C ADD.W R3, R3, #0x40000000
.text:00000830 STRB R0, [R6,R4] ; 通过一个小循环,将最初的字符串("650f909c-7217-3647-9331-c82df8b98e98")逆序,
.text:00000830 ; 得到"89e89b8f-d28c-1339-7463-7127c909f056",保存到R6指向的内存中
.text:00000832 ADDS R2, #1
.text:00000834 ADDS R0, R4, #1
.text:00000836 ADDS R5, R1, #1
.text:00000838 BNE loc_814
.text:0000083A CMP R4, #0 ; R4=23h
.text:0000083C BLT loc_876
.text:0000083E LDR R1, =(aDbeafc24097158 - 0x848)
.text:00000840 MOV R3, R11
.text:00000842 LDR R2, =(aDbeafc24097158+6 - 0x84A)
.text:00000844 ADD R1, PC ; "dbeafc2409715836" ; R1="dbeafc"
.text:00000846 ADD R2, PC ; "2409715836" ; R2="2409715836"
.text:00000846 ; 接下来又是一个循环,继续对字符串进行变换
.text:00000846 ; 表1是"dbeafc",表2是"2409715836"
.text:00000846 ; 输入是"89e89b8f-d28c-1339-7463-7127c909f056"
.text:00000846 ; 依次从字符串中取出一个字符:
.text:00000846 ; 如果是数字,则减去0x30('0'),得到的值作为索引,查表"2409715836"
.text:00000846 ; 如果是字母,则减去0x61('a'),得到的值作为索引,查表"dbeafc"
.text:00000846 ; 如果是'-',则原样存储。
.text:00000846 ; 第1个字符'8',减去0x30,得到8,查表"2409715836",得到'3'
.text:00000846 ; 第2个字符'9',减去0x30,得到9,查表"2409715836",得到'6'
.text:00000846 ; 第3个字符'e',减去0x61,得到4,查表"dbeafc",得到'f'
.text:00000846 ; ...
.text:00000846 ; 最后变换的结果是:"36f36b3c-a03e-4996-8759-8408e626c215"
.text:00000848
.text:00000848 loc_848 ; CODE XREF: sub_784+F0↓j
.text:00000848 LDRB R5, [R6] ; R6="89e89b8f-d28c-1339-7463-7127c909f056"
.text:0000084A SUB.W R4, R5, #0x61
.text:0000084E UXTB R4, R4
.text:00000850 CMP R4, #5
.text:00000852 BHI loc_85A
.text:00000854 ADD R5, R1 ; 如果是字母,减'a',然后查表R2="dbeafc"
.text:00000856 SUBS R5, #0x61 ; 'a'
.text:00000858 B loc_868
.text:0000085A ; ---------------------------------------------------------------------------
.text:0000085A
.text:0000085A loc_85A ; CODE XREF: sub_784+CE↑j
.text:0000085A SUB.W R4, R5, #0x30
.text:0000085E UXTB R4, R4
.text:00000860 CMP R4, #9
.text:00000862 BHI loc_86A
.text:00000864 ADD R5, R2 ; 如果是数字,减'0',然后查表R2="2409715836"
.text:00000866 SUBS R5, #0x30 ; '0'
.text:00000868
.text:00000868 loc_868 ; CODE XREF: sub_784+D4↑j
.text:00000868 LDRB R5, [R5] ; 从表中取出转换后的字符
.text:0000086A
.text:0000086A loc_86A ; CODE XREF: sub_784+DE↑j
.text:0000086A STRB.W R5, [R3],#1 ; 变换后的结果,保存在R3指向的内存中
.text:0000086E SUBS R0, #1
.text:00000870 ADD.W R6, R6, #1 ; R6+=1,指向"89e89b8f-d28c-1339-7463-7127c909f056"中的下一个字符
.text:00000874 BNE loc_848
.text:00000876
.text:00000876 loc_876 ; CODE XREF: sub_784+62↑j
.text:00000876 ; sub_784+80↑j ...
.text:00000876 LDR R1, =(unk_23E8 - 0x884)
.text:00000878 ADD.W R9, SP, #0x238+var_S
.text:0000087C MOV.W R2, #0x100
.text:00000880 ADD R1, PC ; unk_23E8
.text:00000882 MOV R0, R9
.text:00000884 BLX __aeabi_memcpy8 ; 为栈上的一块256字节大小的buf赋初值,貌似是RC4的状态变量S。
.text:00000884 ; 那就暂时将这块内存称为数组S吧,初始内容:
.text:00000884 ; D7 DF 02 D4 FE 6F 53 3C 25 6C 99 97 06 56 8F DE
.text:00000884 ; 40 11 64 07 36 15 70 CA 18 17 7D 6A DB 13 30 37
.text:00000884 ; 29 60 E1 23 28 8A 50 8C AC 2F 88 20 27 0F 7C 52
.text:00000884 ; A2 AB FC A1 CC 21 14 1F C2 B2 8B 2C B0 3A 66 46
.text:00000884 ; 3D BB 42 A5 0C 75 22 D8 C3 76 1E 83 74 F0 F6 1C
.text:00000884 ; 26 D1 4F 0B FF 4C 4D C1 87 03 5A EE A4 5D 9E F4
.text:00000884 ; C8 0D 62 63 3E 44 7B A3 68 32 1B AA 2D 05 F3 F7
.text:00000884 ; 16 61 94 E0 D0 D3 98 69 78 E9 0A 65 91 8E 35 85
.text:00000884 ; 7A 51 86 10 3F 7F 82 DD B5 1A 95 E7 43 FD 9B 24
.text:00000884 ; 45 EF 92 5C E4 96 A9 9C 55 89 9A EA F9 90 5F B8
.text:00000884 ; 04 84 CF 67 93 00 A6 39 A8 4E 59 31 6B AD 5E 5B
.text:00000884 ; 77 B1 54 DC 38 41 B6 47 9F 73 BA F8 AE C4 BE 34
.text:00000884 ; 01 4B 2A 8D BD C5 C6 E8 AF C9 F5 CB FB CD 79 CE
.text:00000884 ; 12 71 D2 FA 09 D5 BC 58 19 80 DA 49 1D E6 2E E3
.text:00000884 ; 7E B7 3B B3 A0 B9 E5 57 6E D9 08 EB C7 ED 81 F1
.text:00000884 ; F2 BF C0 A7 4A D6 2B B4 72 9D 0E 6D EC 48 E2 33
.text:00000888 ADD R4, SP, #0x238+var_T
.text:0000088A MOVS R5, #0 ; 接下来是一个小循环,初始化栈上的(R4指向的)另一块256字节大小的buf。
.text:0000088A ; 貌似是以"36f36b3c-a03e-4996-8759-8408e626c215"为Key,初始化RC4的临时向量T:
.text:0000088A ; for i=0 to 255 do
.text:0000088A ; T[i]=K[i mod keylen];
.text:0000088C
.text:0000088C loc_88C ; CODE XREF: sub_784+11C↓j
.text:0000088C MOV R0, R5
.text:0000088E MOV R1, R10
.text:00000890 BLX sub_D20
.text:00000894 LDRB.W R0, [R11,R1] ; 这行指令中的R1,从0-23h,循环变换。
.text:00000894 ; 所以R1即上面算法中的“i mod 23h(keylen)”
.text:00000894 ; R11="36f36b3c-a03e-4996-8759-8408e626c215"
.text:00000898 STRB R0, [R4,R5] ; 临时向量T,保存在R4指向的内存中。
.text:00000898 ; 最后结果为:
.text:00000898 ; "36f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408"
.text:00000898 ; "e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8"
.text:00000898 ; "759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03"
.text:00000898 ; "e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f3"
.text:0000089A ADDS R5, #1
.text:0000089C CMP.W R5, #0x100
.text:000008A0 BNE loc_88C
.text:000008A2 LDRB.W R0, [SP,#0x238+var_T]
.text:000008A6 SUBS R0, #0x29 ; ')'
.text:000008A8 UXTB R0, R0 ; R0=10
.text:000008AA LDRB.W R1, [R9,R0] ; R9指向状态向量S,R1=S[10]
.text:000008AE STRB.W R1, [SP,#0x238+var_S] ; S[0]=S[10]
.text:000008B2 MOVS R1, #0xD7 ; R1=0xD7
.text:000008B4 STRB.W R1, [R9,R0] ; S[10]=0xD7=S[0]
.text:000008B4 ; 上面几行代码,将状态向量S的索引为0和10的两个元素互换了一下。
.text:000008B4 ; 相当于是将下面重新排列S算法的第1次循环执行完了,下面的循环直接从第2次循环开始执行。
.text:000008B8 MOVS R1, #1 ; 下面是个小循环,结合前面的临时向量T,重新排列一下状态向量S,算法如下:
.text:000008B8 ; (1) j=0;
.text:000008B8 ; (2) for i=0 to 255 do
.text:000008B8 ; (3) j=(j+S[i]+T[i]) mod 256;
.text:000008B8 ; (4) swap(S[i],S[j]);
.text:000008BA
.text:000008BA loc_8BA ; CODE XREF: sub_784+15E↓j
.text:000008BA LDRB.W R2, [R9,R1] ; R2=S[i]:R9指向状态向量S,R1即上面算法中的i,初始值为1。
.text:000008BE LDRB R3, [R4,R1] ; R3=T[i]:R4指向临时向量T
.text:000008C0 ADD R3, R2 ; R3=S[i]+T[i](对应上述算法中的第3行)
.text:000008C2 ADD R0, R3 ; R0=j+S[i]+T[i](对应上述算法中的第3行)
.text:000008C2 ; R0即上面算法中的j,初始值为10
.text:000008C4 ASRS R3, R0, #0x1F
.text:000008C6 ADD.W R3, R0, R3,LSR#24
.text:000008CA BIC.W R3, R3, #0xFF
.text:000008CE SUBS R0, R0, R3 ; R0=(j+S[i]+T[i]) mod 256(对应上述算法中的第3行)
.text:000008D0 LDRB.W R3, [R9,R0] ; R3=S[j](对应上述算法中的第4行)
.text:000008D4 STRB.W R3, [R9,R1] ; S[i]=S[j](对应上述算法中的第4行)
.text:000008D8 ADDS R1, #1 ; i+=1
.text:000008DA CMP.W R1, #0x100
.text:000008DE STRB.W R2, [R9,R0] ; S[j]=S[i](对应上述算法中的第4行)
.text:000008E2 BNE loc_8BA
.text:000008E4 LDR R0, [SP,#0x238+INPUT] ; R0指向输入字符串
.text:000008E6 BLX strlen
.text:000008EA MOVW R2, #0xAAAB
.text:000008EE MOV R8, R0 ; R8=输入字符串长度
.text:000008F0 MOVT.W R2, #0xAAAA
.text:000008F4 LDRB.W R5, [R11,#3] ; R5=0x33
.text:000008F4 ; R11="36f36b3c-a03e-4996-8759-8408e626c215"
.text:000008F8 UMULL.W R0, R1, R8, R2 ; 这块貌似是编译器的除法优化
.text:000008FC LSRS R0, R1, #1
.text:000008FE ADD.W R1, R8, #3
.text:00000902 ADD.W R0, R0, R0,LSL#1
.text:00000906 SUB.W R0, R8, R0
.text:0000090A SUBS R0, R1, R0
.text:0000090C LSLS R0, R0, #3
.text:0000090E STR R0, [SP,#0x238+var_230]
.text:00000910 UMULL.W R0, R1, R0, R2
.text:00000914 ADD.W R0, R5, R1,LSR#2
.text:00000918 STR R0, [SP,#0x238+var_234]
.text:0000091A ADDS R0, #1 ; size
.text:0000091C BLX malloc ; 分配一块3Ch大小的内存
.text:00000920 CMP.W R8, #0 ; R8=输入字符串长度
.text:00000924 BEQ.W loc_A46
.text:00000928 MOV.W R10, #0
.text:0000092C MOVS R2, #0
.text:0000092E MOV.W R12, #0
.text:00000932 STR R5, [SP,#0x238+var_228]
.text:00000934 B loc_9A8
.text:00000936 ; ---------------------------------------------------------------------------
.text:00000936
.text:00000936 loc_936 ; CODE XREF: sub_784+288↓j
.text:00000936 CMP R2, #1 ; R2是明文字符索引
.text:00000938 ITT NE
.text:0000093A ADDNE.W R1, LR, #1
.text:0000093E CMPNE R1, R2
.text:00000940 BNE loc_96A
.text:00000942 LDR R1, [SP,#0x238+var_228] ; var_228保存的是一个偏移值,用于计算保存base64编码结果的最后一个字符的位置
.text:00000944 UXTB.W R6, R11 ; R11=明文字符的RC4加密结果,将其无符号扩展到32bit,赋值给R6
.text:00000948 ADDS R3, R1, R2 ; R2是明文字符索引
.text:0000094A LDRB R4, [R0,R3] ; 取出之前保存的“索引2”(只有高2位有效),保存到R4
.text:0000094C LDR R1, =(byte_4050 - 0x952)
.text:0000094E ADD R1, PC ; byte_4050 ; R1="!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
.text:0000094E ; 这个字符串作为base64字符表
.text:00000950 ORR.W R4, R4, R6,LSR#4 ; R6逻辑右移4位,再异或R4,得到最终的索引2,结果保存到R4
.text:00000950 ; ------------------------------------------------------
.text:00000950 ; base64(3个字节有24个bit,对应4个base64字符):
.text:00000950 ; M a n
.text:00000950 ; 01001101 01100001 01101110
.text:00000950 ; {010011}{01 0110}{0001 01}{101110}
.text:00000950 ; 索引1 索引2 索引3 索引4
.text:00000950 ; ------------------------------------------------------
.text:00000950 ; 根据4个索引,去查base64字符表
.text:00000954 LDRB R4, [R1,R4] ; 查表,取出索引2对应的base64字符
.text:00000956 STRB R4, [R0,R3] ; 保存索引2对应的base64字符
.text:00000958 ADDS R4, R0, R3 ; R4指向保存base64编码结果的最后一个字符位置
.text:0000095A MOVS R3, #0b111100
.text:0000095C AND.W R3, R3, R6,LSL#2 ; R6=明文字符的RC4加密结果,
.text:0000095C ; 逻辑左移2位,再与0x3C(00111100),清除高2位和低2位
.text:0000095C ; 相当于取R6的低4位,然后左移2位,结果保存到R3
.text:0000095C ; 即R3保存的是索引3的高4位
.text:00000960 ADDS R6, R2, #1 ; R2是明文字符索引
.text:00000962 CMP R6, R8 ; R8=明文字符串长度
.text:00000964 STRB R3, [R4,#1] ; 保存索引3(存储在base64编码结果的最后一个字符的后一个字节,目前只有高4位有效)
.text:00000966 BCC loc_A34
.text:00000968 B loc_A82
.text:0000096A ; ---------------------------------------------------------------------------
.text:0000096A
.text:0000096A loc_96A ; CODE XREF: sub_784+1BC↑j
.text:0000096A CMP R2, #2
.text:0000096C ITT NE
.text:0000096E ADDNE.W R1, LR, #2
.text:00000972 CMPNE R1, R2
.text:00000974 BNE loc_A34
.text:00000976 LDR R1, =(byte_4050 - 0x984)
.text:00000978 AND.W R4, R11, #0xC0 ; R11=明文字符的RC4加密结果,
.text:00000978 ; 和0xC0(11000000)与,清除低6位,保留高2位,保存到R4
.text:0000097C LDR.W LR, [SP,#0x238+var_228] ; var_228保存的是一个偏移值,用于计算保存base64编码结果的最后一个字符的位置
.text:00000980 ADD R1, PC ; byte_4050 ; R1="!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
.text:00000982 ADD.W R3, LR, R2 ; R2是明文字符索引
.text:00000986 ADD.W LR, LR, #1
.text:0000098A STR.W LR, [SP,#0x238+var_228]
.text:0000098E LDRB R6, [R0,R3] ; 取出之前保存的“索引3”(只有高4位有效),保存到R6
.text:00000990 ORR.W R6, R6, R4,LSR#6 ; 取R4的高2位,右移6位,与R6进行或操作,得到最终的索引3
.text:00000990 ; ------------------------------------------------------
.text:00000990 ; base64(3个字节有24个bit,对应4个base64字符):
.text:00000990 ; M a n
.text:00000990 ; 01001101 01100001 01101110
.text:00000990 ; {010011}{01 0110}{0001 01}{101110}
.text:00000990 ; 索引1 索引2 索引3 索引4
.text:00000990 ; ------------------------------------------------------
.text:00000990 ; 根据4个索引,去查base64字符表
.text:00000994 LDRB R6, [R1,R6] ; 查表,取出索引3对应的base64字符
.text:00000996 EOR.W R6, R6, #0xF ; 对于索引3对应的base64字符,还要再异或一下0xF
.text:0000099A STRB R6, [R0,R3] ; 保存索引3对应的base64字符
.text:0000099C AND.W R6, R11, #0x3F ; R11=明文字符的RC4加密结果,
.text:0000099C ; 和0x3F(00111111)与,清除高2位,保留低6位,得到索引4,保存到R6
.text:000009A0 ADD R3, R0 ; R3指向保存base64编码结果的最后一个字符位置
.text:000009A2 LDRB R1, [R1,R6] ; R6是索引4,查表,取出索引4对应的base64字符
.text:000009A4 STRB R1, [R3,#1] ; 保存索引4对应的base64字符
.text:000009A6 B loc_A34
.text:000009A8 ; ---------------------------------------------------------------------------
.text:000009A8
.text:000009A8 loc_9A8 ; CODE XREF: sub_784+1B0↑j
.text:000009A8 ; sub_784+2B4↓j
.text:000009A8 ADD.W R1, R10, #1 ; 接下来这块代码,就是对输入字符做RC4加密
.text:000009A8 ; R10即如下产生密钥流算法中的i,R1=i+1(对应第3行)
.text:000009A8 ; (1) i,j=0;
.text:000009A8 ; (2) for r=0 to len do //len为明文长度
.text:000009A8 ; (3) i=(i+1) mod 256;
.text:000009A8 ; (4) j=(j+S[i]) mod 256;
.text:000009A8 ; (5) swap(S[i],S[j]);
.text:000009A8 ; (6) t=(S[i]+S[j]) mod 256;
.text:000009A8 ; (7) k[r]=S[t];
.text:000009AC CMP R2, #0 ; R2是明文字符索引
.text:000009AE MOV.W R3, R1,ASR#31 ; 计算"R1 mod 256",结果保存到R10,即R10=(i+1) mod 256;(对应第3行)
.text:000009B2 ADD.W R3, R1, R3,LSR#24
.text:000009B6 BIC.W R3, R3, #0xFF
.text:000009BA SUB.W R10, R1, R3
.text:000009BE LDRB.W R1, [R9,R10] ; R9指向状态向量S,取出S[R10](对应第4行中的S[i])
.text:000009C2 ADD.W R3, R12, R1 ; R12即如上产生密钥流算法中的j,即R3=j+S[i](对应第4行)
.text:000009C6 MOV.W R4, R3,ASR#31 ; 计算"R3 mod 256",结果保存到R12(对应第4行)
.text:000009CA ADD.W R4, R3, R4,LSR#24
.text:000009CE BIC.W R4, R4, #0xFF
.text:000009D2 SUB.W R12, R3, R4 ; 即R12=(j+S[i]) mod 256(对应第4行)
.text:000009D6 LDRB.W R3, [R9,R12] ; R9指向状态向量S,从S中取出索引S[R12],即S[j](对应第5行)
.text:000009DA STRB.W R3, [R9,R10] ; S[i]=S[j](对应第5行)
.text:000009DE STRB.W R1, [R9,R12] ; S[j]=S[i](对应第5行)
.text:000009E2 LDRB.W R4, [R9,R10] ; R4=S[j](对应第6行)
.text:000009E6 LDR R3, [SP,#0x238+INPUT] ; R3指向明文字符串
.text:000009E8 ADD R1, R4 ; R1=S[i]+S[j](对应第6行)
.text:000009EA LDRB R3, [R3,R2] ; 取出一个明文字符
.text:000009EC UXTB R1, R1 ; 利用UXTB做mod256运算,即R1=(S[i]+S[j]) mod 256;(对应第6行)
.text:000009EE LDRB.W R1, [R9,R1] ; R1=S[t],即取出密钥流中的一个密钥字节(对应第7行)
.text:000009F2 EOR.W R11, R1, R3 ; R11=R1^R3,即该明文字符的RC4加密结果
.text:000009F6 BEQ loc_A0E
.text:000009F8 MOV R1, #0xAAAAAAAB
.text:00000A00 UMULL.W R1, R3, R2, R1 ; R2是明文字符索引,这块的乘法可能是编译器做的除法优化
.text:00000A04 LSRS R1, R3, #1
.text:00000A06 ADD.W LR, R1, R1,LSL#1
.text:00000A0A CMP LR, R2
.text:00000A0C BNE loc_936
.text:00000A0E
.text:00000A0E loc_A0E ; CODE XREF: sub_784+272↑j
.text:00000A0E LDR R1, =(byte_4050 - 0xA1C)
.text:00000A10 UXTB.W R3, R11 ; R11是明文字符的RC4加密结果,赋给R3
.text:00000A14 LSRS R4, R3, #2 ; R3逻辑右移2位,结果保存到R4,得到索引1
.text:00000A14 ; ------------------------------------------------------
.text:00000A14 ; base64(3个字节有24个bit,对应4个base64字符):
.text:00000A14 ; M a n
.text:00000A14 ; 01001101 01100001 01101110
.text:00000A14 ; {010011}{01 0110}{0001 01}{101110}
.text:00000A14 ; 索引1 索引2 索引3 索引4
.text:00000A14 ; ------------------------------------------------------
.text:00000A14 ; 根据4个索引,去查base64字符表
.text:00000A16 LDR R6, [SP,#0x238+var_228] ; var_228保存的是一个偏移值,用于计算保存base64编码结果的最后一个字符的位置
.text:00000A18 ADD R1, PC ; byte_4050 ; R1="!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';"
.text:00000A1A ADD R6, R2 ; R2是明文字符索引
.text:00000A1C LDRB R4, [R1,R4] ; 查表,取出索引1对应的base64字符
.text:00000A1E EOR.W R4, R4, #7 ; 对于索引1对应的base64字符,还要再异或一下0x7
.text:00000A22 STRB R4, [R0,R6] ; 保存索引1对应的base64字符
.text:00000A24 ADDS R4, R0, R6 ; R4指向保存base64编码结果的最后一个字符位置
.text:00000A26 MOVS R6, #0x30 ; '0'
.text:00000A28 AND.W R3, R6, R3,LSL#4 ; R3是明文字符的RC4加密结果,先逻辑左移4位,再异或00110000。
.text:00000A28 ; 即取R3的低2位,然后左移4位,得到索引2的高2位
.text:00000A2C ADDS R6, R2, #1 ; R2是明文字符索引,加1赋给R6
.text:00000A2E STRB R3, [R4,#1] ; 保存索引2(存储在base64编码结果的最后一个字符的后一个字节,目前只有高2位有效)
.text:00000A30 CMP R6, R8 ; R8=明文字符串长度
.text:00000A32 BCS loc_A3C ; 如果R6>=R8,跳转到loc_A3C
.text:00000A34
.text:00000A34 loc_A34 ; CODE XREF: sub_784+1E2↑j
.text:00000A34 ; sub_784+1F0↑j ...
.text:00000A34 ADDS R2, #1 ; R2+=1
.text:00000A36 CMP R2, R8 ; R2=明文字符串索引,R8=明文字符串长度
.text:00000A38 BCC loc_9A8 ; 如果R2<R8,跳转到loc_9A8
.text:00000A3A B loc_A46
.text:00000A3C ; ---------------------------------------------------------------------------
.text:00000A3C
.text:00000A3C loc_A3C ; CODE XREF: sub_784+2AE↑j
.text:00000A3C LDRB R1, [R1,R3]
.text:00000A3E MOVW R2, #0x3B3B ; 在最后补';;',相当于标准base64的'=='
.text:00000A3E ; 因为最后一个base索引缺4bit,需要补两个'='
.text:00000A42 STRH R2, [R4,#2]
.text:00000A44
.text:00000A44 loc_A44 ; CODE XREF: sub_784+304↓j
.text:00000A44 STRB R1, [R4,#1] ; 保存最后一个base64字符
.text:00000A46
.text:00000A46 loc_A46 ; CODE XREF: sub_784+1A0↑j
.text:00000A46 ; sub_784+2B6↑j
.text:00000A46 LDR R1, [SP,#0x238+var_230]
.text:00000A48 CBZ R1, loc_A66
.text:00000A4A LDR R2, =(a98gaLTnFjJG - 0xA54)
.text:00000A4C MOVS R1, #1 ; R1=1
.text:00000A4E LDR R4, [SP,#0x238+var_234]
.text:00000A50 ADD R2, PC ; " {9*8ga*l!Tn?@#fj'j$\\g;;" ; R2=" {9*8ga*l!Tn?@#fj'j$\\g;;"
.text:00000A52
.text:00000A52 loc_A52 ; CODE XREF: sub_784+2DE↓j
.text:00000A52 LDRB R3, [R0,R5] ; 下面是一个循环,比较最后的base64字符串和" {9*8ga*l!Tn?@#fj'j$\\g;;"
.text:00000A54 ADDS R5, #1
.text:00000A56 LDRB.W R6, [R2],#1 ; " {9*8ga*l!Tn?@#fj'j$\\g;;"
.text:00000A5A CMP R6, R3
.text:00000A5C IT NE
.text:00000A5E MOVNE R1, #0 ; 如果不相等,R0=0(结果失败)
.text:00000A60 CMP R5, R4
.text:00000A62 BCC loc_A52
.text:00000A64 B loc_A68
.text:00000A66 ; ---------------------------------------------------------------------------
.text:00000A66
.text:00000A66 loc_A66 ; CODE XREF: sub_784+2C4↑j
.text:00000A66 MOVS R1, #1
.text:00000A68
.text:00000A68 loc_A68 ; CODE XREF: sub_784+2E0↑j
.text:00000A68 LDR R0, [SP,#0x238+var_20] ; 后面是堆栈检查,不用看了
.text:00000A6A LDR R2, [SP,#0x238+var_22C]
.text:00000A6C LDR R2, [R2]
.text:00000A6E SUBS R0, R2, R0
.text:00000A70 ITTTT EQ
.text:00000A72 UXTBEQ R0, R1
.text:00000A74 ADDEQ.W SP, SP, #0x21C
.text:00000A78 POPEQ.W {R8-R11}
.text:00000A7C POPEQ {R4-R7,PC}
.text:00000A7E BLX __stack_chk_fail
.text:00000A82 ; ---------------------------------------------------------------------------
.text:00000A82
.text:00000A82 loc_A82 ; CODE XREF: sub_784+1E4↑j
.text:00000A82 LDRB R1, [R1,R3] ; 查表取出最后一个base64字符(最后一个base64索引可能不足6位)
.text:00000A84 MOVS R2, #0x34 ; '4'
.text:00000A86 STRB R2, [R4,#2] ; 在base64编码结果的最后加个'4'。
.text:00000A86 ; R4目前指向保存base64编码结果的倒数第2个字符位置
.text:00000A88 B loc_A44
.text:00000A88 ; End of function sub_784
把sub_784函数分析清楚之后,就可以写出破解脚本了:
[Python] 纯文本查看 复制代码 # 最后的结果是base64字符串:" {9*8ga*l!Tn?@#fj'j$\\g;;"
str_result = " {9*8ga*l!Tn?@#fj'j$\\g;;"
print 'str_result=' + str_result
# 作者在base64时,将索引1和索引3对应的字符分别异或了0x7和0xf
# 所以先将base64字符串还原回异或之前的样子
# 最后的两个';;',是后补的垫字符,相当于标准base64的'==',不参与异或还原
str_base64 = ''
for i in range(len(str_result)):
if str_result[i] == ';':
str_base64 += ';'
elif i % 4 == 0:
str_base64 += chr(ord(str_result[i]) ^ 0x7)
elif i % 4 == 2:
str_base64 += chr(ord(str_result[i]) ^ 0xf)
else:
str_base64 += str_result[i]
print 'str_base64=' + str_base64
# 作者的base64字符表是非标准的,先将str_base64转换回标准base64字符串
base64_chars = "!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\\';"
base64_chars_std = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
tab = string.maketrans(base64_chars, base64_chars_std)
str_base64_std = str_base64.translate(tab)
print 'str_base64_std=' + str_base64_std
# 利用标准的base64算法进行解码
str_base64_decode = str_base64_std.decode('base64')
#print_hex(str_base64_decode) # fd 1e 8a 4e 09 ca 90 03 e7 f1 85 9f 9b f7 83 3e
# rc4状态向量S
rc4_S = bytearray('D7DF02D4FE6F533C256C999706568FDE'
'40116407361570CA18177D6ADB133037'
'2960E123288A508CAC2F8820270F7C52'
'A2ABFCA1CC21141FC2B28B2CB03A6646'
'3DBB42A50C7522D8C3761E8374F0F61C'
'26D14F0BFF4C4DC187035AEEA45D9EF4'
'C80D62633E447BA368321BAA2D05F3F7'
'166194E0D0D3986978E90A65918E3585'
'7A5186103F7F82DDB51A95E743FD9B24'
'45EF925CE496A99C55899AEAF9905FB8'
'0484CF679300A639A84E59316BAD5E5B'
'77B154DC3841B6479F73BAF8AEC4BE34'
'014B2A8DBDC5C6E8AFC9F5CBFBCD79CE'
'1271D2FA09D5BC581980DA491DE62EE3'
'7EB73BB3A0B9E5576ED908EBC7ED81F1'
'F2BFC0A74AD62BB4729D0E6DEC48E233'.decode("hex"))
#print_hex2(rc4_S)
rc4_KEY = '36f36b3c-a03e-4996-8759-8408e626c215'
# rc4临时向量T
rc4_T = []
for i in range(256):
rc4_T += rc4_KEY[i % len(rc4_KEY)]
#print rc4_T
# 重新排列状态向量S
j = 0
for i in range(256):
j = (j + rc4_S[i] + ord(rc4_T[i])) % 256
rc4_S[i], rc4_S[j] = rc4_S[j], rc4_S[i]
print_hex2(rc4_S) # f0 37 e1 9b 2a 15 17 9f d7 58 4d 6e 33 a0 39 ae
# 利用rc4解密回应该输入的字符串
str_input = ''
i = j = 0
for e in str_base64_decode:
i = (i + 1) % 256
j = (j + rc4_S[i]) %256
rc4_S[i], rc4_S[j] = rc4_S[j], rc4_S[i]
t = (rc4_S[i] + rc4_S[j]) % 256;
str_input += chr(ord(e) ^ rc4_S[t])
print 'str_input=' + str_input
运行得到:fu0kzHp2aqtZAuY6
文/十八垧 |