1. dex处理逻辑
encode1是base64, encode2和encode3比较简单, 略过
sn = encode3(sn) + encode2(sn) + encode1(sn)
public class Main extends ac {
...
protected void onCreate() {
super.onCreate();
...
// 这个不懂为什么没生效, 生效的是基类那个
this.button.setOnClickListener(new View$OnClickListener() {
public void onClick(View v) {
String v2 = Main.this.editText.getText().toString().trim();
if(ua.check(ca.encode1(v2) + ba.encode2(v2) + ab.encode3(v2)) == 1) {
bc.showresult(Main.this.tv, true);
}
else {
bc.showresult(Main.this.tv, false);
}
}
});
}
}
public class uv extends cc {
...
protected void onCreate(Bundle savedInstanceState) {
this.button.setOnClickListener(new View$OnClickListener() {
public void onClick(View v) {
String v2 = uv.this.editText.getText().toString().trim();
if(ua.check(ab.encode3(v2) + ba.encode2(v2) + ca.encode1(v2)) == 1) {
bc.showresult(uv.this.tv, true);
}
else {
bc.showresult(uv.this.tv, false);
}
}
});
}
}
public class ua {
static {
System.loadLibrary("enjoy");
}
...
public static native int check(ua this, String arg1) {
}
}
2. so处理逻辑
JNI_OnLoad中有两个校验和反调试的地方, 静态分析的时候直接nop掉, 安装完后再替换掉就可以正常调试了
(有检测dex signature和TracerPid什么的)
.text:00001F4C BL check_dex
.text:00001F50 BL check_thread
so中的check函数
.text:00001F38 MOVS R3, #JNINativeInterface.RegisterNatives
.text:00001F3C LDR R5, [R2,R3]
.text:00001F3E LDR R2, =(off_5E54 - 0x1F48)
.text:00001F40 MOVS R0, R4
.text:00001F42 MOVS R3, #1
.text:00001F44 ADD R2, PC ; off_5E54
.text:00001F46 BLX R5
.data:00005E54 off_5E54 JNINativeMethod <byte_5E60, aLjavaLangStrin, check+1>
len(sn)<=120, 原始sn长度范围(x+x+x/3*4 <= 120): 11 ~ 36
从结果来看原始sn长度是36, 但是我后面是从11开始穷举的, 浪费了大量的时间
.mytext:0000313E LDR R1, [R5]
.mytext:00003140 MOVS R3, #JNINativeInterface.GetStringUTFChars
.mytext:00003144 LDR R3, [R1,R3]
.mytext:00003146 MOVS R2, #0
.mytext:00003148 MOVS R1, R7
.mytext:0000314A MOVS R0, R5
.mytext:0000314C BLX R3
.mytext:0000314E MOVS R6, R0
.mytext:00003150 BL j_j_strlen_0
.mytext:00003154 STR R4, [SP,#0x50+var_4C]
.mytext:00003156 MOVS R1, #0 ; c
.mytext:00003158 CMP R0, #120
.mytext:0000315A BGT loc_3194
.mytext:0000315C ADD R4, SP, #0x50+s
.mytext:0000315E MOVS R2, #37 ; n
.mytext:00003160 MOVS R0, R4 ; s
.mytext:00003162 BL j_j_memset_0
.mytext:00003166 MOVS R1, R6 ; src
.mytext:00003168 MOVS R2, #36 ; n
.mytext:0000316A MOVS R0, R4 ; dest
.mytext:0000316C BL j_j_memcpy_0
.mytext:00003170 LDR R2, [R5]
.mytext:00003172 MOVS R3, #JNINativeInterface.ReleaseStringUTFChars
.mytext:00003176 LDR R3, [R2,R3]
.mytext:00003178 MOVS R1, R7
.mytext:0000317A MOVS R2, R6
.mytext:0000317C MOVS R0, R5
.mytext:0000317E BLX R3
.mytext:00003180 MOVS R0, R4 ; s
.mytext:00003182 BL j_j_strlen_0
.mytext:00003186 MOVS R1, R0
.mytext:00003188 MOVS R0, R4
.mytext:0000318A BL check_sn
BYTE buf[40];
BYTE key1[8];
BYTE key2[16];
CopyMemory(buf, sn, 36);
FillMemory(buf+36, 0x04, 0x04);
des_enc(buf, sizeof(buf), key1); (这里des_set_key在处理PC2_Table的时候与标准有偏差)
CopyMemory(&key2[12], &buf[32], 4);
rc6_encrypt(buf, 32, key2, sizeof(key2)); (这个不常碰到, 跟了一遍)
memcmp(buf, expected, 32) == 0
rc6与标准的区别:
Q: 0x9e3779b9L => 0x61C88647L
处理前和处理后都进行了byteswap32
signed int __fastcall check_sn(const void *a1, size_t a2)
{
...
if ( a2 == 36 )
{
v6 = j_j_malloc(0x28u);
v7 = v6;
if ( v6 )
{
j_j_memcpy(v6, v3, v4);
v7[36] = 4;
v7[37] = 4;
v7[38] = 4;
v7[39] = 4;
do
{
v8 = g_key1[v2];
v9 = 0;
do
{
v17[8 * v2 + v9] = (v8 >> (7 - v9)) & 1;
++v9;
}
while ( v9 != 8 );
++v2;
}
while ( v2 != 8 );
des_set_key((int)v17);
v10 = 0;
do
{
v11 = &v7[v10];
j_j_memcpy(&dest, &v7[v10], 8u);
v15 = 0;
v16 = 0;
des_1840((int)&dest, (int)&v15);
v10 += 8;
j_j_memcpy(v11, &v15, 8u);
}
while ( v10 != 40 );
update_key2((int)g_key2, (int)&v15);
rc6_encrypt(v7, 0x20u, (int)g_key2, 16);
v12 = 0;
while ( (unsigned __int8)v7[v12] == byte_5D3D[v12] )
{
if ( ++v12 == 32 )
{
result = 1;
goto LABEL_14;
}
}
}
}
result = 0;
...
}
3. 穷举
sn以 kxuectf{ 开头, 以 } 结尾
这里直接按sn长度为36位来穷举了
void Des_SetKey(const char Key[8])
{
static bool K[64];
static bool KL[56];
static bool KR[56];
ByteToBit(K, Key, 64);
Transform(K, K, PC1_Table, 56);
CopyMemory(&KL[0], &K[0], 28);
CopyMemory(&KL[28], &K[0], 28);
CopyMemory(&KR[0], &K[28], 28);
CopyMemory(&KR[28], &K[28], 28);
int offset = 0;
for(int i=0; i<16; i++) {
offset += LOOP_Table[i];
bool Tmp[256];
for(int n=0; n<48; n++) {
if (PC2_Table[n] >= 28)
{
Tmp[n] = KR[PC2_Table[n]-1-28+offset];
} else
{
Tmp[n] = KL[PC2_Table[n]-1+offset];
}
}
memcpy(SubKey[i], Tmp, 48);
}
}
void test_sn36()
{
const char *charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789{}";
const char *charset2 = "^_`mEJCTNKOGWRSFYVLZQAH[\\]upibejctnkogwrsfyvlzqahmdxKOGWRSFYVLui";
const char *charset3 = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm76543210?>}{";
int sn_len = 36;
int indices[36];
char sn[40] = "";
BYTE key1[8] =
{
0xFD, 0xB4, 0x68, 0x54, 0x08, 0xCD, 0x56, 0x4E
};
BYTE key2[16] =
{
0x65, 0x48, 0x32, 0xEF, 0xBA, 0xCD, 0x56, 0x4E, 0x0F, 0x9B, 0x1D, 0x27, 0x00, 0x00, 0x00, 0x00
};
CopyMemory(sn, "kxuectf{", 8);
string s1 = encode3((PBYTE)sn, 8);
for (int k1 = 0; k1 < 64; k1++)
{
for (int k2 = 0; k2 < 64; k2++)
{
for (int k3 = 0; k3 < 64; k3++)
{
BYTE expected[32] =
{
0x42, 0xD3, 0xC3, 0xC2, 0xF1, 0x2A, 0xE9, 0x2D, 0x66, 0xC9, 0x28, 0x22, 0x2C, 0xEB, 0x54, 0x0E,
0x94, 0x07, 0xE5, 0x77, 0x4A, 0x92, 0xB7, 0x92, 0x2E, 0x5D, 0xFD, 0xF0, 0xF3, 0x54, 0x9F, 0xC6
};
BYTE buf1[8];
buf1[0] = charset3[k1];
buf1[1] = charset3[k2];
buf1[2] = charset3[k3];
buf1[3] = charset3[63];
FillMemory(buf1+4, 4, 0x04);
des_encrypt(buf1, 8, key1);
CopyMemory(&key2[12], buf1, 4);
rc6_decrypt(expected, sizeof(expected), key2);
des_decrypt(expected, sizeof(expected), key1);
if (memcmp(expected, s1.c_str(), 8) == 0)
{
CopyMemory(sn, expected, 32);
sn[32] = charset3[k1];
sn[33] = charset3[k2];
sn[34] = charset3[k3];
sn[35] = charset3[63];
sn[sn_len] = 0;
conver_charset(sn, sn_len, indices, charset, charset3);
printf("%s\n", sn);
}
}
}
}
}
>>> kxuectf{D3crypted1sV3rylntere5tin91}
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法