乱码三千 – 分享实用IT技术

乱码三千 – 码出一个新世界


  • 首页

  • 归档

  • 搜索

Android常见App加固厂商脱壳方法的整理

发表于 2020-10-27

目录简述(脱壳前学习的知识、壳的历史、脱壳方法)

  • 第一代壳
  • 第二代壳
  • 第三代壳
  • 第N代壳

    简述Apk文件结构Dex文件结构壳史壳的识别Apk文件结构

1

Dex文件结构

2

壳史

第一代壳 Dex加密

  1. Dex字符串加密
  2. 资源加密
  3. 对抗反编译
  4. 反调试
  5. 自定义DexClassLoader

第二代壳 Dex抽取与So加固

  1. 对抗第一代壳常见的脱壳法
  2. Dex Method代码抽取到外部(通常企业版)
  3. Dex动态加载
  4. So加密

第三代壳 Dex动态解密与So混淆

  1. Dex Method代码动态解密
  2. So代码膨胀混淆
  3. 对抗之前出现的所有脱壳法

第四代壳 arm vmp(未来)

  1. vmp

壳的识别

1.用加固厂商特征:

  • 娜迦: libchaosvmp.so , libddog.solibfdog.so
  • 爱加密:libexec.so, libexecmain.so
  • 梆梆: libsecexe.so, libsecmain.so , libDexHelper.so
  • 360:libprotectClass.so, libjiagu.so
  • 通付盾:libegis.so
  • 网秦:libnqshield.so
  • 百度:libbaiduprotect.so
  • 腾讯御安全: libshell-super.so, libshella.so

2.基于特征的识别代码

3

第一代壳

  1. 内存Dump法
  2. 文件监视法
  3. Hook法
  4. 定制系统
  5. 动态调试法

内存Dump法

  • 内存中寻找dex.035或者dex.036
  • /proc/xxx/maps中查找后,手动Dump

4

android-unpacker https://github.com/strazzere/android-unpacker

5

  • drizzleDumper https://github.com/DrizzleRisk/drizzleDumper

  • 升级版的android-unpacker,read和lseek64代替pread,匹配dex代替匹配odex

6

IDA Pro + dumpDEX**dumpDex https://github.com/CvvT/dumpDex**

7

文件监视法

  • Dex优化生成odex
  • inotifywait-for-Android https://github.com/mkttanabe/inotifywait-for-Android
  • 监视文件变化

8

  • notifywait-for-Android https://github.com/mkttanabe/inotifywait-for-Android
  • 监视DexOpt输出

9

10

Hook法

  • Hook dvmDexFileOpenPartial
  • http://androidxref.com/4.4_r1/xref/dalvik/vm/DvmDex.cpp

11

12

定制系统

  • 修改安卓源码并刷机

13

  • DumpApk https://github.com/CvvT/DumpApk
  • 只针对部分壳

14

动态调试法

  • IDA Pro

15

16

17

gdb gcore法

1
.gdbserver :1234 –attach pid .gdb (gdb) target remote :1234 (gdb) gcore

coredump文件中搜索“dex.035”

18

第二代壳

  1. 内存重组法
  2. Hook法
  3. 动态调试
  4. 定制系统
  5. 静态脱壳机

内存重组法

Dex篇

ZjDroid http://bbs.pediy.com/showthread.php?t=190494

对付一切内存中完整的dex,包括壳与动态加载的jar

19

20

so篇

elfrebuild

21

22

构造soinfo,然后对其进行重建

23

24

Hook法

针对无代码抽取且Hook dvmDexFileOpenPartial失败

Hook dexFileParse

http://androidxref.com/4.4_r1/xref/dalvik/vm/DvmDex.cpp

25

https://github.com/WooyunDota/DumpDex

26

针对无代码抽取且Hook dexFileParse失败

Hook memcmp

http://androidxref.com/4.4_r1/xref/dalvik/vm/DvmDex.cpp

27

28

定制系统

修改安卓源码并刷机-针对无抽取代码

https://github.com/bunnyblue/DexExtractor

29

Hook dexfileParse

30

31

DexHunter-最强大的二代壳脱壳工具

https://github.com/zyq8709/DexHunter

DexHunter的工作流程:

32

DexHunter的工作原理:

33

绕过三进程反调试

http://bbs.pediy.com/showthread.php?p=1439627

34

35

修改系统源码后:

36

http://www.cnblogs.com/lvcha/p/3903669.html

37

1
ls /proc/345/task

38

./gdbserver :1234 –attach346 … (gdb) gcore

1
2


gcore防Dump解决方案:

http://bbs.pediy.com/showthread.php?t=198995

断点mmap调试,针对Hook dexFileParse无效

原理: dexopt优化时, dvmContinueOptimization()->mmap()

39

静态脱壳机

分析壳so逻辑并还原加密算法

http://www.cnblogs.com/2014asm/p/4924342.html

40

自定义linker脱so壳

https://github.com/devilogic/udog

main() -> dump_file()

1
2


41

第三代壳

  1. dex2oat法
  2. 定制系统

dex2oat法

ART模式下,dex2oat生成oat时,内存中的DEX是完整的

http://bbs.pediy.com/showthread.php?t=210532

43

定制系统

Hook Dalvik_dalvik_system_DexFile_defineClassNative

枚举所有DexClassDef,对所有的class,调用dvmDefineClass进行强制加载

44

第N代壳so + vmp动态调试 + 人肉还原

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

apk逆向之so库的破解和调用

发表于 2020-10-27

当你习惯了python的便捷,在逆向安卓是碰到so库的加密分析时,除了使用ida进行函数分析之外,so库的调用也成了一个问题,尤其是遇到只有arm架构的so时,明显感觉到头疼

so库调用方式尝试

1.重新编写一个app进行so库的调用 (不推荐)

痛点:

  • 前期开发费时费力,
  • 开发完毕后只能运行在真机或者模拟器,
  • 如果要执行定时任务相较python或者javaweb而言效率和性能相差太大

2.使用javaweb的形式进行调用(局限性太大,若熟悉树莓派可优选)

优势:

  • 调试方便
  • 可拉进服务器直接定时执行

痛点:

  • 如果使用windows的服务器那么只支持x86的so库, linux的服务器同样不支持arm的so库,即便使用docker也无法运行arm的容器, 除非服务器系统是arm架构, 目前现存的arm系统除了android之外估计都在树莓派了

3.使用python调用so库(若熟悉树莓派可优选)

痛点:

  • 和上面的问题一样, 不支持arm的so库

4.使用 python第三方库 AndroidNativeEmu 调用so(观望)

  • 项目连接:https://github.com/AeonLucid/AndroidNativeEmu

  • 简介: AndroidNativeEmu是基于Unicron实现的一个指令解析器, 让您能够使用python跨平台模拟安卓虚拟机环境

  • 使用资料参考:

    • https://blog.csdn.net/zhangmiaoping23/article/details/101708171
    • https://blog.csdn.net/qq_26914291/article/details/103395857#Invalid%20get_reference%28%25d%29%E9%97%AE%E9%A2%98
    • https://www.anquanke.com/post/id/95199

痛点:

  • 该库目前只支持简单的函数调用,很多功能还未实现,期待后期的发展

5. 使用unidbg(推荐)

优势:

  • 虚拟jvm环境,支持arm32和arm64的so库
  • 弥补了上面javaweb的不足

痛点:

  • 该库的原理是从so中寻找函数,面对动态加载的so比较麻烦, 不如原生System.loadXXX加载方便

总结

有条件折腾的话还是选择树莓派吧

小记

  1. 最近在ubuntu x86 32位的系统上启动springboot调用so库,出现以下问题:
1
/tmp/lib/jniLibs/libkey.so: libstdc++.so: cannot open shared object file: No such file or directory

由于自身能力有限,对C语言不熟,暂时搁置, 来日再处理

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

android原生shell中端可使用的命令

发表于 2020-10-27

android系统本身属于简化版的linux, 有些功能缺失,很多linux命令用不了,这边总结几个可以使用的命令

首先

1.使用adb进入shell中端

1
adb -s 设备名 shell

2.更改读写权限,否则无法操作指令

1
mount -o remount ,rw /

可用指令

  1. ping
1
ping baidu.com
  1. cat
1
2
cat xxx.txt //查看文件
cat /proc/cpuinfo //查看cpu信息
  1. curl
1
curl -o xxxxx //下载文件
  1. mv
1
mv ./a.txt  ../  //移动文件
  1. date
1
date //查看当前时间
  1. top
1
top //查看当前系统资源消耗
  1. 调用Dalvik VM执行apk文件

创建java文件

1
2
3
4
5
public class Foo{
public static void main(String [] args){
System.out.println("Hello, world!haha");
}
}

将.java转换成.class文件

1
dx --dex --output=foo.apk Foo.class

安装到设备上

1
adb push foo.apk /sdcard/

调用Dalvik VM执行foo.apk

1
2
3
adb shell

dalvikvm -cp /sdcard/foo.apk Foo

  1. 查看当前Activity信息
1
adb shell dumpsys activity top   //可快速定位当前activity路径
  1. 命令行启动调试模式
1
adb shell am start -D -n packagename/ MainActivity
  1. 获取进程pid
1
ps | grep packagename
  1. 建立端口转发
1
adb forward tcp:8700 jdwp:pid
  1. 查看设备参数
1
getprop  #该指令可以查看设备所有信息 包括cpu 网络 model等等
  1. 查看设备可用命令
1
cd system/bin  #该目录下可以查看所有可用的指令

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

android在adb shell进入linux之后mkdir提示Read-only file system解决方法

发表于 2020-10-26

前提是你手机拥有root权限, 这边以模拟器为例

1.进入shell模式

1
adb shell

2.切换root用户

1
su

3.更改system权限

1
mount -o remount ,rw /

4.接下来即可成功创建文件夹

1
mkdir mydir

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

linux 压缩和解压缩命令gz、tar、zip、bz2

发表于 2020-10-26

gzip

  • 压缩后的格式为:*.gz
  • 这种压缩方式不能保存原文件;且不能压缩目录
  • 命令举例:
1
2
3
4
5
6
7
8
#压缩
[root@localhost tmp]# gzip buodo
[root@localhost tmp]# ls
buodo.gz
#解压
[root@localhost tmp]# gunzip buodo.gz
[root@localhost tmp]# ls
buodo12345678

tar

  • 命令选项:
1
2
3
4
5
6
-z(gzip)      用gzip来压缩/解压缩文件
-j(bzip2) 用bzip2来压缩/解压缩文件
-v(verbose) 详细报告tar处理的文件信息
-c(create) 创建新的档案文件
-x(extract) 解压缩文件或目录
-f(file) 使用档案文件或设备,这个选项通常是必选的。123456
  • 命令举例:
1
2
3
4
5
6
7
#压缩
[root@localhost tmp]# tar -zvcf buodo.tar.gz buodo
[root@localhost tmp]# tar -jvcf buodo.tar.bz2 buodo

#解压
[root@localhost tmp]# tar -zvxf buodo.tar.gz
[root@localhost tmp]# tar -jvxf buodo.tar.bz21234567

zip

  • 与gzip相比:1)可以压缩目录; 2)可以保留原文件;
  • 选项:
1
-r(recursive)    递归压缩目录内的所有文件和目录1
  • 命令举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#压缩和解压文件
[root@localhost tmp]# zip boduo.zip boduo
[root@localhost tmp]# unzip boduo.zip

#压缩和解压目录
[root@localhost tmp]# zip -r Demo.zip Demo
adding: Demo/ (stored 0%)
adding: Demo/Test2/ (stored 0%)
adding: Demo/Test1/ (stored 0%)
adding: Demo/Test1/test4 (stored 0%)
adding: Demo/test3 (stored 0%)
[root@localhost tmp]# unzip Demo.zip
Archive: Demo.zip
creating: Demo/
creating: Demo/Test2/
creating: Demo/Test1/
extracting: Demo/Test1/test4
extracting: Demo/test3 123456789101112131415161718

bzip2

  • 压缩后的格式:.bz2
  • 参数
1
-k    产生压缩文件后保留原文件1
  • 命令举例
1
2
3
4
5
6
#压缩
[root@localhost tmp]# bzip2 boduo
[root@localhost tmp]# bzip2 -k boduo

#解压
[root@localhost tmp]# bunzip2 boduo.bz2

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

adb shell中设置android系统linux内部环境变量

发表于 2020-10-26

Android系统本身就是基于linux系统, 有时候我们需要给其配置环境变量,如下操作:

配置步骤

  1. 进入shell环境
1
adb shell
  1. 更改system系统为可读写
1
mount -o remount ,rw /
  1. 将 /system/etc/目录下的 mkshrc文件拉出来进行修改 该文件几位系统环境变量配置文件
1
adb -s 设备名 pull /system/etc/mkshrc  ./
  1. 使用文本编辑器编辑 mkshrc文件,找到 ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} 这么一行

    1
    ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh}
  2. 将环境变量依葫芦画瓢添加至改行尾部, 如

1
${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} ${mypath:=/system/bin/my}
  1. 然后找到export处,将自己的环境变量加到后面:

    1
    export HOME HOSTNAME MKSH SHELL TERM USER mypath
  2. 将修改完后的mkshrc文件push会Android系统进行替换

1
adb -s 设备名 push ./mkshrc /system/etc/mkshrc
  1. 进入shell环境 使用 export 命令即可查看刚刚配置的环境变量参数
1
export

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

linux查看系统架构的几种方法

发表于 2020-10-26

有时候,咱们不清楚自己安装的操作系统是什么架构的x86还是arm, 64位还是32位, 可以通过以下指令进行查询

查看是x86还是arm系统

1.uname -a 命令

1
uname -a  //如果输出带有x86字样即为x86系统 带有arm字样则位arm系统

2. file 命令

1
file /sbin/init //如果输出带有x86字样即为x86系统

查看系统是64位还是32位

1.getconf 命令

1
getconf LONG_BIT  //如果输出32即为32位系统 输出64即为64位系统

2.dpkg 命令

1
dpkg --print-architecturemkdir mydir //如果当前 Linux 是 64 位则输出 amd64,是 32 位则会输出 i386

3.arch 命令

1
arch   //如果输出 x86_64 则表示为 64 位系统,如果输出 i686 或 i386 则表示为 32 位系统

4. hostnamectl 指令

1
hostnamectl

​ 如下:image-20210914145006176

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

arm32系统的树莓派4b docker安装32位java镜像并部署springboot项目

发表于 2020-10-26

最近通过docker在树莓派上部署springboot项目,然而报错了,因为直接下载的jdk不支持。
先看一下之前的docker文件

1
2
3
4
5
FROM java:8
ADD meeting-0.0.1-SNAPSHOT.jar /meeting-1.0-SNAPSHOT.jar
EXPOSE 8084
ENTRYPOINT ["java", "-jar", "/meeting-1.0-SNAPSHOT.jar", ""]
1234

这里从docker镜像官网获取的java是64位的,树莓派32位系统不能用,所以需要自己弄一个镜像。

  1. 首先去oracle下载一个32位的arm镜像,我下载了jdk-8u241-linux-arm32-vfp-hflt.tar.gz,注意不能下载错。
  2. 将镜像解压缩并放置到Linux的/usr/lib/jvm文件夹下
  3. 打开文件~/.bashrc,增加以下内容
1
2
3
4
5
6
7
nano ~/.bashrc
1
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_241
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
1234

  1. 查看是否生效
1
2
3
source ~/.bashrc
java -version
12

  1. 编写Dockerfile文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#Docker image of JDK8 in ARM32
# VERSION 8
# Author: yuwen
#基础镜像使用的是OpenJDK官方镜像公用的
FROM buildpack-deps:stretch-scm
#作者
MAINTAINER yuwen <yuwengoku@163.com>
# Default to UTF-8 file.encoding
ENV LANG C.UTF-8
ENV JAVA_HOME /usr/local/jdk8
ENV PATH $JAVA_HOME/bin:$PATH
ENV JDK_FILE jdk-8u241-linux-arm32-vfp-hflt.tar.gz
COPY $JDK_FILE /usr/local/
RUN mkdir -p "$JAVA_HOME"; \
tar --extract \
--file /usr/local/$JDK_FILE \
--directory "$JAVA_HOME" \
--strip-components 1 \
--no-same-owner; \
rm /usr/local/$JDK_FILE
1234567891011121314151617181920

其中,from不要修改,作者可以修改,其他的不要变化即可。

  1. 将Dockerfile文件和下载的镜像一起拷贝到同一个目录,并创建镜像

    然后执行创建镜像命令:
1
2
docker build -t arm32jdk:8 .
1


\7. 创建好了以后,就可以利用这个我们自己创建的jdk镜像部署springboot项目了
我创建自己的springboot项目的Dockerfile文件:

1
2
3
4
5
FROM arm32jdk:8
ADD meeting-0.0.1-SNAPSHOT.jar /meeting-1.0-SNAPSHOT.jar
EXPOSE 8084
ENTRYPOINT ["java", "-jar", "/meeting-1.0-SNAPSHOT.jar", ""]
1234
  1. 这里我创建好自己的镜像后,创建容器还遇到了一个问题,就是容器创建默认的时区是中时区,即(UTC+0)

    那么获取的系统时间就不对,所以创建容器的时候需要指定时区为上海(UTC+8),即东八区:
1
2
docker run --name meeting -e TZ="Asia/Shanghai" -p 8084:8084 -d --restart=always meeting
1

然后进入容器重新查看时区:

1
2
3
docker exec -i -t  gateway-server /bin/bash
date -R
12

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Windows环境下使用ndk-build指令编译c/c++生成so库

发表于 2020-10-23

编译环境

  • android-ndk-r9b (这里使用老版本库为例 ,将该库目录下的ndk-build.cmd添加至系统环境变量中,方便后续操作,不同版本的ndk库ndk-build位置可能不太一样,这里你也可以使用最新库)

  • Windows10

编译步骤

  1. 第一步 创建工程文件夹, 这里以E盘下App目录为例
  2. 第二步 在App文件夹下创建子文件夹jni, 并将编写好的源代码以及Android.mk文件放入
  3. 打开命令窗口,执行编译命令
1
ndk-build.cmd  NDK_PROJECT_PATH=E:\App
  1. 等待编译 完成后会在jni同级目录下生成libs和obj两个文件夹,libs目录下存放的是编译好的so库,obj目录下存放的是.o链接库, 如果有需要,使用以下命令可以清除obj目录下文件:
1
ndk-build.cmd  clean

编译扩展配置

  1. 默认情况下编译只生成armeabi架构的so库,如果要生成x86以及其他cpu架构的so,需要在Android.mk文件所在目录下新建Application.mk文件,并加入以下配置:
1
2
3
APP_ABI := armeabi armeabi-v7a x86
或者编译所有架构
APP_ABI := all # 注意必须小写
  1. 默认情况下使用Andorid.mk作为编译脚本,如果想指定编译脚本文件可以添加APP_BUILD_SCRIPT参数进行指定:
1
ndk-build.cmd  NDK_PROJECT_PATH=E:\App APP_BUILD_SCRIPT=new_android.mk
  1. 默认情况下使用Andorid.mk同级目录下找Application.mk 如果想指定Application.mk文件路径,可以添加NDK_APPLICATION_MK进行指定:
1
ndk-build.cmd  NDK_PROJECT_PATH=E:\App NDK_APPLICATION_MK=new_application.mk

注意事项

  1. 编译时默认找jni目录下源码进行编译,所以需要在工程目录下新建jni子目录,原因在此

  2. android工程在引用so库时,必须放在对应架构目录下,否则打包apk时会忽略该文件的打包, 比如指定jni目录为libs ,引用的so是x86架构的,那么必须在libs目录下再新建一个名为x86的文件夹,然后将so放进去, 否则so库不打包

附加

ndk编译脚本文件Android.mk配置:

1
2
3
4
5
6
7
8
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := key #so库引用名称 编译时会自动添加lib前缀,最后的结果为libkey.so
LOCAL_SRC_FILES := Test.cpp # 需要编译的源码

include $(BUILD_SHARED_LIBRARY)

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

使用Android Studio快速集成SpringBoot项目

发表于 2020-10-19

起因

主要从事Android开发的小伙伴,偶尔进行后端开发,由于Android Studio是Idea的子系,直接使用Studio进行开发即可,无需另外下载Idea工具

配置环境

  • Android Studio
  • Jdk8
  • gradle-6.1.1

快速集成SpringBoot工程

  1. 第一步

前往 https://start.spring.io/下载启动demo, 我这里选择的是gradle工程java语言 已经添加web依赖

  1. 第二步 打开demo工程, 将修改gradle属性文件

    这一步为非必需操作, 由于我这边使用的是all版本, 为了节省网络资源,直接复用

  1. 第三步 调整build.gradle文件, 示例如下

    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
       buildscript {
    ext {
    springIOVersion = '1.0.10.RELEASE'
    springBootVersion = '1.5.9.RELEASE'
    }
    repositories {
    mavenLocal()
    mavenCentral()
    maven { url "http://repo.spring.io/release" }
    maven { url "http://repo.spring.io/milestone" }
    maven { url "http://repo.spring.io/snapshot" }
    maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
    classpath "io.spring.gradle:dependency-management-plugin:${springIOVersion}"
    classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
    }
    }

    plugins {
    id 'org.springframework.boot' version '1.5.9.RELEASE'// 同步工程提示找不到 如果直接注释不影响启动 无法使用插件打包发布
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
    }

    group = 'com.example'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '1.8'//默认demo使用的是jdk11 这里根据自身电脑安装的环境进行修改

    repositories {//增加国内仓库 加快下载速度
    mavenCentral()
    maven{
    url ('http://maven.aliyun.com/nexus/content/groups/public/')
    }
    google()
    jcenter()
    }

    dependencies {
    compile 'org.springframework.boot:spring-boot-starter:2.3.4.RELEASE'
    compile 'org.springframework.boot:spring-boot-starter-web:2.3.4.RELEASE'
    //implementation 打包时打不进 暂时未找到原因 改用 compile
    }

    test {
    useJUnitPlatform()
    }
  2. 第四步 编写接口测试

1
2
3
4
5
6
7
8
9
*注意controller包是在引导类的同一级目录或者子包
@Controller
public class QuickStartController {
@RequestMapping("/quick")
@ResponseBody
public String quick(){
return "hello SpringBoot !";
}
}

5.第五步 启动springBoot项目

1
2
3
4
5
运行引导类的main方法,观察日志
Tomcat started on port(s): 8080 (http) with context path ''

说明该项目发布到8080端口,虚拟路径为空,浏览器访问
http:localhost:8080/quick

至此, 大功告成

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

1…353637…51

乱码三千

android程序员一枚,擅长java,kotlin,python,金融投资,欢迎交流~

505 日志
145 标签
RSS
© 2026 乱码三千
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%