文章目录

  • 一、 阅读然后回答2个问题
  • 二、阅读
  • 三、阅读
    • 复现android逆向过程
    • 1、先在android模拟器上安装好CrackMe1.apk文件,
    • 2、用apktool将CrackMe1.apk反编译:
    • 3、接着我们要从apk中提取.dex文件
      • 3.1、dex文件转换为jar
      • 3.2、反编译jar包
      • 3.3、代码分析
      • 3.4、a类的分析:
      • 3.5、类b的分析:
      • 3.6、类c的分析:
    • 4. 破解方法:
      • 4.1 单纯的破解,用代码注入的方法得到注册码
      • 4.2 读取程序对应的文件得到注册码
      • 4.3 编写注册机得到注册码
  • 四、 (2选1,原理一样的)

一、 阅读然后回答2个问题

阅读
https://en.wikipedia.org/wiki/X86_calling_conventions
然后回答2个问题:
What does this code do?
The function has 4 arguments and it is compiled by GCC for Linux x64 ABI (i.e., arguments are passed in registers).

<f>:
0:  mov   r8,rdi    //把rdi寄存器中的值送给r8寄存器
3:  push    rbx      //将rbx寄存器中的值压入栈
4:  mov   rdi,rsi     //把rsi寄存器中的值送给rdi寄存器
7:  mov   rbx,rdx    //把rdx寄存器中的值送给rbx寄存器
a:  mov   rsi,r8    //把r8寄存器中的值送给rsi寄存器
d:  xor   rdx,rdx    //rdx寄存器异或置0
begin:
10:   lods   rax,QWORD PTR ds:[rsi]    //从串取指令,从DS:SI所指向的空间中取出一个字节/字/双字放入寄存器中AL/AX/EAX,同时把SI+(-)1/2/4.(LODS相当于MOV AL,[SI]和INC SI.)(取出一个值后地址加1,方便下次取出下一个值,rsi存放了源地址)
12:   div   rbx      // 无符号除法DX -AX / rbx,商回送AX,余数回送DX
15:   stos   QWORD PTR es:[rdi],rax    //保存串,是LODS的逆过程,保存在ES:DI中(保存一个值后地址加1,方便下次保存下一个值,rdi存放了目的地址)
17:   loop   begin     //CX不为零时循环
19:   pop   rbx        //rbx寄存器中的值出栈
1a:   mov   rax,rdx    //把rdx寄存器中的值0送给rax寄存器
1d:   ret            //返回

四个参数,第一个参数是rsi,表示源地址;第二个参数是rdi,表示目的地址;第三个参数是rbx,表示除数;第四个参数是rcx,表示源地址上数据的数目。
原来的C语言代码应该是类似这样的:

void f(DWORD *arr1,DWORD *arr2,DWORD x,DWORD n)for(int i=0;i<n;i++){arr2[i]=arr1[i]/x;}return ;

函数功能:就是将[参数2]指向的数组的前[参数4]个数的值都除以[参数3]之后,赋值给[参数1]指向的数组。

<f>:0:             push   rbp             //将rbp的值入栈1:             mov    rbp,rsp       //将rsp的值送到rbp4:             mov    QWORD PTR [rbp-0x8],rdi    //将rdi的值放到ds[rbp-0x8]地址的四个字的位置上8:             mov    QWORD PTR [rbp-0x10],rsi   //将rsi的值放到ds[rbp-0x10]地址的四个字的位置上c:             mov    rax,QWORD PTR [rbp-0x8]        //将地址ds[rbp-0x8]上的四个字送到rax10:             movzx  eax,BYTE PTR [rax]        //将地址ds[rax]上的一个字节无符号扩展送到eax13:             movsx  dx,al               //将al有符号扩展送到dx
//将rdi的低字节有符号扩展后的值放在dx17:             mov    rax,QWORD PTR [rbp-0x10]    //将ds[rbp-0x10]的字送到rax1b:             mov    WORD PTR [rax],dx            //将dx的值送到ds[rax]地址
//把rdi的最低字节进行符号扩展后的值,给rsi的一个字1e:             mov    rax,QWORD PTR [rbp-0x10]   //将ds[rbp-0x10]的数据送到rax22:             movzx  eax,WORD PTR [rax]      //将ds[rax]的字无符号扩展送到eax
//把rsi的一个字给eax25:             test   ax,ax        //ax与ax进行与操作,不改变ax28:             jne    2c           //不为0就跳到2c2a:             jmp    38          //跳转到382c:             add    QWORD PTR [rbp-0x8],0x1     //ds[rbp-0x8]地址的值加131:             add    QWORD PTR [rbp-0x10],0x2   //ds[rbp-0x10]地址的值加236:             jmp    c      //跳转到c38:             pop    rbp  //rbp出栈39:             ret             //返回

函数功能:将一个数组(rdi为首地址)按有符号扩展后存入另一个数组(rsi为首地址)。

二、阅读

https://www.pediy.com/kssd/pediy07/pediy7-94.htm
一个简单的linux crackme的逆向

三、阅读

https://blog.csdn.net/ir0nf1st/article/details/67799899
逆向一个IOS CrackMe
https://www.cnblogs.com/LittleHann/p/3374206.html
android逆向学习小结–CrackMe_1

复现android逆向过程

1、先在android模拟器上安装好CrackMe1.apk文件,


1.1再打开运行一下看它是要干什么:

只有一个输入框,那说明这个验证码的输入来自别的地方,因为我们知道,不管你的加密算法是啥,总是要有一个函数输入源的,我们在UI界面上输入的相当于是结果,而输入源应该来自于别的地方,计算完之后和我们在UI上输入的结果进行对比,大致是这个思路。

2、用apktool将CrackMe1.apk反编译:


2.1、查看AndroidManifest.xml文件。了解到主activity为:Main。

2.2、再到CrackMe1\res\values路径下,查看出现的字符串String之类的资源,出现了之前我们运行app时出现的名字:“lohan’s crackme1”、输入提示信息:“Please enter the registration code:”、无效的序列:“Invalid serial”等:

2.3、id:

2.3、String和id对应的编号:

3、接着我们要从apk中提取.dex文件

将CrackMe1.apk文件的后缀名改为.zip,之后解压缩:

3.1、dex文件转换为jar

将classes.dex复制到dex2jar的目录下,用dex2jar将classes.dex转换成classes-dex2jar.jar。

3.2、反编译jar包

用jd-gui反编译jar包,来查看java源代码:

看到里面很多的类、方法、变量都用a、b、c代替了,基本上可以判定是配ProGuard混淆了,不过问题也不大,虽然显示的是无意义的函数名,但是因为函数比较少,所以不太影响我们分析代码流程。

3.3、代码分析

先从Main.class开始分析:

  1. 先是onCreate()初始化函数,先初始化b和c的类。然后调用b.a()生成并存储"机器码",然后调用c.a(),也就是判断是否已经存储了serial,并判断是否能通过算法校验。如果不能通过,则什么都不做,这就是启动时检测注册状态的做法,即如果你之前已经注册了,那在之后的登录后就会自动识别出来,但是我们如果是第一次启动且没有注册,那这里就什么也不做。
    如果能通过,则调用自身的方法a()。而自身的方法a()又调用了c.b()方法,即检查我们输入的serial和机器码的MD5值是否相同,如果相同则什么也不做,如果不同就把下面的按钮和TextView等UI控件给隐藏了。并启动倒计时类a.start()。即二次验证。
    ps:
    这里要注意的是,由于程序使用了ProGuard来混淆代码,所以用jd-gui翻译出来的代码全都是从a,b,c开始计数,而且经常是变量、类、方法的命名混合了起来。我们在看java代码的时候遇到难懂的地方要结合smali代码一起看,这样才能获取比较准确的对程序代码流的把握。

public void onClick(View paramView)
if (c.a(((EditText)findViewById(2131034114)).getText().toString()) == 0)
{
Toast.makeText(this, 2130968577, 0).show();
return;
}
Toast.makeText(this, 2130968578, 0).show();
onClick()监听函数判断我们通过UI输入的serial是否和"机器码"的MD5值相同,如果不相同则弹出提示Invalid serial!(可以通过ID值反查出对应的字符串),如果相同则弹出Thanks for purchasing!
main的a()函数:

它的构造函数是负责初始化程序的用户界面,就是开始的输入框、按钮:
通过调用layout目录下的main.xml文件下的id号获取对应的控件,而main.xml文件下的id号又通过values目录下的ids.xml文件来自于publics.xml文件,例如(2131034113):

在文件中0x7f050001对应的就是2131034113,所以这样就能通过id号来找到相应的控件了。设置控件的可见性为4,意思是不可见的,但还占着原来的空间。
之后就是初始化a类,开始a类的倒计时功能:

3.4、a类的分析:

可以看到,类a是一个CountDownTimer:

我们看出这个类的功能是倒计时6秒, 从onFinish函数(计时完毕时触发)然后调用c.a(),也就是判断我们输入的serial是否等于"机器码"的MD5值。如果不能通过,就设置TextView内容提示注册无效。

3.5、类b的分析:

类 b 提供了一个公共的构造函数 public b(Context paramContext), 一个私有的成员函数private String b(), 以及一个公有成员函数 public final void a()。
b(): 通过TelephonyManager获取设备相关的一些信息,然后通过PackageManager获取到自身的签名。然后把这些字符串拼接起来返回给调用者:

a()调用方法b()获取字符串,然后通过SharedPreferences.Editor将这个字符串值存储到键machine_id,可以理解为机器码。也就是说,这个加密函数的输入是本机的机器码:

经过上面的分析,类b对外提供方法a,功能就是生成"机器码"并存储到系统中,对应的键为machine_id。

3.6、类c的分析:

类c提供的方法较多,我们逐个分析。

  1. 构造函数

把参数上下文Context传入给自己的属性,并初始化两个字符串。

  1. public static String b()

通过MessageDigest计算传入参数 的MD5值。

  1. public static boolean b()

通过 getPackageManager 获取自身的签名,如果签名与构造函数中的两个字符串b(f0d412b5530e1f9841aab434d989cc77)或者c(4ec407446b872351e613111339daae9)任意一个相等,那么返回false,否则返回true。

  1. public static int a(String var0)

可以看出这段代码的功能为计算机器码的 MD5,如果与传入的参数var0一致,那么通过SharedPreferences存入到serial(机器码的MD5值var0)字段中。 当然还有调用b方法进行一些判断,自身的签名不能是已知的两个。

  1. public static boolean a()

这个其实就是上面的 int a(String var0)的包装函数,通过SharedPreferences获取serial字段(机器码的MD5值),并传给这个方法,返回相应的返回值(判断结果):

4. 破解方法:

4.1 单纯的破解,用代码注入的方法得到注册码

经过分析,我们知道应该在b.smali的155行:

move-result-object v2 这里代码注入,因为这个b()的作用就是获取当前"机器码"(注意,这里获取的是没有MD5之前的"机器码",因为程序中的MD5都是临时算出来的)。
我们在这里加入:

const-string v3, “SN” invoke-static {v3, v2},
Landroid/util/Log;>v(Ljava/lang/String;Ljava/lang/String;)I

用apktool将smalli代码重新回编译CrackMe1.apk。

还需要对CrackMe1.apk进行签名在android上才能运行:

在命令行中执行 adb logcat -s SN:v ,然后再启动程序,会在命令行中看到一大串字符串,这些字符串就是我们要的机器码。

4.2 读取程序对应的文件得到注册码

我们知道,所谓的SharedPreferences本质上是保存在当前程序空间下的/data/data//shared_prefs/ _preferences.xml 文件中的。
我们在android模拟器中找到这个文件,直接读取这个文件的内容:

可以看到,和我们通过代码注入的方式得到的机器码是相同的。

4.3 编写注册机得到注册码

这种方法是最好的,编写注册机要求我们对目标程序的代码有全盘的认识,然后模拟原本的算法或者逆向原本的算法写出注册机。
我们用Eclipse重新生成一个新的工程 com.lohan.crackme。注意,工程的报名必须和目标程序的包名一致,这样我们的注册机运行后得到的APK签名才会是一样的:

通过监听获取注册码:

四、 (2选1,原理一样的)

MTC3 Broadcasting and low exponent — RSA-Attack
https://www.mysterytwisterc3.org/en/challenges/level-ii/broadcasting-and-low-exponent–rsa-attack
Alice’s Birthday Party (Part 2)
https://www.mysterytwisterc3.org/en/challenges/level-ii/alices-birthday-party-part-2
hint:中国剩余定理 https://www.di-mgt.com.au/bigdigits.html

选的是Alice’s Birthday Party (Part 2):
题目当中给出了

N1=
514745167025222387434132377137056715954750729807151447929894289695587285793889099978536904494455862473045694392353612260528582074521711735864082380505874261026769465596315849668245703081452047808798727647904141791488099702631575692170683102622471798376397440600292225038412176681344166204027842724877162681931
N2=
332459552799915544356022641605448137617079921391832222557892949808060953028449422328281413629912335051440744955455010851012308918294549765005480121061697711447087615327860789708246235156912421474047484838827777697938563515420810650393553528058831317409340577149233554235346445890238642955390137465511286414033
N3=
665701912162243069059653781669230805473457427767514323262762891771122352328706695409103713864384833437438648120217615990765220365745013739246022203593234785338178963805463643869398986119431772931646042972240277833431035018628949924813463553419243108837309078316455504749755062865258063926243606206806549969161

e=3

c1=
159610386572167689266326385036487109027500941380400104125191262882664358398577536610497671009102596940624920315091422093100238619835848693651492344785000232303139338861093680138737091249739575100655219967271819921458016154329847843423233652818852580016834561970850695063090000199448970052668647861992230109134
c2=
80704323590708576386562863656130406931573788060159775931074197125212042930440694778363300836637666152530601069635539711403775897104413839059003511049631024172974390473641408894970527777947213128650545118958630567223577806350516381008539951304600069024003674444114727988917350720932569342357635015732615468372
c3=
290728542387622789691059470283422806073663108257730190721270583629901119139049111765276898786687400514004023098315787810926656039376046957101984075353288285867739293190825676944209163087896697394093577432590616749562076462942759742984949258019827469729922204479107792698042941392668070743176808454529741938138

也就是已知

根据中国剩余定理,是可以求出me,e=3,继而求出10进制明文。之后我们将它转换成16进制数字,再转换成Base64的字符串,最后解码即可得到明文字符串。
源代码:

# coding=utf-8
#py -2#求最大公因数
def gcd(a, b):if not b:return a, 1, 0else :g, x, y = gcd(b, a % b)return g, y, x - y * (a / b)def solve(n, A = [],B = []):M = 1ans = 0for i in A:M *= ifor i in range(n):Mi = M / A[i]G, X, Y = gcd(Mi, A[i])ans = (ans + Mi * X * B[i]) % Mreturn ansdef lf(n) :return n*n*ndef pd(n) :l=1r=nwhile (l<=r) :mid=(l+r)/2if (lf(mid)==n) :return 1if (lf(mid)>n) : r=mid-1if (lf(mid)<n) : l=mid+1return 0def klf(n) :l=1r=nwhile (l<=r) :mid=(l+r)/2if (lf(mid)==n) :return midif (lf(mid)>n) : r=mid-1if (lf(mid)<n) : l=mid+1n=3
L1 = []
L2 = []
N1=514745167025222387434132377137056715954750729807151447929894289695587285793889099978536904494455862473045694392353612260528582074521711735864082380505874261026769465596315849668245703081452047808798727647904141791488099702631575692170683102622471798376397440600292225038412176681344166204027842724877162681931
N2=332459552799915544356022641605448137617079921391832222557892949808060953028449422328281413629912335051440744955455010851012308918294549765005480121061697711447087615327860789708246235156912421474047484838827777697938563515420810650393553528058831317409340577149233554235346445890238642955390137465511286414033
N3=665701912162243069059653781669230805473457427767514323262762891771122352328706695409103713864384833437438648120217615990765220365745013739246022203593234785338178963805463643869398986119431772931646042972240277833431035018628949924813463553419243108837309078316455504749755062865258063926243606206806549969161
C1=159610386572167689266326385036487109027500941380400104125191262882664358398577536610497671009102596940624920315091422093100238619835848693651492344785000232303139338861093680138737091249739575100655219967271819921458016154329847843423233652818852580016834561970850695063090000199448970052668647861992230109134
C2=80704323590708576386562863656130406931573788060159775931074197125212042930440694778363300836637666152530601069635539711403775897104413839059003511049631024172974390473641408894970527777947213128650545118958630567223577806350516381008539951304600069024003674444114727988917350720932569342357635015732615468372
C3=290728542387622789691059470283422806073663108257730190721270583629901119139049111765276898786687400514004023098315787810926656039376046957101984075353288285867739293190825676944209163087896697394093577432590616749562076462942759742984949258019827469729922204479107792698042941392668070743176808454529741938138
L1.append(C1)
L1.append(C2)
L1.append(C3)
L2.append(N1)
L2.append(N2)
L2.append(N3)
C=solve(n, L2, L1)
N=N1*N2*N3ans=0
for k in range(1000010) :if pd(k*N+C)==1 :ans=klf(C)break
# 得到16进制的明文
v=[]
while (ans) :v.append(ans%16)ans=ans/16
len0=len(v)for i in range(len(v)/2) :tmp=v[i]v[i]=v[len0-i-1]v[len0-i-1]=tmp
# 输出数字
for i in range(len(v)) :print(v[i]),
print# 将数字转换为对应的Base64字符
string = ""
for i in range(len(v)/2) :num=0for j in range(2) :num=v[2*i+j]+num*16# 字符串string = string + chr(num)# 输出数字对应的Base64字符,且在python 2中输出不换行,不加空格:# sys.stdout.write(chr(num))
print string
# Base64解码
import base64
print(base64.b64decode(string.encode()))

实验结果截图:

解密后的结果是:Einladung zu meiner Geburtstagsparty. Die Party findet am 20.12.2012 in Bletchley statt.
这是德语,意思是“邀请你来参加我的生日派对。2012年12月12日在布莱奇利举行的派对”。
而题目中的问题是派对的地址,所以答案就是Bletchley。

逆向工程实验——pre6(汇编、Android逆向、RSA算法破解)相关推荐

  1. 《教我兄弟学Android逆向07 IDAPro破解第一个so》

    上一篇 < 教我兄弟学Android逆向06 用AndroidStudio编写第一个so>我带你用AS编写了第一个so 现在回顾一下 首先我创建了一个myJNI类 里面包含一个Native ...

  2. 逆向工程实验_lab1(Pyhon逆向)

    文章目录 1.tuts 4 you论坛注册 2.elfpass文件破解 3.逆向win.pyc 4.cmpy2.exe逆向py 5. (选做)crackme文件 1.tuts 4 you论坛注册 去下 ...

  3. Android逆向 Frida 算法转发

    *.前置条件 frida 服务 python支持库 fastapi,uvicorn,frida 1. 开启服务 # 指定非标准端口 adb shell su ./data/local/tmp/fsar ...

  4. JS逆向-RSA算法加密(房天下模拟登录)

    前言 该文章为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系作者删除~ 需求 模拟登录 目标网站:aHR0cHM6Ly9wYXNzcG9ydC5mYW5 ...

  5. Android逆向之旅---破解某应用加密算法(动态调试so和frida hook so代码)

    一.样本静态分析 最近有位同学发了一个样本给我,主要是有一个解密方法,把字符串加密了,加解密方法都放在so中,所以之前也没怎么去给大家介绍arm指令和解密算法等知识,正好借助这个样本给大家介绍一些so ...

  6. Android逆向笔记(二) -- 破解AutoR的注册码验证

    文章目录 0x0 前言 0x1 界面分析 0x2 反编译 0x3 破解 0x0 前言 此软件为练习用Crackme.apk 仅有验证逻辑 此软件不同于普通的Android软件, 是由Mono for ...

  7. Android逆向之旅---破解某支付软件防Xposed等框架Hook功能检测机制

    一.情景介绍 最近想写几个某支付软件的插件,大家现在都知道现在插件大部分都是基于Xposed的hook功能,包括之前写了很多的某社交软件的插件,所以不多说就直接用Jadx打开支付软件之后然后找到想要h ...

  8. 密码学实验-RSA算法的几种攻击方法的实现

    目录 一.实验要求 (一) RSA算法攻击方法分析相关要求 二.代码简述 (一)费马方法 1.原理: 2.代码分析 (二)pollard rho 方法 1.原理 2.代码分析: (二)pollard ...

  9. RSA算法详解及攻击原理分析-附攻击范例

    文章目录 RSA算法 1.算法背景 1.1 公钥密码 1.2 公钥体制数学基础 1.3 公钥通信的流程 2.RSA算法数学基础 2.1 RSA相关的数论基础 2.2 欧拉定理及推广 3. RSA算法构 ...

最新文章

  1. jbpm 6 vs activities 5评估(持续更新、亲测实际项目评估)
  2. “好像在哪见过你”现在有了科学解释,一群脑细胞帮你回忆那张脸 | Science
  3. dos下常用网络相关命令解释
  4. 回调函数与DOM事件
  5. IOS UIPageController
  6. 20145302张薇《Java程序设计》实验三报告
  7. oracle数据库中分区表的效果是,分区表实际测试中发现并没有什么效果,帮忙看看问题出在哪里了???...
  8. 四川大学2015年数学分析考研试题
  9. 基于asp.net mvc的近乎产品开发培训课程(第四讲)
  10. 【LeetCode】【数组】题号:*289,生命游戏
  11. Kurento Tree 简介
  12. paip.流程图的图形化编程及源码生成时序图
  13. Java SE 原生数据类型
  14. php获取qq音乐的api类,QQ音乐api接口梳理
  15. java实习第一天日志
  16. 大名鼎鼎的挖掘鸡最新版本6.5
  17. fmpeg分析视频gop的大小
  18. android开机动画切换
  19. QT: 为自己的QT程序添加一个登录界面
  20. 【Python】条件变量、信号变量、事件

热门文章

  1. 南卡和OPPO蓝牙耳机哪个更好?高性价比蓝牙耳机评测
  2. 文件岛 服务器大陆,重回文件岛《究极数码暴龙》奇遇系统解析
  3. C++ 线性表的结构体定义(顺序表和链式表)
  4. 微信Web APP应用
  5. android手机固件升级原理,为什么常说Android手机千万别频繁的系统升级,背后的真实原因?...
  6. 橙瓜码字多端同步、十份云储存本地实时备份,最放心的码字软件
  7. 银行信用卡客户价值分析(Python数据分析)
  8. 读 Samuel Enoch Stumpf 之《西方哲学史》
  9. Maximal submatrix(直方图最大矩阵)
  10. rpm安装包安装方式