【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, ...
随机推荐
- [转帖]xargs详解
https://www.cnblogs.com/xiaofeng666/p/10800939.html xargs与find经常结合来进行文件操作,平时删日志的时候只是习惯的去删除,比如 # find ...
- [转帖]Linux下strace调试系统应用参数总结(附实例操作讲解)
文章目录 一.简介 二.常用参数详解 三.实例详解 3.1 跟踪具体进程 3.2 监控具体程序执行过程 四.其他相关知识链接 一.简介 strace命令是一个集诊断.调试.统计与一体的Linux 用户 ...
- [转帖]鹅厂微服务发现与治理巨作PolarisMesh实践-上
文章目录 概述 定义 核心功能 组件和生态 特色亮点 解决哪些问题 官方性能数据 架构原理 资源模型 服务治理 基本原理 服务注册 服务发现 安装 部署架构 集群安装 SpringCloud应用接入 ...
- [转帖]RPC 框架架构设计
github地址:https://github.com/xiaojiesir/mini-rpc RPC 又称远程过程调用(Remote Procedure Call),用于解决分布式系统中服务之间的调 ...
- rpm包方式安装oracle21c
下载相关依赖包 https://yum.oracle.com/repo/OracleLinux/OL8/appstream/x86_64/index.htmlhttps://www.oracle.co ...
- 【如何提高IT运维效率】深度解读京东云基于NLP的运维日志异常检测AIOps落地实践
作者:京东科技 张宪波.张静.李东江 基于NLP技术对运维日志聚类,从日志角度快速发现线上业务问题 日志在IT行业中被广泛使用,日志的异常检测对于识别系统的运行状态至关重要.解决这一问题的传统方法需 ...
- echarts 设置legend样式
设置legend样式 legend: { x: 'center', data: ['班车', '包车'], icon: "circle", // 这个字段控制形状 类型包括 cir ...
- 【一个小发现】VictoriaMetrics 中 vmselect 的 `-search.denyPartialResponse` 选项不应该开启
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 一直以为vmselect 的 -search.denyPa ...
- 从零开始配置 vim(17)——快捷键提示
之前我们定义了各种各样的快捷键,有为了增强功能自定义的,有针对插件的.数量一多有的时候就不那么容易记忆了.要是每次要去配置文件找我定义了哪些快捷键肯定会影响使用的. 本篇将要介绍一个插件,它是快捷键的 ...
- 【5】数据可视化pygal,画出美观的图表
相关文章: 全网最详细超长python学习笔记.14章节知识点很全面十分详细,快速入门,只用看这一篇你就学会了! [1]windows系统如何安装后缀是whl的python库 [2]超级详细Pytho ...