吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 5904|回复: 105

[安卓逆向图文] 分析一个CrackMe

  [复制链接]
白云点缀的蓝 发表于 2021-8-7 18:07 | 显示全部楼层 |阅读模式

我们把apk拖入模拟器,然后打开
图片 1.png

我们随意输入一串密码点击输入密码试试
可以看到,提示我们验证码校验失败

图片 2.png

我们打开jeb进行分析,直接把apk拖进去

图片 3.png
我们聊聊jeb的使用,拖入apk即可进行自动反编译
下面是反编译出来的代码,也就是dex文件反编译后的相关代码

图片 4.png

这个就是相关的一些资源文件

图片 5.png

图片 6.png

配置清单里面存放有apk的相关配置信息,例如activity,service,都在这里面
下面这个就是代码区了,双击ByteCode即可进入,不过jeb会默认给你打开ByteCode这个字节码区

图片 8.png

图片 7.png

下面这个字符串就是一些代码中出现的方法,类所在路径,还有传参时的字符串,

图片 10.png

图片 9.png

我们顺着之前的验证码校验失败的Toast弹窗来找到相关的逻辑。
在屏幕上显示一段文字,没别的东西的话,那这个一般就是Toast弹窗了,

图片 11.png

我们右键,点击如下箭头所在的位置,这个就是查找引用,看那个方法引用了这个字符串
图片 12.png

点击后,我们可以看到相关引用地方

图片 13.png
我们双击显示出来的引用,来到如下位置
图片 14.png

我们按一下tab键,转为java代码
图片 15.png

我们把代码复制出来,然后进行分析

[Java] 纯文本查看 复制代码
package com.yaotong.crackme; 
import android.app.Activity;  
import android.content.Intent; 
import android.os.Bundle;  
import android.view.View.OnClickListener; 
import android.view.View;  
import android.widget.Button; 
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity { 
public Button btn_submit;   
  	public EditText inputCode;

    static {
        System.loadLibrary("crackme"); 
    }

    @Override  // android.app.Activity   //说明这个方法重写了
    protected void onCreate(Bundle arg3) {   //protected
        super.onCreate(arg3);
        this.setContentView(0x7F030000);  // layout:activity_main
        this.getWindow().setBackgroundDrawableResource(0x7F020000);  // drawable:bg
        this.inputCode = (EditText)this.findViewById(0x7F060000);  // id:inputcode
        this.btn_submit = (Button)this.findViewById(0x7F060001);  // id:submit
        this.btn_submit.setOnClickListener(new View.OnClickListener() {
            @Override  // android.view.View$OnClickListener
            public void onClick(View arg6) {
                if(MainActivity.this.securityCheck(MainActivity.this.inputCode.getText().toString())) {
                    MainActivity.this.startActivity(new Intent(MainActivity.this, ResultActivity.class));
                    return;
                }

                Toast.makeText(MainActivity.this.getApplicationContext(), "验证码校验失败", 0).show();
            }
        });
    }

    public native boolean securityCheck(String arg1) {
    }
}

下面是对代码的分析
[Java] 纯文本查看 复制代码
package com.yaotong.crackme;    
//包路径,也就是这个MainActivity所在的路径,写上这个后,				
//com.yaotong.crackme这个里面的所有相关类都可以在MainActivity里使用了
//package为关键字,固定写法,后面为你的当前类所在的文件夹,也就是包
//下面这些就是导入这个类里面的相关方法需要用到的包
//import为固定写法,一个关键字,我们不能用这个关键字给变量取名字,
//import 后面跟你的类中要用到的方法所在的包就行
import android.app.Activity;   //活动 activity相关方法
import android.content.Intent; //意图 Intent 相关方法
import android.os.Bundle;  //Bundle相关方法
import android.view.View.OnClickListener; //OnClickListener 点击事件相关方法
import android.view.View;  //View 视图相关方法
import android.widget.Button; //Button 按钮相关方法
import android.widget.EditText;//EditText 编辑框相关方法
import android.widget.Toast;//Toast弹窗相关方法
public class MainActivity extends Activity {  
//定义一个类为MainActivity 继承了Activity ,Activity 									
//里面的相关方法,变量,在MainActivity 里面都可以使用
//extends 为继承的意思 后面跟一个类
//public class 为固定写法,前面的public 可以换,可以用private,protected 
//public ,private,protected,这些为权限限定符,可以限定你类,变量或者方法不让其他类访问,
}
public Button btn_submit;   
//定义一个按钮对象,名字为btn_submit ,权限为public
public EditText inputCode;
//定义一个编辑框对象,名字为inputCode  权限为pubilc

   static {
        System.loadLibrary("crackme");  
//static 静态代码块,当MainActivity对象创建时,首先执行里面的代码,
//System是一个对象,打点.调用loadLibrary方法,传入参数crackme,
//意思为加载so库,名字为crackme前后省略lib,.so,这个文件我在之前有说,
//是一个c\c++编写的文件,需要jni才能调用
//loadLibrary方法在System里面
    }

 @Override  // android.app.Activity   //说明这个方法重写了
 protected void onCreate(Bundle arg3) {   
//权限为protected,返回值为空 方法名为onCreate,
//(Bundle arg3)这个为参数,参数类型为Bunldle,名字为arg3
 super.onCreate(arg3);
//super.onCreate(arg3),调用父类的onCreate方法,传入的参数为arg3,这个			
//arg3也就是上面那个protected权限修饰的一个方法
//父类也就是MainActivity继承的那个activity类,也就是说调用activity类里面的onCreate方法
  	this.setContentView(0x7F030000);  // layout:activity_main
//这个setContentView为设置布局的一个方法,布局文件的id为0x7F030000
//这个id为 layout:activity_main,后面会说明id所在位置,这个id是开发工具自动为我们生成的
//this指的是当前所在类的对象,也就是MainActivity实例化后的对象
//为什么这个类中没有setContentView方法也能调用呢?
//因为MainActivity这个类继承了activity类

     this.getWindow().setBackgroundDrawableResource(0x7F020000);  // drawable:bg
//getWindow().setBackgroundDrawableResource设置窗口背景为 drawable目录下的bg.png图片
//getWindow()返回一个对象,然后打点.调用setBackgroundDrawableResource()方法,传入R文件中的id
//R文件在后面我截图了,R类中,会保存resource文件中的每一个信息,产生id,这样我们的代码才能引用他
     this.inputCode = (EditText)this.findViewById(0x7F060000);  // id:inputcode
//调用findViewById方法,传入inputcode 在R文件中的id,返回的是一个view对象,
//findViewById这个方法的意思是通过id来查找相关视图
//我们需要把他转为EditText对象,因为这个对象实际上是一个编辑框,(EditText)这个就是强转
//强转可以把父类转为子类,可以把int数据类型转为double类型等
//然后把EditText对象赋值给在前面定义的public 权限的EditText对象
     this.btn_submit = (Button)this.findViewById(0x7F060001);  // id:submit
//调用findViewById方法,传入submit在R文件中的id,返回的是一个view对象,
//findViewById这个方法的意思是通过id来查找相关视图
//我们需要把他转为Button对象,因为这个对象实际上是一个编辑框,(Button)这个就是强转
//强转可以把父类转为子类,可以把int数据类型转为double类型等
//然后把Button对象赋值给在前面定义的public 权限的Button对象
this.btn_submit.setOnClickListener(new View.OnClickListener() {  
//给按钮btn_submit绑定一个监听事件,setOnClickListener就是设置点击事件
//这个setOnClickListener方法需要一个OnClickListener的实现类
//在这里用的是匿名内部类的方式实现的
//这个OnClickListener在View类中,所以前面要加View,然后打点
            @Override  // android.view.View$OnClickListener
// @Override 说明这个方法是一个重写方法,这个onClick方法在OnClickListener里面
//这个方法是一个抽象方法,需要我们自己实现
            public void onClick(View arg6) {
 //这个方法前面都是固定的  public void onClick(View arg6) {}
//里面的内容需要我们自己写,在这里面我们可以看到相关的逻辑
//下面我分开讲这个校验逻辑
    if(MainActivity.this.securityCheck(MainActivity.this.inputCode.getText().toString())) {
                    MainActivity.this.startActivity(new Intent(MainActivity.this, ResultActivity.class));
                    return;
                }

                Toast.makeText(MainActivity.this.getApplicationContext(), "验证码校验失败", 0).show();
            }
        });
    }






if(MainActivity.this.securityCheck(MainActivity.this.inputCode.getText().toString())) {
                    MainActivity.this.startActivity(new Intent(MainActivity.this, ResultActivity.class));
                    return;
                }

                Toast.makeText(MainActivity.this.getApplicationContext(), "验证码校验失败", 0).show();
            }
        });
    }
//if(){}固定写法 ()这里面写相关的返回值为真或者为假的代码,比如==,> < <= >=等,或者写一个方法,//这个方法的返回值为true或者false即可
//MainActivity.this.securityCheck(MainActivity.this.inputCode.getText().toString())
//为什么是MainActivity.this呢?一个我们新建了一个类,如果我们this放前面的话,就调用的是我们的实//现类里面的方法,也就是说只有onClick方法可以被调用。
MainActivity.this//就是指定在MainActivity里面方法,类的话,是调用不到,我们只能new一个对象
//也就是新建一个对象,创建对象的写法,new 类名();如果括号里面没有写值,那么调用的是无参构造方法
//构造方法没有返回值,例如public 类名(){}这个就是一个无参构造方法,如果里面写参数,比如基本数据类型
//int double float long 等,还有其他的数据类型也就是引用数据类型,比如对象,放一个接口也行,只不过我们要给他传入一个实现类
基本数据类型

图片 22.png

引用数据类型
图片 23.png



[Java] 纯文本查看 复制代码
//然后调用了securityCheck,传入了一个参数MainActivity.this.inputCode.getText().toString()
//这个参数是String类型的,这个inputCode就是那个编辑框对象,也就是crackme软件的那个输入框,
//调用了getText().toString()方法,getText返回一个对象,然后用这个对象调用方法,返回一个String

//如果这个securityCheck方法返回值为true那么执行下面这个startActivity方法
MainActivity.this.startActivity(new Intent(MainActivity.this, ResultActivity.class));
//这个方法会开启新的activity,传入的参数是一个intent,
//这个intent传入的参数第一个为当前MainActivity
//第二个参数为要开启的activity的类,.class就是类
//调用完这个开启activity方法后,执行 return;这个就是结束当前方法,
//如果这个securityCheck方法返回值为false的话,那么就执行下面的Toast弹窗
  Toast.makeText(MainActivity.this.getApplicationContext(), "验证码校验失败", 0).show();
//调用Toast对象里面的makeText方法,传入三个参数,第一个为上下文,也就是context,这个//MainActivity.this.getApplicationContext()的返回值为一个上下文,第二个参数为我们想要显示的字符串,
//第三个参数为显示多长时间

我们看一下新开启的activity
图片 24.png

[Java] 纯文本查看 复制代码
package com.yaotong.crackme;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class ResultActivity extends Activity {
    @Override  // android.app.Activity
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText("Congratulations!!!You Win!!");
        this.setContentView(tv);
    }
}
很多代码我都在前面详细讲了,这里我讲一下没讲的代码,new TextViewthis,
这个就是创建一个TextView对象,传入的参数为this,调用的是有参构造
返回的对象用tv变量保存,TextView为这个变量的引用数据类型
然后用tv调用了setText方法,传入一个字符串String类型的参数,这里面的意思
大概是恭喜你,成功了。
然后调用setContentView()方法传入tv参数,这样就能把这个TextView显示到界面上
在界面显示的对象实际上是一个view对象,是所有控件的父类,因此我们可以传入textview
this指的是当前activity,也就是ResultActivitysetContentView方法在继承activity后才能使用
也就是 extends Activity
接下来我们看看这个securityCheck方法
[Java] 纯文本查看 复制代码
public native boolean securityCheck(String arg1) {
    }
这个方法是一个native方法,说明这个方法的逻辑在so层,也就是libcrackme.so这个文件里面
下面我们要用到的工具是ida,因为ida可以很好的分析so
我们首先要解压apk,然后获得如下文件
图片 25.png

这个so文件在lib文件夹里,
这里扩展一下文件结构,lib是存放一些so文件的,这个so文件由ndk生成
C\C++语言开发,在java层无法直接调用,但是可以通过jni间接调用
我们只要声明这个方法为native方法,就可以调用了,但前提是,在so中有相关这个方法的实现。
META-INF这个是存放签名的,
AndroidMainfest.xml这个文件是存放相关的配置信息,比如activity,service,包名,是否全屏,标题,apk的图标等,都可以在这里进行设置
Classes.dex这个是相关的java代码转成了class字节码文件
为什么不直接是.class后缀呢?因为版权问题,所以谷歌自己写了个编译转换方式,直接转为dex文件,不直接转为class文件,同时也为了方便,因为你要编写大量的类,那么就会生成大量的class字节码文件,这样也不方便,所以直接合在一起了,
resources.arsc这里面存放了相关资源的索引,比如布局文件里面id,他的相关索引会出现在
resources.arsc里面,详细参考下面


布局文件的id,按钮视图,编辑框视图的id所在位置,这个id每当你在Resources文件中声明时都会自动生成

图片 21.png



图片 20.png





Resource资源产生相关的id信息都存储在R类下

图片 19.png


图片 17.png


图片 18.png

这些是权限限定符的详解

图片 16.png


我们进入存放so文件的lib文件夹,
可以看到这里面还有一层文件夹,armeabi,这个代表这个so文件是arm架构的。
用于运行在arm的手机中

图片 26.png
我们把这个文件拖入ida里面,
这里我们默认即可,然后点击ok
然后出现提示,我们点击ok
图片 27.png

图片 28.png
如果不想再弹出这个提示,我们可以勾选下面这个框框

图片 29.png

函数相关的都在这里

图片 30.png

其他的ida讲解,你可以参考这个
这里面讲解了相关ida的使用
ida分析一个易语言登陆程序
https://www.52hb.com/thread-51805-1-1.html
(出处: 吾爱汇编论坛)
我们找到那个securityCheck方法在so层的实现
我们可以看到有securityCheck函数,这个其实就是check方法的实现了
图片 31.png

Java_com_yaotong_crackme_MainActivity_securityCheck
Java代表这是一个java层的,
com yaotong crackme 这个就是包名
MainActivity就是activity
SecurityCheck这个就是在java中的方法名

合起来就是在java层有个securityCheck方法,他在MainActivity类里,
这个类在com.yaotong.crackme包下。中间我们换成_就是在so层的函数名了
我们只传了一个参数,为什么这个函数有三个参数呢?
int __fastcall Java_com_yaotong_crackme_MainActivity_securityCheck(int a1, int a2, int a3)
实际上有两个参数ida未识别,一个JNIEnv *, 还有一个是jclass,
第一个是JNI环境的指针,第二个是java的类
我们可以导入安卓jni开发的相关头文件,然后修改类型,ida就会自动为我们识别代码
导入方式如下

图片 32.png

然后我们选择jni的头文件

图片 33.png

然后会提示我们解析成功

图片 34.png

下面我们修改参数类型,前两个参数是我之前讲的那两个参数,一个JNIEnv*,一个jclass
对着第一个int类型右键,点击Set lvar type

图片 35.png

然后我们把类型写上,JNIEnv*,点击ok

图片 36.png

第二个参数,同样右键,然后点击set
图片 38.png

输入jclass,点ok

图片 39.png

我们可以看到效果
未识别前
图片 37.png

识别后

图片 40.png

图片 41.png

第三个变量的类型不应该是int类型,而是string类型,我们可以在string前面加个j
代码java的string类型,也就是jstring

图片 42.png

我们可以改改变量名字,方便阅读
对着变量名右键,也就是a1,然后选择箭头所在的选项,Rename

图片 43.png

第一个参数是JNIEnv*,所以我们把他取名为env,这个名字可以任意,只是我们方便阅读
输入要改的名字后,我们点击ok

图片 44.png

重复前面的操作

图片 46.png

图片 45.png

因为class是关键字,所以会报错,我们应该在前面加个_

图片 48.png

图片 47.png



重复前面的操作,右键,选择Rename
这个是改好后的函数
int __fastcall Java_com_yaotong_crackme_MainActivity_securityCheck(JNIEnv *env, jclass _class, jstring string)

图片 49.png


可以看到方便阅读多了
当然我们也可以右键选择这个,

图片 50.png

图片 51.png
我们选择JNIEnv,第一个的是类型名,第二个是这个JNIEnv结构体的声明,
第三个是占用内存大小
可以看到第二个结构体声明里面又有一个结构体指针,并且是const修饰,因此这个*functions不可修改,struct类型名,说明这是一个结构体类型,后面的是这个结构体,functions是这个结构体的指针,const,struct,都是固定写法。指针相关知识,
你可以看这篇文章,这里面有详细讲到。
对一个文字转语音软件的分析
https://www.52hb.com/thread-51699-1-1.html
(出处: 吾爱汇编论坛)

这里我说一下结构体是什么,怎么定义一个结构体。

[C] 纯文本查看 复制代码
struct person{
char *name;//姓名,char类型的指针,里面可以存放字符
int age;//年龄   int类型,存放整型变量
char *sex;//性别  char类型的指针,里面可以存放字符
}

定义格式struct  xxx{
这里面写数据类型;
}
下面是其中一种定义格式,也是常见的一种定义格式

[C] 纯文本查看 复制代码
#include <stdio.h>
int main() {
	struct person
	{
		char* name;//姓名
		int age;//年龄
		char* sex;//性别
	};
	struct person Person;
	Person.name = "starry";
	Person.age = 2;
	Person.sex = "男";
	printf("%s %d %s", Person.name, Person.age, Person.sex);
}
struct person Person; 
//struct person是数据类型,类似int一样,Person是变量名
//struct person是一个整体,
Person.name = "starry";
//给name变量赋值为starry
Person.age = 2;
//给age变量赋值为2
Person.sex = "男";
//给sex变量赋值为男
printf("%s %d %s", Person.name, Person.age, Person.sex);
 //分别打印name,age,sex
//%s是指与char类型匹配, %d是与整型匹配,
//%s %d %s 分别对应Person.name, Person.age, Person.sex

这个只是其中一种写法,也是常见的一种写法
想了解其他写法可以参考这个网站
http://c.biancheng.net/view/2031.html
如下是运行图
图片 52.png

我们主要分析下面这个代码
图片 54.png

[C++] 纯文本查看 复制代码
v5 = env->functions->GetStringUTFChars(env, string, 0);
把我们在java层传入的字符串转成c类型的char,
env是一个结构体指针,如果是指针那我们就要用->箭头这种形式来访问里面变量,函数等.
env->functions,这个functions实际上还是一个结构体指针所以又有一个->箭头
最后这个就是functions结构体指针里面的函数了GetStringUTFChars
这个函数有三个参数,第一个是env,第二个是string,第三个是0
我演示一下结构体指针
[C++] 纯文本查看 复制代码
#include<iostream>  //包含一个系统头文件iostream
using namespace std;//使用命名空间 using namespace为固定写法

int main() {  

	struct functions
	{
		void PriStr(int a,double b,const char *c) {
			cout << a << b << c << endl;
		}
	};
	struct JNIEnv
	{
		functions* fu;
	};
	JNIEnv JNIENv;
	JNIEnv* JNI=&JNIENv;
	functions fun;
	JNI->fu = &fun;
	JNI->fu->PriStr(66, 77, "hello");
}[/size][/font][/align][align=left][font=微软雅黑][size=4]
[/size][/font][/align][align=left][font=微软雅黑][size=4]



在这里我定义了两个结构体struct functionsstruct JNIEnv
struct functions结构体里有函数,返回值为void类型,参数有三个,
类型分别为int,double,const char*
struct JNIEnv这个结构体里面保存了struct functions结构体的指针
扩展:在c++中结构体前面可以省略struct,
也就是说没必要这样来定义一个结构体变量struct JNIEnv JNIENv;

JNIEnv JNIENv;//定义一个JNIEnv结构体,名字为JNIENv
JNIEnv* JNI=&JNIENv;//定义一个JNIEnv结构体指针,&JNIENv为取JNIENv变量的地址
functions fun;//定义一个fun结构体变量
JNI->fu = &fun;//因为JNI是一个结构体指针,所以我们不能打.点来获取结构体里面的相关变量
//获得fu变量后,给fu变量赋值为fun的地址;
JNI->fu->PriStr(66, 77, "hello");//JNI是结构体指针所以->箭头指向内部的值,fu也是结构体指针,所以再次指向箭头来获取里面的东西,也就是PriStr函数,我们传入66,77,hello
然后我们点击运行,就会执行cout << a << b << c << endl;
然后打印相关数据到控制台

图片 55.png

因为是c中char *字符类型,所以我们可以改为c_string名字,方便阅读
图片 56.png

[C] 纯文本查看 复制代码
v6 = off_628C;//这个不确定是什么,我们继续向下看


[C++] 纯文本查看 复制代码
while (1)
{
	v7 = (unsigned __int8)*v6;
	if (v7 != *(unsigned __int8*)c_string)
		break;
	++v6;
	++c_string;
	v8 = 1;
	if (!v7)
		return v8;
}

可以看到有个while(1)死循环,
v7 = (unsigned __int8)*v6;//把*6的值转为无符号int8类型,实际上就是char类型,大家查阅相关资料就知道了,然后赋值给v7
if (v7 != *(unsigned __int8*)c_string)
//如果v7不等于c_string那么执行下面的break代码;
//直接跳出了这个死循环,(unsigned __int8*)c_string,
//这个就是把c_string转为无符号unsigned __int8*类型,然后取*,获得里面值
                break;
如果break了会怎么样呢?
就会执行下面的这个return 0;,然后把这个0给java层的那个if条件判断语句
0就是假,如果为假,那么就执行toast弹窗,提示验证码错误的信息
我们继续向下看
++v6; //++v6这个就是v6这个指针加v6这个类型去*的类型的大小
//比如char 类型他是1个字节的,同时这个v6又是char *类型的,那么就是+1
//比如一个地址0x12340000,把这个地址给一个变量名为a,那么++a就是
//这个地址+1,也就是0x12340001,
++c_string;//这个也是给指针+对应类型长度也就是+1,一个字节的长度
//v8 = 1;//给v8赋值为1,这个是关键因为下面有个return v8,返回1的话,那么就是成功了
if (!v7)//如果v7为空,代表对比完了,因为空取反就是真,就返回v8,然后就成功了
return v8;
现在的关键是怎么找到这个正确的验证码.
下面我们开始动态调试来获取这个码
这边用的是真机调试,因为我没有root所以下了个vmos虚拟机
可以root远程调试.很方便.
下载地址:
VMOS安卓虚拟机专业版
https://www.52hb.com/thread-51332-1-1.html
(出处: 吾爱汇编论坛)
这个连接方式,虚拟机会告诉你的,直接照着写就行
我们输入adb connect 192.168.1.4:5666
图片 57.png

我们重新打开ida,然后选择如下
图片 58.png

输入对应的ip和端口号就可以开始调试

图片 59.png
在调试前,我们需要把ida的调试文件放到手机中

图片 60.png



图片 61.png

放入手机的命令是adb push H:\IDA_Pro_v7.5_Portable(1)\dbgsrv\android_server /data/local/tmp
你也可以放到其他目录,/data/local/tmp这个目录比较常用而已

图片 63.png
放入后,我们进入/data/local/tmp
如果要进入我们先要adb shell
图片 64.png
然后执行su命令,su用于获取最高权限,方便调试,给相关文件设置权限
图片 65.png

我们输入 cd /data/local/tmp进入文件夹

然后输入 ls- a
可以列出所有的文件

列出后我们可以看到自己刚刚放入的文件android_server
图片 66.png



我们需要给这个文件赋予最高权限,同时需要给他执行权限
修改权限命令chmod 777 你的文件

图片 67.png
然后我们./android_server执行这个调试文件

图片 68.png
然后我们就可以在ida上进行调试了
调试前,我们先要在手机上打开那个crackme
然后我们在ida上输入我们手机的ip地址后,点击ok

图片 69.png

选择我们的进程
图片 70.png

双击进入
这个就是在下载手机上的相关文件了

图片 71.png

出现下面这个信息,我们点击ok就行

图片 72.png

图片 73.png


然后我们点击绿色箭头进行运行

图片 74.png

选择一个我们ctrl+F搜索

我们搜索libCrackme

图片 75.png

双击找到这个securitycheck函数


图片 77.png



双击securitycheck函数,进入后,按空格放大,然后按tab键,把汇编转成c伪代码,然后我们在下面这个位置下断

图片 78.png
我们改一下这个函数的参数,第一个是JNIEnv*第二个是jclass,第三个是jstring,
在修改前,我们需要导入jni的头文件
图片 80.png

图片 79.png

图片 81.png


可以看到相关函数名已经可以看到了
图片 82.png


我们在app上输入一串字符串然后,点击输入密码

可以看到断下来了
图片 84.png

我们可以修改一下相关的变量名,方便阅读分析

右键选择rename

图片 85.png

图片 83.png


按照前面的分析,我们执行一下这行代码应该就能获取真码了
F8是步过
F7是步入
Ctrl+F7是运行到return代码处
F4是运行到鼠标指定位置

图片 86.png


这里我们F8
F8后,我们双击这个flag,这个flag是我修改的名字

图片 87.png

双击flag后进入到这个代码区
我们可以看到一串字符串

图片 88.png

我们按键盘上的a,把这个字符串变为一串的
按a后我们点击yes

图片 89.png

这个是转换后的结果,aiyou,bucuoo
图片 90.png



我们继续单步
可以看到这个123456789数字其实就是我之前输入的,
我们试一下把这一串字符串改成上面转换后的结果,看看能不能成功
这个c_string在r0,所以我们到r0这个位置进行修改

图片 91.png


图片 92.png

我们对着r0后面那个地址右键,然后选择jmp

图片 93.png


Jmp后的代码

图片 94.png

我们按一下a
把这一串123456789变为连起来的

图片 95.png

我们右键选择Hex View-1,地址不一样是因为刚刚ida卡死了

图片 99.png



图片 97.png

我们选择这一串字符串进行右键选择edit
图片 98.png

修改好后,别忘了应用,然后我们继续单步


图片 102.png

图片 100.png


图片 104.png





第一次比对V11=0x61  c_string=0x61
第二次V11=0X69                c_string=0x69
第三次V11=        0x79    c_string=0x79
第四次v11=0x6F   c_string=0x6F
第五次V11=0x75   c_string=0x75
第六次v11=0x2C   c_string=0x2C
第七次v11=0x62   c_string=2x62
第八次 v11=0x75  c_string=0x75
第九次 v11=0x63  c_string=0x63
第十次 v11=0x75  c_string=0x75
第十一次 v11=0x6F c_string=0x6F
第十二次 v11=0x6F c_string=0x6F
第十三次 v11=0    c_string=0
当我们执行完第十三次后,程序弹出Congratulations!!!You Win!!

我们把分析结果放入vs,进行转换,不出意外的话,结果是aiyou,bucuoo
简单写了一个代码,可以看到,这个真码就是aiyou,bucuoo

[C] 纯文本查看 复制代码
#include <stdio.h>
int main(){
	const char buf[20] = { 0x61,0x69,0x79,0x6F,0x75,0x2C,0x62,0x75,0x63,0x75,0x6F,0x6F,0 };
	printf("%s", buf);

}

图片 105.png


我们拿着这个真码,放到模拟器上试试,可以看到成功了

图片 106.png

图片 107.png

到此,分析结束




图片 76.png

评分

参与人数 41威望 +1 HB +151 THX +20 收起 理由
再来壹瓶 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
花盗睡鼠 + 2 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
longge188 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
虚心学习 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
1157141823 + 3 + 1
创客者V2.0 + 1
459121520 + 1
后学真 + 1
yeyue + 2 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
冷亦飞 + 1
飞刀梦想 + 1
腿毛利 + 1
summersn0w + 1 + 1
于理 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
无影无踪 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
crosssss + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
成丰羽 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
我是好人 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
Hackers + 2 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
playboy + 1
拿着雪糕 + 1 + 1
消逝的过去 + 2 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
hetao8003200 + 1
nypht1228 + 1 + 1
kalove + 1
bnjzzheng + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
lai1124 + 1 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
6378895 + 2 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
jianxuu + 1
langzill + 1
kll545012 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
baoyue + 1
CraftDeadMRC + 1
lies + 1
king51999 + 1 [快捷评语]--你将受到所有人的崇拜!
wanao2008 + 2 + 1 [快捷评语]--吃水不忘打井人,给个评分懂感恩!
eurchin + 1 + 1 [快捷评语]--你将受到所有人的崇拜!
aゞ烛火 + 6 + 1 很强,写满整整一页
Shark恒 + 1 + 100 + 1 [快捷评语]--你将受到所有人的崇拜!
192939 + 3 + 1 [快捷评语]--你将受到所有人的崇拜!
fei9865 + 2 + 1 [快捷评语]--你将受到所有人的崇拜!

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
 楼主| 白云点缀的蓝 发表于 2021-8-7 18:15 | 显示全部楼层

文档版,及相关课件,
https://starrysp.lanzoui.com/b0ag1w8fi
密码:71c1

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
Shark恒 发表于 2021-8-8 08:34 | 显示全部楼层

精华走一个!这么细致的讲解,不给精华天理难容了!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
 楼主| 白云点缀的蓝 发表于 2021-8-8 11:43 | 显示全部楼层

Shark恒 发表于 2021-8-8 08:34
精华走一个!这么细致的讲解,不给精华天理难容了!

学破解上传问题.jpg
这个列表上传功能没有用,一上传还得重新打开帖子才会正常显示,而且还会丢失登陆token,让token失效,然后就要重新登陆一下,建议修复一下,

另外恒老师是不是忘给这个帖子评分了,

点评

Shark恒”点评说:
哎呀!好的  详情 回复 发表于 2021-8-8 13:45
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
Shark恒 发表于 2021-8-8 13:45 | 显示全部楼层

starry、星空 发表于 2021-8-8 11:43
这个列表上传功能没有用,一上传还得重新打开帖子才会正常显示,而且还会丢失登陆token,让token失效,然后 ...

哎呀!好的
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
lies 发表于 2021-8-9 00:12 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
eurchin 发表于 2021-8-9 09:04 | 显示全部楼层

学习学习,谢谢。。
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
funnk666 发表于 2021-8-24 15:49 | 显示全部楼层

学习学习!谢谢
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
CraftDeadMRC 发表于 2021-12-28 12:42 | 显示全部楼层

感谢分享
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
阿桂哥 发表于 2022-1-4 10:35 | 显示全部楼层

谢谢楼主分享
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层
9层
10层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

快速回复 返回顶部 返回列表