public class MainActivity extends AppCompatActivity {
static int n = 0;
public static byte[] su;
public native String stringFromJNI();
static {
System.loadLibrary("native-lib");
su = new byte[]{-66, -81, 25, -66, 122, -8, 42, -10, 78, -117, 104, -17, 118, -27, 40, -80, -20, 40, -60, -80, -11, -5, 75, 5, 100, 47, -48, 42, -119, -60, -66, 113};
}
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
Button aa = (Button) findViewById(R.id.button);
aa.setOnClickListener(new View.OnClickListener() { // from class: com.r0ysue.test.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
TextView ss = (TextView) MainActivity.this.findViewById(R.id.editTextTextPassword);
String mingwen = MainActivity.string2hex(ss.getText().toString());
if (mingwen.length() != 26) {
tv.setText("error");
return;
}
String ming1 = mingwen.substring(0, 8);
String ming2 = mingwen.substring(8, 16);
String ming3 = mingwen.substring(16, 24);
String ming4 = mingwen.substring(24, 26);
int a1 = Integer.parseInt(ming1, 16);
int a2 = Integer.parseInt(ming2, 16);
int a3 = Integer.parseInt(ming3, 16);
int a4 = Integer.parseInt(ming4, 16);
if (MainActivity.this.encrypt(a1, a2, a3, a4) == 1) {
tv.setText("success");
} else {
tv.setText("error");
}
}
});
}
public int encrypt(int a, int b, int c, int d) {
try {
String content = Integer.toString(a, 16) + Integer.toString(b, 16) + Integer.toString(c, 16) + Integer.toString(d, 16);
String password = "r0ysue-yyds";
while (password.length() < 16) {
password = password + "0";
}
SecretKeySpec secretKeySpec = new SecretKeySpec(password.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(1, secretKeySpec);
byte[] result = cipher.doFinal(byteContent);
int n2 = 0;
while (true) {
byte[] bArr = su;
if (n2 >= bArr.length) {
return 1;
}
if (result[n2] != bArr[n2]) {
return 0;
}
n2++;
}
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public static String string2hex(String string) {
StringBuffer unicode = new StringBuffer();
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
unicode.append(Integer.toHexString(c));
}
return unicode.toString();
}
}
看起来很简单,java写个解密过程,填入密码,还是不对。
看来so里有问题
.init_proc
查了下,是最早运行的一个函数unsigned __int64 init_proc()
{
......
v10 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
strcpy(v9, "com/r0ysue/test/MainActivity");
for ( i = 0; i < (unsigned __int64)__strlen_chk(v9, 0x1Du); ++i )
byte_54A0[i] = v9[i] ^ 0x3C;
strcpy((char *)&v8, "encrypt");
for ( j = 0; j < (unsigned __int64)__strlen_chk((const char *)&v8, 8u); ++j )
byte_54D2[j] = v9[j - 8] ^ 0x3D;
strcpy((char *)&v7, "(IIII)I");
for ( k = 0; k < (unsigned __int64)__strlen_chk((const char *)&v7, 8u); ++k )
byte_5504[k] = *((_BYTE *)&v7 + k) ^ 0x3F;
for ( m = 0; m <= (unsigned __int64)__strlen_chk(v9, 0x1Du); ++m )
{
byte_54A0[m] ^= 0x3Cu;
if ( m == (unsigned __int64)__strlen_chk(v9, 0x1Du) )
byte_54A0[m] = 0;
}
for ( n = 0; n < (unsigned __int64)__strlen_chk((const char *)&v8, 8u); ++n )
{
byte_54D2[n] ^= 0x3Du;
if ( n == (unsigned __int64)__strlen_chk(v9, 0x1Du) )
byte_54D2[n] = 0;
}
for ( ii = 0; ; ++ii )
{
result = __strlen_chk((const char *)&v7, 8u);
if ( ii >= result )
break;
byte_5504[ii] ^= 0x3Fu;
if ( ii == (unsigned __int64)__strlen_chk(v9, 0x1Du) )
byte_5504[ii] = 0;
}
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
return result;
}
可以看到貌似在搜集java层 encrypt 方法的信息
byte_54A0 v9 MainActivity
byte_54D2 v8 encrypt
byte_5504 v7 (IIII)I
点进Java_com_r0ysue_test_MainActivity_stringFromJNI看看
__int64 __fastcall Java_com_r0ysue_test_MainActivity_stringFromJNI(JNIEnv *env)
{
......
v11 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
pthread_create_sym = find_sym_sub_1604((__int64)"/system/lib64/libc.so", (__int64)"pthread_create");
v6 = 1;
__android_log_print(6, "r0ysue", "%x", pthread_create_sym);
v5 = fopen("/proc/self/maps", "r");
while ( __fgets_chk(v10, 1024LL, v5, 1024LL) )
{
if ( strstr(v10, "libc.so") )
{
if ( v6 == 2 )
{
v1 = strtok(v10, "-");
v7 = strtoul(v1, 0LL, 16);
}
else
{
strtok(v10, "-");
}
v2 = strtok(0LL, " ");
strtoul(v2, 0LL, 16);
++v6;
}
}
ret_chars = sub_20C0(pthread_create_sym, v7);
sub_1D40((__int64)env, (__int64)sub_272C, (__int64)byte_54A0, (__int64)byte_54D2, (__int64)byte_5504, 0);
result = sub_3104((__int64)env, (__int64)ret_chars);
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
return result;
}
这一行 sub_1D40((__int64)env, (__int64)sub_272C, (__int64)byte_54A0, (__int64)byte_54D2, (__int64)byte_5504, 0);
把上面的encrypt
的三个信息全取出来了,还传入了一个函数sub_272C
,点进去看看是在干啥
_QWORD *__fastcall sub_1D40(
JNIEnv *env,
__int64 t_func,
const char *class_name,
const char *method_name,
const char *sig_name,
int arg_num)
{
.......
v23 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
v20 = method_name;
v21 = sig_name;
v22 = t_func;
if ( !dword_5350 )
{
v12 = 1;
v11 = fopen("/proc/self/maps", "r");
while ( __fgets_chk(v19, 1024LL, v11, 1024LL) )
{
if ( strstr(v19, "libart.so") )
{
__android_log_print(6, "r0ysue", "%s", v19);
if ( v12 == 1 )
{
v6 = strtok(v19, "-");
libart_startp_qword_53F8 = strtoul(v6, 0LL, 16);
}
else
{
strtok(v19, "-");
}
v7 = strtok(0LL, " ");
strtoul(v7, 0LL, 16);
++v12;
}
}
++dword_5350;
}
v10 = find_sym_sub_1604((__int64)"/system/lib64/libart.so", (__int64)"art_quick_generic_jni_trampoline");
jclass_mainActivity = (__int64)find_class_sub_2040(env, class_name);
jmethodId = (_QWORD *)getMethodId_sub_2078(env, (void *)jclass_mainActivity, method_name, sig_name);
qword_5358[arg_num] = *(_QWORD *)((char *)jmethodId + 4);
qword_53A8[arg_num] = jmethodId[5];
*(_QWORD *)((char *)jmethodId + 4) ^= 0x80100uLL;// *(_DWORD *) (a1 + 4) & 0x80000 == 0为是否为native的标志
jmethodId[5] = libart_startp_qword_53F8 + v10 - 0x25000;// //需要-0x25000是因为libart.so的程序头在偏移为0x25000 , 这里没遍历偷懒了
jmethodId[4] = t_func;
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
return jmethodId;
}
看到了一个关键的字符串art_quick_generic_jni_trampoline
和下面修改jmethod 成员的操作,和 开源项目sandhook 十分相似,应该就是使用native方法去替换java层方法的行为
那么可以推测 t_func 即 sub_272C 就是替换用的native函数
bool __fastcall sub_272C(JNIEnv *env, jobject jobject, int a, int b, int c, int d)
{
if ( sub_2128(a, qword_5000, 101) == 0x384B099F681D8BA5LL )
{
if ( sub_2128(b, qword_5000, 101) == 0xCA6AFBB12E68AFF0LL )
return sub_2128(c, qword_5000, 101) == 0x910E2DB62AE48A6ELL
&& sub_2128(d, qword_5000, 101) == 0x1664C6F89BFD8183LL;
else
return 0;
}
else
{
return 0;
}
}
.data:0000000000005000 79 2D 65 75 73 79 30 72 qword_5000 DCQ 'r0ysue-y' ; DATA XREF: sub_272C+C↑o
.data:0000000000005000 ; sub_272C+4C↑r
.data:0000000000005008
从这里可以推测出sub_2128
是某种加密算法, 密钥为 r0ysue-y
,开始寻找算法特征
DES 算法 参考 https://www.cnblogs.com/zhangzixu/p/14471822.html
.data:0000000000005008 3A 32 2A 22 1A 12 0A 02 3C 34+ip_byte_5008 DCB 0x3A, 0x32, 0x2A, 0x22, 0x1A, 0x12, 0xA, 2, 0x3C, 0x34, 0x2C, 0x24, 0x1C, 0x14, 0xC, 4, 0x3E, 0x36
.data:0000000000005008 2C 24 1C 14 0C 04 3E 36 2E 26+ ; DATA XREF: sub_2128+B4↑o
.data:0000000000005008 1E 16 0E 06 40 38 30 28 20 18+DCB 0x2E, 0x26, 0x1E, 0x16, 0xE, 6, 0x40, 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 8, 0x39, 0x31, 0x29, 0x21
.data:0000000000005008 10 08 39 31 29 21 19 11 09 01+DCB 0x19, 0x11, 9, 1, 0x3B, 0x33, 0x2B, 0x23, 0x1B, 0x13, 0xB, 3, 0x3D, 0x35, 0x2D, 0x25, 0x1D, 0x15
.data:0000000000005008 3B 33 2B 23 1B 13 0B 03 3D 35+DCB 0xD, 5, 0x3F, 0x37, 0x2F, 0x27, 0x1F, 0x17, 0xF, 7
.data:0000000000005080 01 01 02 02 02 02 02 02 01 02+left_move_byte_5080 DCB 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
.data:0000000000005080 02 02 02 02 02 01
从算法中使用到的数组来看,这明显是一个DES加密算法
那么只要对 0x384B099F681D8BA5LL 0xCA6AFBB12E68AFF0LL 0x910E2DB62AE48A6ELL 0x1664C6F89BFD8183LL 进行解密即可得出
使用 https://gchq.github.io/CyberChef 进行解密操作得到....i-lo....ve-r....0ysu.......e
flag即为i-love-r0ysue