【Android 逆向】看雪题目:找出flag 如果输入正确则屏幕上的 hello会变成success
1. apk 安装到手机,只有一个输入框,随便输入点什么,提示error
2. apk拖入到jadx中
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里有问题
2. so拖入IDA中,发现了一个函数.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层方法的行为
参考: https://github.com/asLody/SandHook
那么可以推测 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
【Android 逆向】看雪题目:找出flag 如果输入正确则屏幕上的 hello会变成success的更多相关文章
- 《第一行代码 android》 读书笔记:找出当前界面对应的Activity
在android开发中找出当前界面对应的Activity,步骤如下: 新建一个BaseActivity继承自Activity,然后在BaseActivity中重写onCreate()方法,通过getC ...
- sar 找出系统瓶颈的利器 目前Linux上最为全面的系统性能分析工具之一 直接 sar -dur 1 30 即可看内存 CPU和IO占用
12. sar 找出系统瓶颈的利器 sar是System Activity Reporter(系统活动情况报告)的缩写.sar工具将对系统当前的状态进行取样,然后通过计算数据和比例来表达系统的当前运行 ...
- 2019看雪CTF 晋级赛Q2第四题wp
上次参加2019看雪CTF 晋级赛Q2卡在了这道题上,虽然逆出算法,但是方程不会解,哈哈哈哈,果然数学知识很重要呀,现在记录一下. 首先根据关键信息,根据错误提示字符串定位到这里: 1 int __t ...
- 如何在 Linux 中找出最近或今天被修改的文件
1. 使用 ls 命令,只列出你的 home 文件夹中今天的文件. ls -al --time-style=+%D | grep `date +%D` 其中: -a- 列出所有文件,包括隐藏文件 -l ...
- python经典算法题目:找出这两个有序数组的中位数
题目:找出这两个有序数组的中位数 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以 ...
- 一起来刷《剑指Offer》-- 题目一:找出数组中重复的数字(Python多种方法实现)
数组中重复的数字 最近在复习算法和数据结构(基于Python实现),然后看了Python的各种"序列"--比如列表List.元组Tuple和字符串String,后期会写一篇博客介绍 ...
- python基础练习题(题目 两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单)
day14 --------------------------------------------------------------- 实例022:比赛对手 题目 两个乒乓球队进行比赛,各出三人. ...
- Android系统,动态找出一个包下所有的类
最近在写一个android应用,由于针对不同机型功能很不同,为了隔离变化,希望将各项功能插件化,通过编译开关来控制不同版本的功能,而不是在代码中通过逻辑来判断. 我想了一个办法,用表驱动的方法,结合插 ...
- c语言题目:找出一个二维数组的“鞍点”,即该位置上的元素在该行上最大,在该列上最小。也可能没有鞍点
//题目:找出一个二维数组的“鞍点”,即该位置上的元素在该行上最大,在该列上最小.也可能没有鞍点. // #include "stdio.h" #include <stdli ...
- 面试- 阿里-. 大数据题目- 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
假如每个url大小为10bytes,那么可以估计每个文件的大小为50G×64=320G,远远大于内存限制的4G,所以不可能将其完全加载到内存中处理,可以采用分治的思想来解决. Step1:遍历文件a, ...
随机推荐
- [转帖]【基础】HTTP、TCP/IP 协议的原理及应用
https://juejin.cn/post/6844903938232156167 前言 本文将持续记录笔者在学习过程中掌握的一些 HTTP .TCP/IP 的原理,以及这些网络通信技术的一些应用场 ...
- MySQL数据库页存储结构学习与了解
MySQL数据库页存储结构学习与了解 背景 MySQL总是出现奇奇怪怪的问题. 想着自己能够学习与提高一下. 最近看了很多文档.关于MySQL数据库相关的. 想着总结和提炼一下, 希望能够给未来的工作 ...
- [转帖]一次python服务的性能优化经历
https://juejin.cn/post/7208708762265616421 问题背景: 在我们的业务中,有一些推荐的场景会需要走到集团研究院的算法推荐服务,对一些用户进行个性化的课件推荐 ...
- buildkit 官网 service 资料
[Unit] Description=BuildKit Requires=buildkit.socket After=buildkit.socket Documentation=htt ...
- 浅析大促备战过程中出现的fullGc,我们能做什么?
作者:京东科技 白洋 前言: 背景: 为应对618.双11大促,消费金融侧会根据零售侧大促节奏进行整体系统备战.对核心流量入口承载的系统进行加固优化,排除系统风险,保证大促期间系统稳定. 由于大促期间 ...
- Fabric区块链浏览器(1)
本文是区块链浏览器系列的第三篇,本文介绍区块链浏览器的主体部分,即区块数据的解析. 这一版本的区块链浏览器是基于gin实现的,只提供三种接口: /block/upload:POST,上传Protobu ...
- Go 循环之for循环,仅此一种
Go 循环之for循环,仅此一种 目录 Go 循环之for循环,仅此一种 一.for 循环介绍 二.for 循环结构 2.1 基本语法结构 2.2 省略初始值 2.3 省略初始语句和结束语句 2.4 ...
- 从零开始配置vim(32)——最后再说两句
很抱歉我决定结束这个系列的内容了.原本我打算介绍markdown.orgmode相关的配置,甚至还打算介绍如何在vim 中使用 emacs 的 org-agenda 来进行日常的任务管理.但是出于一些 ...
- springboot整合nacos的入门Demo
Nacos介绍 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现.配置管理和服务 ...
- springboot项目导入外部jar包的bean的几种方式
背景 公司封装了基础包和日志包,将公共的配置抽取出来,供所有项目使用,因此就需要考虑,怎么引入外部jar包的Bean实例: 思考 因为公司的jar包就是普通的jar,不支持springboot的自动配 ...