入门学习逆向的个人笔记,预览(欢迎探讨)
[原创] 肉丝的r0env2022(kali linux)配置xrdp远程桌面,以及Genymotion安卓11的ssh登陆问题和11系统amr64转译问题.
[分享] (Android) 逆向入门记录 一篇杂文, 记录.
[分享] Adndroid逆向的基础环境准备 的一些记录 , 抄袭royuse的课 第3课
[分享] 安卓逆向课题之4, Activity,service,content provider,broadcast receiver和实例演示,完毕
[分享] 安卓逆向课题之5, mobile spider get started. 两天高度集中学习, 承上启下的流程,a明白,b上手练.(5th完结)
[分享] 安卓逆向课题之6, mobile spider get started. Object的自动化动态分析和快速定位(笔记完毕)
[分享] 安卓逆向课题之7, mobile spider get started. 看电视直播App(未加固)去广告升级 (笔记待完善)
[分享] 安卓逆向课题之8, 真实App实操带壳App重打包去强制升级(部分抄袭别人笔记)(一次不完美的实践)
[讨论] Android Reverse Project No.9, "Types of App Security Protection, Identification and Handling Methods"
[原创] Android Reverse Project No.9, "Types of App Protection“ ---- Video Course
源码,以及Jeb,jadx 反编译的源码对比
Jeb 给出了 editText 资源的位置.对象的位置
jadx 相同的对象, editText 资源的位置, 只是分离显示,一个商业软件, 一个免费软件的差异.
ID资源会放在 Resources.arsc 里面 ( 上图 )
app-debug.apk source code in Android Studio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package com.example.myapplication1105;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
EditText username_et;
EditText password_et;
TextView message_tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
password_et = (EditText) this .findViewById(R.id.editText2);
username_et = (EditText) this .findViewById(R.id.editText);
message_tv = ((TextView) findViewById(R.id.textView));
this .findViewById(R.id.button).setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
if (username_et.getText().toString().compareTo( "admin" ) == 0 ) {
message_tv.setText( "You cannot login as admin" );
return ;
}
message_tv.setText( "Sending to the server :" + Base64.encodeToString((username_et.getText().toString() + ":" + password_et.getText().toString()).getBytes(), Base64.DEFAULT));
}
});
}
}
|
app-debug.apk Decompile code in JEB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package com.example.myapplication1105;
import android.os.Bundle;
import android.util.Base64;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
TextView message_tv;
EditText password_et;
EditText username_et;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
this .setContentView( 0x7F0B001C );
this .password_et = (EditText) this .findViewById( 0x7F080098 );
this .username_et = (EditText) this .findViewById( 0x7F080097 );
this .message_tv = (TextView) this .findViewById( 0x7F080181 );
this .findViewById( 0x7F080058 ).setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
if (MainActivity. this .username_et.getText().toString().compareTo( "admin" ) == 0 ) {
MainActivity. this .message_tv.setText( "You cannot login as admin" );
return ;
}
MainActivity. this .message_tv.setText( "Sending to the server :" + Base64.encodeToString(MainActivity. this .username_et.getText().toString() + ":" + MainActivity. this .password_et.getText().toString().getBytes(), 0 ));
}
});
}
}
|
通过对比, Jeb编译代码和source code 接近90%相同
病毒代码对比
GDA 4.10 的对比,
在app入口的 代码的解释
对比, jeb/jadx/jda class name 命名差别不大
kali must install chinese font. font will display currect.
use jeb , decompile, MainActivity(). the key comment after code per line.
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle arg14) {
LogCatBroadcaster.start( this );
super .onCreate(arg14);
this .startService( new Intent( this , MyServiceOne. class ));
}
}
|
所有核心逻辑全部都在--com.shimeng.qq2693533893 的-- MyServiceOne(class) 这个类里面.
恶意代码中找到,修改系统的配置,太黑了.
又是上述3项对比
静态, 动态,要一起看.
手工脱壳,基本不可用, win时代可以, 现在基本都是 自动脱壳机,修改ART的源码.
vmp 确实是手脱.
调试病毒
APP启动后,usb 被强制断开
管理 android虚拟机的快照功能.
如果需要还原,
点击关闭窗口,即可恢复快照.
运行如下命令即可 启动恢复好的环境.
1 | "C:\Program Files\Genymobile\Genymotion\player.exe" --vm-name 706eac57-e1eb-4541-9a75-242d5a7ae25e --resume
|
函数解析 USBLock( )函数
代码解释如下
目前的分析状态就是在有source code 基础上的分析.
在royuse的课程分析中, 变量 v8, intent 2, intent12,没有一个是对的, 在 3年后的今天, 在 jeb 2022版本中,改进了, 课程中出现的 问题, 解析明确. 上图中,已经明显没了课程中的错误.
反编译
问题依旧, 只能作为参考.
使用Frida 打印 Intent 内容
决定都是hook, 看他有没有经过这里, 动态hook时候, 的参数,调用栈,返回值, print 出来, 才是真的结果.
通过注册自己的启动信号到系统的广播事件, 功能如下.
你掌握的技能,(目前知道这个软件源代码的情况下,你需要能够开发出这个勒索软件)你才能真正掌握他的构成.
首先,你能开发一个恶意app ,你才能开发他的hook.
你能开发他的hook ,你就能控制他的所有行为了.
本地安装termux su root, 下 执行 frida-server. 通过ip链接.不会收到病毒影响. 绕过adb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | vbox86p: /data/local/tmp
Usage:
frida [OPTION?]
Help Options:
帮助选项:
-h, --help Show help options 显示帮助选项
Application Options:
应用程序选项:
--version Output version information and exit
显示版本信息并退出
-l, --listen=ADDRESS Listen on ADDRESS
在 ADDRESS 上监听
--certificate=CERTIFICATE Enable TLS using CERTIFICATE
使用 CERTIFICATE 启用 TLS
--origin=ORIGIN Only accept requests with ?Origin? header matching ORIGIN (by default any origin will be accepted)
仅接受具有匹配 ORIGIN 的 "Origin" 标头的请求(默认情况下,将接受任何来源)
--token=TOKEN Require authentication using TOKEN
要求使用 TOKEN 进行身份验证
--asset-root=ROOT Serve static files inside ROOT (by default no files are served)
提供 ROOT 内的静态文件(默认情况下不提供文件)
-d, --directory=DIRECTORY Store binaries in DIRECTORY
将二进制文件存储在 DIRECTORY 中
-D, --daemonize Detach and become a daemon
分离并成为守护进程
--policy-softener=system|internal Select policy softener
选择策略软化器
-P, --disable-preload Disable preload optimization
禁用预加载优化
-C, --ignore-crashes Disable native crash reporter integration
禁用本地崩溃报告程序集成
- v , --verbose Be verbose
显示详细信息
|
1 2 | . /frida-server-15 .2.2-android-x86 -l 0.0.0.0:8888 // 代表监听所有地址本地和公网的8888端口
// 服务端操作
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | └─
Usage: objection [OPTIONS] COMMAND [ARGS]...
_ _ _ _
___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_| _| _| | . | |
|___|___| |___|___|_| |_|___|_|_|
|___|(object)inject(ion)
Runtime Mobile Exploration
by: @leonjza from @sensepost
By default, communications will happen over USB, unless the --network option
is provided.
Options:
-N, --network Connect using a network connection instead of USB.
-h, --host TEXT [default: 127.0.0.1]
-p, --port INTEGER [default: 27042]
-ah, --api-host TEXT [default: 127.0.0.1]
-ap, --api-port INTEGER [default: 8888]
-g, --gadget TEXT Name of the Frida Gadget /Process to connect to.
[default: Gadget]
-S, --serial TEXT A device serial to connect to.
-d, --debug Enable debug mode with verbose output. (Includes
agent source map in stack traces)
--help Show this message and exit .
Commands:
api Start the objection API server in headless mode.
device- type Get information about an attached device.
explore Start the objection exploration REPL.
patchapk Patch an APK with the frida-gadget.so.
patchipa Patch an IPA with the FridaGadget dylib.
run Run a single objection command .
signapk Zipalign and sign an APK with the objection key.
version Prints the current version and exists.
|
objection 的链接方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | objection -N -h 192.168.31.35 -p 8888 -g com.android.settings explore
adb connect 192.168.31.35
vbox86p:/
root 8014 2644 64896 46392 poll_schedule_timeout f17dabb9 S frida-server-15.2.2-android-x86
vbox86p:/
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 8014 /frida-server-15 .2.2-android-x86
tcp 0 0 192.168.31.35:8888 192.168.31.81:44284 ESTABLISHED 8014 /frida-server-15 .2.2-android-x86
vbox86p:/
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN root 67365 8014 /frida-server-15 .2.2-android-x86
tcp 0 0 192.168.31.35:8888 192.168.31.81:44284 ESTABLISHED root 66512 8014 /frida-server-15 .2.2-android-x86
unix 3 [ ] STREAM CONNECTED 67377 8014 /frida-server-1 @ /data/local/tmp/re .frida.server /pipe-094e09da4080d0b09b2dd8a9f89d22a7
unix 3 [ ] STREAM CONNECTED 67419 8014 /frida-server-1
unix 3 [ ] STREAM CONNECTED 67375 8014 /frida-server-1 @ /data/local/tmp/re .frida.server /pipe-c46232b81779e52cfde9e28c56dfb8f1
vbox86p:/
frida-ser 8014 root 7u IPv4 0t0 67365 TCP :8888->:0 (LISTEN)
frida-ser 8014 root 8u IPv4 0t0 66512 TCP :8888->:44284 (ESTABLISHED)
vbox86p:/
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 8014 /frida-server-15 .2.2-android-x86
tcp 0 0 192.168.31.35:8888 192.168.31.81:44284 ESTABLISHED 8014 /frida-server-15 .2.2-android-x86
|
反向查找开放的端口(特定的APP)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | . /frida-server-15 .2.2-android-x86 -l 0.0.0.0 // 代表监听所有地址本地和公网的默认端口
// 服务端操作
vbox86p:/
tcp 0 0 0.0.0.0:27042 0.0.0.0:* LISTEN 12098 /frida-server-15 .2.2-android-x86
// find the listenning port is 27042
vbox86p:/
root 12098 2644 56588 44860 poll_schedule_timeout f6cbbbb9 S frida-server-15.2.2-android-x86 // find the process ID
vbox86p:/
tcp 0 0 0.0.0.0:27042 0.0.0.0:* LISTEN 12098 /frida-server-15 .2.2-android-x86 // find the process port
vbox86p:/
frida-ser 12098 root 7u IPv4 0t0 73142 TCP :27042->:0 (LISTEN) // Filter the port of Frida-server.
未知的app得到它监听的端口, 通过这样的方法.
|
启动恶意 app 后 USB被强制断开
Package Name: com.shimeng.qq2693533893
根据进程名开始hook
晕, 怎么没有进程名?
恶意app 还在跑呀.
跑起来了.
1 2 3 | ┌──(root㉿r0env)-[~]
└─
// load hluwa"s plugins for Frida.
|
trace特定的class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | com.shimeng.qq2693533893 on (Google: 8.1 . 0 ) [net] # plugin wallbreaker classdump com.shimeng.qq2693533893.MyBroadcast --fullname
package com.shimeng.qq2693533893
class MyBroadcast {
实例方法
void onReceive(android.content.Context, android.content.Intent);
}
|
重点关注 onReceive 方法
依然和 MyServices 方法关联
1 | com.shimeng.qq2693533893 on (Google: 8.1.0) [net]
|
Tracer功能就是 跟踪按钮 点击后, 调用了哪些 function.
Trace 他的执行路径. 通过静态和动态分析确定他的主要逻辑在哪里?
最后还需要验证,
就是将这个类给hook上, 主要功能就在 MyServicesOne里面.
于是将此类的所有方法都 hook 上了.
hook 大意, 在函数上挂了个钩子, 只要函数执行一次, 就会有个记录.
钩子可以把 参数替换掉, 返回值可以替换掉, 钩子也可以把调用栈打映出来.
从哪里到这里的 调用栈 print 出来.
如果不懂,加上调用栈,
1 | android hooking watch class com.shimeng.qq2693533893.MyServiceOne --dump-backtrace
|
探测这个类是从哪里过来被调用的.
1 2 | android hooking watch class com.shimeng.qq2693533893.MyServiceOne.access$L1000018 --dump-
backtrace
|
修改如下
1 2 | android hooking watch class_method com.shimeng.qq2693533893.MyServiceOne.access$L1000018
--dump-backtrace
|
擦找 100018 是什么?
1 2 3 4 5 6 7 8 9 10 11 | (agent) [415183] Called com.shimeng.qq2693533893.MyServiceOne.access$L1000018(com.shimeng.qq2693533893.MyServiceOne)
(agent) [415183] Backtrace:
com.shimeng.qq2693533893.MyServiceOne.access$L1000018(Native Method)
com.shimeng.qq2693533893.MyServiceOne$100000007.run(MyServiceOne.java:309)
android.os.Handler.handleCallback(Handler.java:790)
android.os.Handler.dispatchMessage(Handler.java:99)
android.os.Looper.loop(Looper.java:164)
android.app.ActivityThread.main(ActivityThread.java:6494)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
|
第一个可疑函数 com.shimeng.qq2693533893.MyServiceOne.access$L1000018**
第二个可疑函数com.shimeng.qq2693533893.MyServiceOne$100000007
一下是代码解读
1 2 3 4 5 6 7 8 9 | MyServiceOne.this.hand2.postDelayed(this, 1800L);:在 MyServiceOne 实例的 hand2 上使用 postDelayed 方法,定时执行当前的 Runnable 实例(即自身),间隔为 1800 毫秒。
AudioManager v1 = (AudioManager)MyServiceOne.this.getSystemService( "audio" );:获取应用程序的音频管理器。
v1.setStreamVolume(3, v1.getStreamMaxVolume(3), 4);:设置第 3 个音频流的音量为最大值。
v1.getStreamMaxVolume(0); 和 v1.getStreamVolume(0);:获取第 0 个音频流的最大音量和当前音量。
((Vibrator)MyServiceOne.this.getApplication().getSystemService( "vibrator" )).vibrate(new long[]{100L, 1500L, 100L, 1500L}, -1);:使用应用程序的振动器服务(Vibrator)执行振动,传递了振动模式和振动重复次数。
|
这里就找到了嫌疑.
下一步确认解密的地方.
继续hook 该类下 ,所有的方法.
1 | com.shimeng.qq2693533893 on (Google: 8.1.0) [net]
|
以便于 点击按钮,监控调用栈
通过调用栈知道是音量的+ 号.
显示的调用栈,
代表了调用两次流程.
经过了这类的方法体,他的执行流程,
通过过滤得到
在 Jadx-gui 中 执行流程基本一致
判断第一个函数 ”颜如玉“ 是从哪里调用过来的.
从新开一个 objection 进程,探测一个类的名字.
1 2 3 4 | objection -N -h 192.168.31.202 -g com.shimeng.qq2693533893 explore -P ~/.objection /plugins
android hooking watch class_method com.shimeng.qq2693533893.MyServiceOne.颜如玉 --dump-ar
gs --dump-backtrace --dump- return
|
验证猜想
输入 2009 , 再次点击, 出现了调用栈
难道从 100000xxx 0002过来的?
其实就是一个匿名的内部类.
New onClick. 就是一个内部类.
100000002代码的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | class 100000002 implements View.OnClickListener {
private final MyServiceOne this $ 0 ;
private final TextView val$a00;
private final TextView val$b00;
private final Button val$button;
private final TextView val$c00;
private final TextView val$d00;
private final EditText val$editText;
private final TextView val$lpo;
private final TextView val$textView2;
private final int val$诗梦;
100000002 (EditText arg16, int arg17, TextView arg18, Button arg19, TextView arg20, TextView arg21, TextView arg22, TextView arg23, TextView arg24) {
this .val$editText = arg16;
this .val$诗梦 = arg17;
this .val$lpo = arg18;
this .val$button = arg19;
this .val$textView2 = arg20;
this .val$a00 = arg21;
this .val$b00 = arg22;
this .val$c00 = arg23;
this .val$d00 = arg24;
}
static MyServiceOne access$ 0 ( 100000002 arg4) {
return MyServiceOne. this ;
}
@Override
public void onClick(View arg15) {
String v2 = this .val$editText.getText().toString();
if (v2.length() >= 3 ) {
String v3 = MyServiceOne.颜如玉(颜如玉QQ2693533893.getSaltMD5( "" + this .val$诗梦 )).replaceAll( "\\D+" , "" );
if (v3.length() > 9 && v3.length() > 3 ) {
StringBuffer v6 = new StringBuffer();
StringBuffer v7 = new StringBuffer().append( "9DDEB743E935CE399F1DFAF080775366" );
if (v6.append(颜如玉QQ2693533893.getSaltMD5(MyServiceOne.颜如玉(v2.substring( 0 , 3 ))))
.append(v2.substring( 3 , v2.length())).toString()
.equals(v7.append(v3.substring( 0 , 9 )).toString())) {
MyServiceOne. this .util.removeView();
MyServiceOne. this .sm2();
return ;
}
--MyServiceOne. this .L;
++MyServiceOne. this .B;
this .val$lpo.setText( "很抱歉!密码错误解锁请加QQ群:437732815联系管理员购买正确的密码解锁...密码以错"
+ MyServiceOne. this .B + "次!" + "还剩" + MyServiceOne. this .L + "次输入错误密码的机会" );
if (MyServiceOne. this .L <= 0 ) {
this .val$lpo.setText( "本次输入密码错误次数累计以达10次,请重启手机后获取输入密码机会!" );
this .val$button.setVisibility( 8 );
this .val$textView2.setVisibility( 8 );
this .val$editText.setVisibility( 8 );
this .val$a00.setVisibility( 8 );
this .val$b00.setVisibility( 8 );
this .val$c00.setVisibility( 8 );
this .val$d00.setVisibility( 8 );
return ;
}
}
}
}
}
|
搜索字符串是纯粹碰运气
"解除锁定" 搜索不到
“识别码” 搜索到
搜索字符串是纯粹碰运气,
以上的流程才是科学的方法. 你经过哪个class ,从这个class print BackTrace. 一步一步来,往上搜索. 搜到真正调用的地方. 这才是真正科学的方法.
内存是不会骗人的.
可以看到每个反编译, Decompile Application, 都有自己的想法.
病毒,其实把所有的逻辑都写在了services里面.
总结
Xpose 可以hook系统启动的一些的函数, 可以随着ART一起启动.
Frida 做不到. 必须手动启动.
Xpose 天生和系统集成的.
静态分析是辅助.
动态分析是主力.
Jar 包的概念.
编写插件时候,需要引入一个依赖.
会有一个 XposedBridge 的 jar 包.
app 开发时候,总是需要引入第三方 动态库, 本质是一个jar包.
你开发的所有组件不可能自己去写, 而是寻找有些的框架,节省开发时间.
Jar包是有 source code 的.
一定要懂开发, 否则后面寸步难行.
图片动态加载, 一致加载, 就不用看了.
source code is open source. 那就不用看了.
com.google.android.exoplayer2.source这样的包名的组件就不用看了.
开发的高度决定逆向的高度
《android软件安全权威指南》
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2024-1-19 23:03
被calleng编辑
,原因: 编辑 样式, 修改标题和结构