本帖最后由 Light紫星 于 2021-2-27 08:56 编辑
【2021春节】解题领红包之三
逆向总结本题所用到的工具:
Jadx1.2.0
FrIDA14.2.13
ida7.5.0
Python3.7.2
首先安装apk,打开后是这个界面:
随便输入flag,提示flag格式错误,请重试。拖入jadx,找到关键函数,如下:
[Java] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package p004cn.pojie52.cm01;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
public native boolean check(String str);
static {
System.loadLibrary( "native-lib" );
}
@Override
public void onCreate(Bundle bundle) {
super .onCreate(bundle);
setContentView(C0266R.layout.activity_main);
final EditText editText = (EditText) findViewById(C0266R.C0268id.flag);
findViewById(C0266R.C0268id.check).setOnClickListener( new View.OnClickListener() {
public void onClick(View view) {
String trim = editText.getText().toString().trim();
if (trim.length() != 30 ) {
Toast.makeText(MainActivity. this , "flag格式错误,请重试" , 0 ).show();
} else if (MainActivity. this .check(trim)) {
Toast.makeText(MainActivity. this , "恭喜你,验证正确!" , 0 ).show();
} else {
Toast.makeText(MainActivity. this , "flag错误,再接再厉" , 0 ).show();
}
}
});
}
}
|
这里判断了输入的长度是否为30位,然后进入了so验证。
下一步,把so拖入ida,直接定位到关键函数:Java_cn_pojie52_cm01_MainActivity_check
该函数内容如下:
[C] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | __int64 __fastcall Java_cn_pojie52_cm01_MainActivity_check(_JNIEnv *a1, __int64 a2, __int64 a3)
{
const char *v5;
size_t v6;
int v7;
__int64 v8;
_BYTE *v9;
int8x16_t v10;
int8x16_t v11;
int8x16_t v12;
int8x16_t v13;
int8x16_t v14;
int8x16_t v15;
__int64 v16;
unsigned int v17;
_BYTE v19[33];
int v20;
char v21;
char v22;
char v23;
char v24;
char dest[16];
__int128 v26;
__int128 v27;
__int128 v28;
__int64 v29;
v29 = *(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
if ( a1->functions->GetStringUTFLength(a1, a3) != 30 )
return 0;
v5 = a1->functions->GetStringUTFChars(a1, a3, 0LL);
v28 = 0u;
v27 = 0u;
v26 = 0u;
*dest = 0u;
v6 = strlen (v5);
strncpy (dest, v5, v6);
a1->functions->ReleaseStringUTFChars(a1, a3, v5);
v7 = strlen (dest);
sub_B90(dest, v7, "areyousure??????" );
v8 = strlen (dest);
v9 = sub_D90(dest, v8);
*v19 = unk_11A1;
*&v19[16] = unk_11B1;
*&v19[25] = unk_11BA;
v10.n128_u64[0] = 0xB2B2B2B2B2B2B2B2LL;
v10.n128_u64[1] = 0xB2B2B2B2B2B2B2B2LL;
v11.n128_u64[0] = 0xFEFEFEFEFEFEFEFELL;
v11.n128_u64[1] = 0xFEFEFEFEFEFEFEFELL;
v19[0] = 53;
v12 = veorq_s8(vaddq_s8(veorq_s8(vaddq_s8(*&v19[1], v10), xmmword_1130), xmmword_1140), v11);
v13.n128_u64[0] = 0x101010101010101LL;
v13.n128_u64[1] = 0x101010101010101LL;
v14.n128_u64[0] = 0x3E3E3E3E3E3E3E3ELL;
v14.n128_u64[1] = 0x3E3E3E3E3E3E3E3ELL;
*&v19[1] = vaddq_s8(
veorq_s8(vsubq_s8(v13, vorrq_s8(vshrq_n_u8(v12, 7uLL), vshlq_n_s8(v12, 1uLL))), xmmword_1150),
v14);
v20 = 1782990162;
v15 = veorq_s8(vaddq_s8(veorq_s8(vaddq_s8(*&v19[17], v10), xmmword_1160), xmmword_1170), v11);
v21 = ((1
- ((2 * ((((unk_11C6 - 78) ^ 0xB2) - 117) ^ 0xFE)) | ((((((unk_11C6 - 78) ^ 0xB2) - 117) ^ 0xFE) & 0x80) != 0))) ^ 0x25)
+ 62;
v16 = 0LL;
v22 = ((1
- ((2 * ((((unk_11C7 - 78) ^ 0xB1) - 118) ^ 0xFE)) | ((((((unk_11C7 - 78) ^ 0xB1) - 118) ^ 0xFE) & 0x80) != 0))) ^ 0x26)
+ 62;
*&v19[17] = vaddq_s8(
veorq_s8(vsubq_s8(v13, vorrq_s8(vshrq_n_u8(v15, 7uLL), vshlq_n_s8(v15, 1uLL))), xmmword_1180),
v14);
v23 = ((1
- ((2 * ((((unk_11C8 - 78) ^ 0xB0) - 119) ^ 0xFE)) | ((((((unk_11C8 - 78) ^ 0xB0) - 119) ^ 0xFE) & 0x80) != 0))) ^ 0x27)
+ 62;
v24 = ((1
- ((2 * ((((unk_11C9 - 78) ^ 0xBF) - 120) ^ 0xFE)) | ((((((unk_11C9 - 78) ^ 0xBF) - 120) ^ 0xFE) & 0x80) != 0))) ^ 0x28)
+ 62;
while ( v9[v16] == v19[v16] )
{
if ( v9[v16] )
{
if ( ++v16 != 41 )
continue ;
}
v17 = 1;
goto LABEL_9;
}
v17 = 0;
LABEL_9:
free (v9);
return v17;
}
|
大概看一下流程,先判断输入是否30位,然后把输入的数据传入sub_B90进行处理,再传入sub_D90处理一次,处理后的结果为v9,最后v9和v19进行比较。所以这里要先看一下sub_b90和sub_d90是干什么的,直接使用frida进行hook调用这两个函数,
frida代码如下:
[Python] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | import frida, sys
jscode =
def on_message(message, data):
if message[ 'type' ] = = 'send' :
print ( " {0}" . format (message[ 'payload' ]))
else :
print (message)
pass
device = frida.get_remote_device()
session = device.attach( 'cn.pojie52.cm01' )
script = session.create_script(jscode)
script.on( 'message' , on_message)
print ( ' Start attach' )
script.load()
sys.stdin.read()
|
结果如下:
然后我们先进入sub_b90看一下,
sub_b90函数内容如下:
[C] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | unsigned __int64 __fastcall sub_B90(_BYTE *a1, unsigned int a2, char *s)
{
unsigned __int64 result;
unsigned __int64 v7;
signed int v8;
int v9;
int v10;
int v11;
int v12;
signed int v13;
__int64 v14;
int v15;
int v16;
int v17;
int v18;
int v19;
__int128 v20[2];
__int128 v21[14];
__int64 v22;
v22 = *(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
result = strlen (s);
v20[0] = xmmword_11D0;
v20[1] = xmmword_11E0;
qmemcpy(v21, " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmno" , 80);
v21[5] = xmmword_1240;
v21[6] = xmmword_1250;
v21[7] = xmmword_1260;
v21[8] = xmmword_1270;
v21[9] = xmmword_1280;
v7 = 0LL;
v8 = 0;
v21[10] = xmmword_1290;
v21[11] = xmmword_12A0;
v21[12] = xmmword_12B0;
v21[13] = xmmword_12C0;
do
{
v9 = *(v20 + v7);
v10 = v8 + v9 + s[v7 - v7 / result * result];
v11 = v10 + 255;
if ( v10 >= 0 )
v11 = v10;
v8 = v10 - (v11 & 0xFFFFFF00);
*(v20 + v7++) = *(v20 + v8);
*(v20 + v8) = v9;
}
while ( v7 != 256 );
if ( a2 )
{
v12 = 0;
v13 = 0;
v14 = a2;
do
{
v15 = v12 + 1;
if ( v12 + 1 >= 0 )
v16 = v12 + 1;
else
v16 = v12 + 256;
v12 = v15 - (v16 & 0xFFFFFF00);
v17 = *(v20 + v12);
v18 = v13 + v17;
v19 = v18 + 255;
if ( v18 >= 0 )
v19 = v18;
v13 = v18 - (v19 & 0xFFFFFF00);
--v14;
*(v20 + v12) = *(v20 + v13);
*(v20 + v13) = v17;
*a1++ ^= *(v20 + (*(v20 + v12) + v17));
}
while ( v14 );
}
return result;
}
|
大概分析一下sub_b90,是根据传入的第三个参数s把v20进行了一个初始化,然后再把参数a1和v20进行了异或运算,主要看这个异或运算,先设想一下,如果是把a1进行了异或,那么得到的结果和a1之前的数据再异或就可以计算出异或的key,这里我们把它叫做xorkey,那么先看一下我们传入的参数,是30个1,也就是30个0x31 ,然后看结果,第一位是0xe0,0x31^0xe0 = 209,然后把参数改为30个2,即0x32,得出首位的结果是0xe3,0xe3^0x32结果也是209,证明我们的思路是正确的,然后依次求出所有的xorkey,
最后计算出的结果为:
xorkey = [209, 90, 6, 144, 68, 230, 199, 229, 222, 40, 247, 242, 102, 145, 200, 133, 66, 223, 249, 224, 130, 1, 43, 59, 56, 99, 55, 189, 46, 77]
接下来看sub_d90,咋一看返回值,全是字母,看起来有点像base64,于是用base64编码30个1进行测试,发现结果吻合,于是可以断定sub_d90是base64函数。
接下来,就可以写出通过v9求输入参数的函数:
[Python] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | import base64
xorkey = [ 209 , 90 , 6 , 144 , 68 , 230 , 199 , 229 , 222 , 40 , 247 , 242 , 102 , 145 , 200 , 133 , 66 , 223 , 249 , 224 , 130 , 1 , 43 , 59 , 56 , 99 , 55 , 189 , 46 , 77 ]
def sub_B90(data,l):
ret = []
for i in range (l):
ret.append(((data[i]))^xorkey[i])
s = ''
for i in ret:
s + = chr (i)
print (s)
return ret
def resv(data):
data = base64.b64decode(data)
t = sub_B90(data, len (data))
return (t)
|
调用resv即可计算出输入的参数。
这个时候我们发现,还有一个v19是我们不知道的,如果找到v19然后代入resv就能求出本题的结果!
根据ida的注释,我们知道v19是xsp+0h,而dest是xsp+38h,而dest又作为参数传入了sub_b90,这里我直接hooksub_b90,得到xsp,然后再在v19初始化结束之后输出xsp的值,即可得到v19,
这里的hook代码如下:
[Python] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import frida, sys
jscode =
def on_message(message, data):
if message[ 'type' ] = = 'send' :
print ( " {0}" . format (message[ 'payload' ]))
else :
print (message)
pass
device = frida.get_remote_device()
session = device.attach( 'cn.pojie52.cm01' )
script = session.create_script(jscode)
script.on( 'message' , on_message)
print ( ' Start attach' )
script.load()
sys.stdin.read()
|
随便输入一个30位的注册码,得到的结果如下:
看来这个字符串就是我们要的了。把这个字符串代入函数resv,即可求出本题的flag:
输入52pojieHappyChineseNewYear2021到输入框,点击验证按钮,提示成功!本题分析结束。
【2021春节】安卓中级题逆向总结_pdf.zip
(441.04 KB, 下载次数: 257)
|