1. 安装apk到手机

2. 随意输入账号和密码,点击register,报错crackme1:ERROR

3. 将apk拖入到jadx中进行观察

    public native String register(String str);

    static {
System.loadLibrary("native-lib");
} /* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
initViews();
} private void initViews() {
this.edt_code = (EditText) findViewById(R.id.edt_code);
this.edt_username = (EditText) findViewById(R.id.edt_username);
Button button = (Button) findViewById(R.id.btn_register);
this.btn_register = button;
button.setOnClickListener(new View.OnClickListener() { // from class: com.r0ysue.first.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View view) {
String obj = MainActivity.this.edt_username.getText().toString();
String obj2 = MainActivity.this.edt_code.getText().toString(); // password
if (obj.equals("")) {
Toast.makeText(MainActivity.this, "用户名不能为空", 0).show();
return;
}
String register = MainActivity.this.register(obj);
if (register.equals(obj2) && !register.equals("Error!")) {
Toast.makeText(MainActivity.this, "SUCCESS!", 0).show();
} else {
Toast.makeText(MainActivity.this, "ERROR!", 0).show();
}
}
});
}

那么很明显就是账号通过native函数处理后与密码进行比较来判断成功与失败

4. 加压缩apk, 拿到so文件,丢入到IDA中进行静态分析看看

__int64 __fastcall Java_com_r0ysue_first_MainActivity_register(JNIEnv *env, jobject object, void *name)
{
const char *char_arr; // x21
JNIEnv t_env; // x8
__int128 *ret_chars; // x1
unsigned int str_len; // w0
__int128 v10[6]; // [xsp+0h] [xbp-90h] BYREF
int v11; // [xsp+60h] [xbp-30h]
__int64 v12; // [xsp+68h] [xbp-28h] v12 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
char_arr = (*env)->GetStringUTFChars(env, name, 0LL);
if ( strlen(char_arr) - 6 < 0xF )
{
v11 = 0;
memset(v10, 0, sizeof(v10));
str_len = strlen(char_arr);
base64_encode((const unsigned __int8 *)char_arr, str_len, (char *)v10);
(*env)->ReleaseStringUTFChars(env, name, char_arr);
t_env = *env;
ret_chars = v10;
}
else
{
(*env)->ReleaseStringUTFChars(env, name, char_arr);
t_env = *env;
ret_chars = (__int128 *)"Error!";
}
return (__int64)t_env->NewStringUTF(env, (const char *)ret_chars);
}

这里可以看到 输入的字符串长度必须大于6 且小于20才可以,并且会进行base64_encode处理,这里是不是正真base64呢? 打开函数看看,整理后可得

unsigned __int8 *__fastcall base64_encode(unsigned __int8 *char_arr_p, unsigned int length, char *ret_v10)
{
unsigned __int8 *t_char; // x8
int v4; // w13
int v5; // w12
__int64 i; // x10
char t_tbale_char; // w14
int v8; // w15
__int64 v9; // x13
int step; // w14
unsigned __int64 tt_Char; // x11
int v12; // w9
int v13; // w10
int v14; // w9 if ( length )
{
t_char = char_arr_p;
v4 = 0;
v5 = 0;
LODWORD(char_arr_p) = 0;
i = length;
do
{
tt_Char = *t_char;
if ( v5 == 2 )
{
t_tbale_char = word_8FC[((unsigned int)tt_Char >> 6) & 0xFFFFFFC3 | (4 * (v4 & 0xF))];
v5 = 0;
v8 = (_DWORD)char_arr_p + 1;
v9 = tt_Char & 0x3F;
ret_v10[(unsigned int)char_arr_p] = t_tbale_char;
step = 2;
}
else if ( v5 == 1 )
{
v5 = 2;
v9 = (16 * v4) & 0x30LL | (tt_Char >> 4);
step = 1;
v8 = (int)char_arr_p;
}
else
{
v9 = tt_Char >> 2;
step = 1;
v8 = (int)char_arr_p;
v5 = 1;
}
char_arr_p = (unsigned __int8 *)(unsigned int)((_DWORD)char_arr_p + step);
ret_v10[v8] = word_8FC[v9];
--i;
++t_char;
v4 = tt_Char;
}
while ( i );
if ( v5 == 2 )
{
v14 = (_DWORD)char_arr_p + 1;
ret_v10[(unsigned int)char_arr_p] = word_8FC[(4 * (int)tt_Char) & 0x3CLL];
char_arr_p = (unsigned __int8 *)(unsigned int)((_DWORD)char_arr_p + 2);
ret_v10[v14] = 61;
}
else if ( v5 == 1 )
{
v12 = (_DWORD)char_arr_p + 1;
v13 = (_DWORD)char_arr_p + 2;
ret_v10[(unsigned int)char_arr_p] = word_8FC[(16 * (int)tt_Char) & 0x30LL];
char_arr_p = (unsigned __int8 *)(unsigned int)((_DWORD)char_arr_p + 3);
ret_v10[v12] = 61;
ret_v10[v13] = 61;
ret_v10[(unsigned int)char_arr_p] = 0;
return char_arr_p;
}
ret_v10[(unsigned int)char_arr_p] = 0;
}
else
{
char_arr_p = 0LL;
*ret_v10 = 0;
}
return char_arr_p;
}

这里有一个关键的变量 word_8FC,点进去看看

.rodata:00000000000008FC 41 42 43 44 45 46 47 48 49 4A+aAbcdefghijklmn DCB "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.rodata:00000000000008FC 4B 4C 4D 4E 4F 50 51 52 53 54+ ; DATA XREF: base64_encode(uchar const*,uint,char *)+4↑o
.rodata:00000000000008FC 55 56 57 58 59 5A 61 62 63 64+

可以看到一个标准的base64编解码表"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

由此可以推测,这就是一个标准的base64编码函数

5. 用frida调用验证一下

function main() {
Java.perform(function () { var MainActivityHandler = Java.use('com.r0ysue.first.MainActivity') console.log('1111')
if (MainActivityHandler != undefined) {
console.log('2222' )
MainActivityHandler.register.implementation = function (str) {
console.log('hooked i = ' + str)
var ret = this.register(str)
console.log('hooked ret = ' + ret) return ret
}
} }) } setTimeout(main, 1000)

日志

ooked i = dfdd
hooked ret = Error!
hooked i = dfddfggv
hooked ret = ZGZkZGZnZ3Y=
hooked i = 1234567
hooked ret = MTIzNDU2Nw==
hooked i = 1234567
hooked ret = MTIzNDU2Nw==
hooked i = 12345678
hooked ret = MTIzNDU2Nzg=
hooked i = ddddddd
hooked ret = ZGRkZGRkZA==
hooked i = dddd
hooked ret = Error!
hooked i = ddddd
hooked ret = Error!
hooked i = dddddd
hooked ret = ZGRkZGRk
hooked i = dddddd
hooked ret = ZGRkZGRk
hooked i = dddddd
hooked ret = ZGRkZGRk

使用在线编解码工具测试,确实是正确的

6. 使用python开发注册机

import base64

while True:
username = input("请输入用户名(长度小于等于20): ")
if len(username) > 20:
print("输入长度超过20,请重新输入")
elif len(username) < 6:
print("输入长度小于6,请重新输入")
else:
b64_byt = base64.b64encode(username.encode('utf-8'))
print("密码:", b64_byt)

【Android逆向】破解看雪9月算法破解第一题的更多相关文章

  1. off-by-one&doublefree. 看雪10月ctf2017 TSRC 第四题赛后学习

    off-by-one 0x00 发现漏洞 1.off-by-one 在massage函数中,如图所示,可以修改的字节数比原内存大小多了一个字节 2.悬挂指针 可以看到,在free堆块的时候,没有清空指 ...

  2. 力扣算法经典第一题——两数之和(Java两种方式实现)

    一.题目 难度:简单 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数, 并返回它们的数组下标. 你可以假设每种输入只会对应一 ...

  3. Android逆向之so的半自动化逆向

    因为工作需要,转型干android逆向,有几个月了.不过对于so的逆向,任然停留在,难难难的阶段,虽然上次自己还是逆向了一个15k左右的小so文件,但是,那个基本是靠,一步一步跟代码,查看堆栈信息来自 ...

  4. 天眼查sign 算法破解

    天眼查sign 算法破解 最近真的在sign算法破解上一去不复返 前几天看过了企查查的sign破解 今天再看看天眼查的sign算法破解,说的好(zhuang)点(bi)就是破解,不好的就是这是很简单的 ...

  5. Android逆向之旅---Android中锁屏密码算法解析以及破解方案

    一.前言 最近玩王者荣耀,下载了一个辅助样本,结果被锁机了,当然破解它很简单,这个后面会详细分析这个样本,但是因为这个样本引发出的欲望就是解析Android中锁屏密码算法,然后用一种高效的方式制作锁机 ...

  6. Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码)

    Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码) 来源 https://blog.csdn.net/jiangwei0910410003/article/details/51 ...

  7. Android 逆向实战篇(加密数据包破解)

    1. 实战背景由于工作需要,要爬取某款App的数据,App的具体名称此处不便透露,避免他们发现并修改加密逻辑我就得重新破解了. 爬取这款App时发现,抓包抓到的数据是加密过的,如图1所示(原数据较长, ...

  8. 看雪论坛 破解exe 看雪CTF2017第一题分析-『CrackMe』-看雪安全论坛

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 逆向 黑客 破解 学习 论坛 『CrackMe』 http://bbs.pediy.co ...

  9. 启xin宝app的token算法破解——逆向篇(二)

    启xin宝app的token算法破解--抓包分析篇(一)文章已经对该app进行了抓包分析,现在继续对它进行逆向. 对于一个app而言,我们要逆向app,需要知道什么呢? 逆向工具 Java基础,甚至c ...

  10. Android逆向——破解水果大战

    最近公司需要测试安卓app安全,但安卓基本上0基础,决定开始学习下安卓逆向根据吾爱破解上教程 <教我兄弟学Android逆向系列课程+附件导航帖> https://www.52pojie. ...

随机推荐

  1. CentOS确认网口是否插入网线的办法

    最近公司的机器存在网络问题, 部分网络总是不通, 比较奇怪. 最近一直想处理好. 第一步: 先查看网口的设备信息 可以使用 ip link show 可以讲网口信息都展示出来. 一般情况下  NO-C ...

  2. TypeScript 类型增强declare的使用

    类型增强 declare 的使用 1.如果一个有一个全局变量 golabaol . 在index.html中. 2.我们在xx.vue中使用 golabaol .这个时候会报错 找不到名称" ...

  3. 【k哥爬虫普法】非法入侵计算机信息系统,获取1500万余条个人信息!

    我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K 哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识, ...

  4. 从零开始配置vim(24)——自动补全

    neovim 自带的代码补全的效果并不好,而且它分为好多类,如果需要人为的去判断使用路径补全.使用当前buffer中的单词补全.亦或者使用include 来进行补全,那样使用起来就很不方便了.针对代码 ...

  5. 应对数据爆炸时代,揭秘向量数据库如何成为AI开发者的新宠,各数据库差异对比

    应对数据爆炸时代,揭秘向量数据库如何成为AI开发者的新宠,各数据库差异对比 随着大模型的爆火,向量数据库也越发成为开发者关注的焦点.为了方便大家更好地了解向量数据库,我们特地推出了<Hello, ...

  6. C/C++ 常用开发代码片段

    由于内容较少,所以,不想把它放在我的本地博客中了,暂时保存在这里,代码有一部分来源于网络,比较经典的案例,同样收藏起来. Stack 栈容器 Stack容器适配器中的数据是以LIFO的方式组织的,它是 ...

  7. Docker从认识到实践再到底层原理(四-2)|Docker镜像仓库实战案例

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总 然后就是博主最近最花时间的一 ...

  8. 零基础入门Vue之皇帝的新衣——样式绑定

    回顾 大致掌握了上一节的 插值语法 我已经可以把想要的数据显示到页面上,并且仅需要修改变量,页面就会跟着实时改变 但如果对于已经熟悉前端的人来说,单单有数据还是不太行,还需要css对数据进行样式的修饰 ...

  9. public private protected 的辨析

    一. public 1.作为类内成员的访问修饰符时,由public修饰的成员数据或者成员函数可以在类外(即派生类内以及实例化的对象后)以及类内进行随意访问 可以看到public成员Data在类外是可访 ...

  10. .NET Core开发实战(第16课:选项数据热更新:让服务感知配置的变化)--学习笔记

    16 | 选项数据热更新:让服务感知配置的变化 选项框架还有两个关键类型: 1.IOptionsMonitor 2.IOptionsSnapshot 场景: 1.范围作用域类型使用 IOptinsSn ...