教我兄弟学Android逆向04 动态调试smali代码:https://www.52pojie.cn/thread-658865-1-1.html

From:Android Studio 3.6 调试 smali:https://blog.csdn.net/jha334201553/article/details/104494732
From:Smalidea+IntelliJ IDEA/Android Studio无源码调试:https://bbs.pediy.com/thread-220743.htm
From:反编译之利用AndroidStudio动态调试smali源码:https://juejin.im/post/5c14622be51d4556c90be796
From:Android Studio动态调试smail源码:https://blog.csdn.net/hp910315/article/details/52790740

smalidea 插件 github 地址:https://github.com/JesusFreke/smalidea
Android Studio Smali 调试:https://www.jianshu.com/p/b9312b056d15
Android调试系列—使用android studio调试smali代码:https://www.cnblogs.com/gordon0918/p/5570811.html
Android Studio动态调试smali:https://zhuanlan.zhihu.com/p/85469186
AndroidStudio+ideasmali动态调试smali汇编:https://www.cnblogs.com/lanrenxinxin/p/4891424.html
Android Studio + smalidea进行smail动态调试:https://blog.csdn.net/u013736724/article/details/53292855
微信 APP 调试方法 AndroidStudio3.2 + smalidea + Xposed + BDOpen:http://www.bloguan.com/?id=518

前言

Android studio 配置

  • Android Studio 配置(一):https://www.cnblogs.com/qianguyihao/p/4390905.html
  • Android Studio 配置(二):https://www.cnblogs.com/qianguyihao/p/4392611.html
  • Android Studio 配置(三):gradle项目构建:https://www.cnblogs.com/xiaofengfeng/p/9133043.html

在安装 Android Studio之前,建议先提前准备好单独的 Android SDK,这个可以在http://www.androiddevtools.cn/ 网站下载。Android Studio 安装完成后,第一次启动 AS 前,为了避免重新下载新版本的SDK,需要做如下操作:AS启动前,打开安装目录,请先将bin目录的idea.properties 文件中增加一行:disable.android.first.run=true 就行了,避免第一次打开AS时自动重新下载 SDK。mac 平台的右键安装包->Show Package Contents 就找到bin目录了。

第一次打开 Android Studio 时,需要配置JDK和SDK:

上图中,选择“Project Structure”,弹出如下界面:(选择JDK和Android SDK的路径)

在使用 Android Studio 的时候,也可以随时修改JDK和Android SDK的路径。选择「File-->Other Settings-->Default Sructure」,即可进行同样的修改。

AndroidStudio 安装的时候 Gradle 的配置

将如图路径改成你自定义的。这时候你会发现以后下载的Gradle都会进入这个文件里面。 但是当在 Android 里面执行 gradlew 命令的时候你会发现,他又去下载对应的 gradle 版本了,而且保存路径在默认的 c://[用户]//.gradle 里面。这时候问题来了,打开 gradle-wrapper.properties 文件仔细研究下文件里面写的东西:

指定一个默认路径 GRADLE_USER_HOME 这个路径就是c盘。既然发现问题的根源,现在来修改 GRADLE_USER_HOME 的值。在系统环境变量里面添加 GRADLE_USER_HOME 变量,值为指定的位置,然后一定要重启计算机。

From :[Android Studio系列(五)] Android Studio手动配置Gradle的方法:https://blog.csdn.net/u010142437/article/details/109119501

(1) android sutdio第一次打开一个工程巨慢怎么办? 
(2) 手动配置Gradle Home为什么总是无效? 
(3) 明明已经下载了Gradle,配置了gradle home,为什么打开工程还是去自动下载Gradle?

Gradle 构建配置详解:https://www.knowledgedict.com/tutorial/gradle-build-script-config.html

史上最全 Android build.gradle 配置详解:https://www.jianshu.com/p/c11862136abf

在开发过程中,

  • debug 版本我们可以跟踪调试,查看 bug 等信息,
  • 但是 release 版本中只能去打 log 进行代码进行猜测,还有就是 dump 堆栈等无法与代码直接交互的方法。无源码调试 指的是在没有源代码的情况下可以对 app 进行代码调试,逆向 smali 代码,然后查看其运行逻辑。对发现 release 版本问题的过程中可以让我们更块的定位错误。

工具及下载地址:

  • ①、apktool.jar 用于反编译 smali,下载地址:https://github.com/iBotPeaches/Apktool 。
            可以使用 AndroidKiller,更新 AK 的 apktool,然后使用 AK 进行反编译。。。
  • ②、Android Studio 下载地址: https://developer.android.google.cn/studio/
  • ③、smalidea-0.05.zip (目前最新版 0.05),AS 插件,用来给 smali 下断点,单步调试,
            下载地址:https://github.com/JesusFreke/smalidea 。解决新版 IDEA 或者 AndroidStudio 不能调试 smali 工程的问题( 不能下断点 ):https://blog.csdn.net/binbin594738977/article/details/106571844/

1. AndroidStudio 安装 smalidea 插件:

从 github 下载 smalidea.zip,然后在 Android 中依次选择: File   -->  Setting   将会弹出设置对话框

然后在设置对话框依次选择:  Plugins   -->  <设置图标>  --> Install Plugin from Disk...

在弹出选择对话框中,找到下载好的 smalidea 压缩包,选择ok即可

点击 OK ,会重新启动 Android Studio ,至此,插件安装就完成了。

2. 反编译

目前常用的 Android 反编译工具有:

  • Baksmali:https://github.com/JesusFreke/smali
  • Apktool:https://ibotpeaches.github.io/Apktool/
  • dex2jar:https://sourceforge.net/projects/dex2jar/

步骤:

  • 第一步:拿到想要 debug 的 apk。
  • 第二步:反编译 apk 得到 smali 代码。可以使用 baksmali 进行反编译,也可以使用 apktool 进行反编译。这里使用 AndroidKiller ( AK 其实调用 apktool)。

使用 Baksmali 反编译 apk 

baksmail 反编译命令:java -jar baksmali-2.2.1.jar  d myapp.apk -o ~/projects/myapp/src

$java -jar baksmali-2.0.5.jar debug.apk -o debug/src

使用 apktool 反编译 apk

apktool 反编译命令:java -jar apktool.jar d myapp.apk -o ~/projects/myapp/src

使用命令 : java.exe -jar  apktool.jar d -f  <xxxx.apk>  -o <xxxx目录>

也可以直接使用 ApkTool 集成环境(如果反编译失败可以替换其中的 apktool.jar 至最新版):

这是使用 AndroidKiller 进行反编译( AK 下载地址:https://down.52pojie.cn/Tools/Android_Tools/

让 apk 可调式

参考:简单实现Android手机“全局可调试”(ro.debuggable = 1)的方法:http://www.mamicode.com/info-detail-2915184.html

在 Android 真机上调试程序有一个前提,就是这个apk包必须有 debuggable=true 的属性才行。而除了自己开发的apk能够控制打包属性之外,其他的程序发行之后显然不会设这个值为 true 的(不然随随便便就能被Debug ,岂不是很没安全感 )。为了调试这些第三方的apk,我们可以从整个手机系统入手 —— 因为除了每个apk中的 debuggable 标志以外,这个标志还可以在系统中全局指定,换句话说,只要把系统里的 debuggable 值设为true,那么不管apk的这个属性是什么值 都可以被调试了。

Android手机系统的 ro.debuggable 这一配置位于 /default.prop 文件中,而 /default.prop 又来源于手机每次启动时 boot.img 中 ramdisk 的挂载,所以想要直接通过修改 /default.prop 是不可行的,但是系统文件是只读的,改了也没用。网上流传较广的是改 boot.img ,然而锤子并没有解锁 bootloader ,改了的话会变砖的。好在Magisk 有一个模块能够助我们实现这个修改,且看操作(默认手机已经 root 且安装了 Magisk ):

这一步很关键,就是让运行在设备中的程序支持 debug。方法有几种:

  • 把设备 root 掉
  • 修改测试机的 /default.prop 文件的 ro.debuggable=1,目测这一步也可能需要 root。
  • 使用模拟器
  • 修改 apk 的 Manifest application 属性 android:debuggable="true",可以用 apktool 解出 Manifest 然后修改,接着重新打包回去。( 反编译 apk,修改 AndroidManifest.xml 的 debug 属性并在程序的入口处添加 waitForDebugger代码进行调试等待。 )
  • 打开系统调试总开关,使用 am 命令,以调试模式启动应用。
  • 终极办法,自己编译一个 debug 版的 rom,这个稍微麻烦一点,自己编一个,想怎么玩就怎么玩。

下面只说明两种方法,

  • 一种是 修改 " AndroidManifest.xml " 实现可调试
  • 另一种是 打开系统调试总开关

修改 " AndroidManifest.xml " 实现可调试

使用 AndroidKiller 进行反编译,然后在反编译出的 AndroidManifest.xml 添加 debug 属性

如果没有这个属性的话,需要自己添加 android:debuggable="true" 然后编译安装修改后的 apk 即可。。。

不添加代码的方法:

修改 AndroidManifest.xml 之后不用在程序的启动界面添加代码也行,只要以 am 命令运行程序就行了。

以调试状态启动 app ,命令:adb shell am start -D -n com.wizardev.testjar/.MainActivity

转发 8700 端口 到安卓 对应进程 [app_pid]。命令:adb forward tcp:8700 jdwp:446

记住这时候需要将 DDMS 关掉,不然会出现错误

添加代码方法:

  • 找到启动 apk 的启动界面,然后在启动界面的 onCreate 方法的第一行添加 invoke-static {}, Landroid/os/Debug;->waitForDebugger()V 这句代码。

那么问题来了,我们怎么知道程序的启动界面呢?

其实可以通过以下几种方式:

方法 1:

观察 “AndroidManifest.xml” 文件中的代码,看下图

如果图中 “1” 处的代码,那么程序启动界面的 smail 文件就是 “2” 处的名称。如上图启动界面的 smali 文件就是“MainActivity.smali”。然后在 “MainActivity.smali” 文件中的 onCreate 方法中添加等待调试的代码即可。用 am 命令,使目标程序以调试模式运行。

方法 2:

通过 adb 命令来发现 apk 的启动界面,命令:adb shell dumpsys activity top

运行命令后会出现以下界面

图中用红框标记的就是启动界面的 smali 文件名。注:这种方式找到的启动界面可能不准确,因为有的 app 会有欢迎页,因此推荐用第一种方法来找 app 的启动界面。

主 Activity 中加入  invoke-static {},Landroid/os/Debug;->waitForDebugger()V

回编译 apk 并签名安装到手机,如果启动 app 出现以下界面,则说明以启动模式运行app成功。

(这步如果重新打包失败,可以安装原始包,把手机 root 配置/default.prop 的 ro.debuggable=1 开启全局调试。

然后用命令启动 Activiry : adb shell am start -D -n package属性的值/android:name属性的值  )

打开系统调试总开关( 不用修改 " AndroidManifest.xml " )

  这种方法不用修改 “AndroidManifest.xml”,而且当你打开这个开关后手机中的所有 App 都是可以调试的了。但是这种方法操作起来比较复杂,而且手机必须是已经 root 过的,下面会详细描述怎么打开系统调试的总开关。

  这里会介绍两种方法,第一种方法是大家普遍采用的,但是我使用采用第一种方法没有成功,于是查找资料找到了第二种方法,如果你使用第一种方法不成功,那么可以试下第二种方法。

第一种方法

  • 1. 点击这里下载“mprop”文件。

  • 2. 依次运行一下命令设置 “ro.debuggable”

adb push “下载mprop文件所在的位置”\mprop /data/local/tmp/
adb shell su
chmod 755 /data/local/tmp/mprop
data/local/tmp/mprop
setprop ro.debuggable 1
/data/local/tmp/mprop -r
  • 3. 运行 getprop ro.debuggable 命令可以查看 debuggable 的状态,显示为 1,表示更改成功。

注:这种方法在开机后设置的“debuggable”将会失效,需要重新设置

第二种方法

这种方法其实说复杂也不复杂,说不复杂呢!还是有一点复杂的。具体操作方法如下:

  • 需要手机刷入“Magisk”,刷入方法就想卡刷手机系统一样,可以到https://github.com/topjohnwu/Magisk/releases。
Magisk是什么:简单的说就是集成了root的框架,它强大的是root权限还可以设置对其他软件隐藏。
Magisk的强大远不止这点,想了解Magisk具有哪些功能可以自行搜索。
  • 通过Magisk安装“MagiskHide Props Config”模块,安装的方法可以看下面的动图。

安装完成之后需要重启,才能生效。

打开终端,输入以下命令

adb shell
props

会出现这个界面

然后,输入3,回车,会出现这个界面

然后,输入1,回车,出现这个界面

可以看到图中显示当前的 “ro.debuggable” 的值为0,如果要修改为1的话输入“y”,回车即可修改完成。

接着终端会弹出是否重启,这时继续输入“y”重启,重启后修改的“ro.debuggable”才会生效。

经过以上5步,就可以打开系统调试的总开关,这时我们打开“Monitor”工具就可以看见当前运行的所有的所有进程了。

注:通过这种方式修改的 “ro.debuggable” 值,重启手机后不会还原,就是设置过后,手机重启后不需要再次设置。

打开系统调试的总开关后,这时通过 am 命令以调试模式启动应用,即可让目标应用处于可调式状态。

adb shell am start -D -n xxx

这里的 “xxx” 为我们要调试应用的启动界面,如我们要调试应用的启动界面为 com.wizardev.testjar/.MainActivity 则输入以下命令

adb shell am start -D -n com.wizardev.testjar/.MainActivity

这是目标应用就会以调试模式运行,处于等待调试的状态。

3. 将 反编译后的smali  导入到 Android Studio

将目标应用的 smali 源码导入 AndroidStudio。将 smali 源码导入AndroidStudio 挺简单的,只需要将反编译出来的 smali 文件夹放在工程项目中的模块的 src 目录下即可。

3.1 导入 smali 代码

简单的说一下操作方法吧。有两种方法导入:

方法 1:新建工程导入 smali 代码

  1. 新建一个 Android 项目,不新建项目也行,但需要新建一个 module。
  2. 将项目切换到 project 视图,将新建的项目下 app 中 src 目录下的代码删除或将新建的 module 下 src 目录下的代码删除。
  3. 将我们反编译的 smali 文件夹放入 src 目录下。

这样,就将反编译的 smali 文件导入到 AndroidStudio 目中了。操作后的目录界面大致如下。

方法 2:不新建工程导入 smali 代码

步骤总览:

  • Import Project... -> Create project from existing sources
  • 将 Project(ALT+1)里面默认的 Android 视图切换为 Project 视图,将 src 设置为 Sources Root.
  • Project Structure(Ctrl+Shift+ALT+S),Project SDK设置为 Android API 10 Platform
  • 远程调试配置,Run -> Edit Configuration进入Run/Dubug ConfigurationsAdd New Configuration(+符号) -> Remote,将 5005 端口,修改为 8700 端口。

图示说明:

默认选择 Create project from existing sources ,一路 next ,最后点击 Finish 即可

3.2 导入工程后设置 Sources root

用 Project 视图,在项目目录右键  --> Mark Directory as ... --> Sources root

3.3 设置 Project 的 sdk:

如果是通过 "新建项目" 导入的 smali ,则不需要设置 sdk,因为 新建工程 时已经有默认的 sdk

如果是通过 " 导入已经存在的 " 方式导入 smali ,则需要设置 sdk :

选择 sdk ,这里选择 sdk  1.8

3.4 配置 远程调试 ( 远程 debug 配置 )

添加一个 remote 调试,

或者:

修改调试的端口:8701 ( 未占用端口均可 )

安装设置 debug=true 的 APK,在需要的地方打好断点,通过以下命令行启动进程调试等待模式:
启动第一个 Activity 然后等待 debug,命令:adb shell am start -D -S -W 包名/MainActivity(带路径)

命令行启动调试模式,adb shell am start -D -n packagename/ MainActivity
packagename 为进程名,MainActivity 为首页 Activity

启动调试 app,通过 adb shell dumpsys activity top | grep --color=always ACTIVITY 在终端获取包名和页面信息。

进入等待调试

获取运行 apk 的进程(pid):adb shell ps | grep 包名

  • 示例:adb shell ps | findstr "zhuceji" ,如下图所示,可以看到 pid 是 3170

端口映射: adb forward tcp:8701 jdwp:<pid>

  • 执行:adb forward tcp:8701 jdwp:3170    建立端口转发,即把端口 8701 上面的信息通过 jdwp 转发到 pid 是 3170 上面

进入等待调试后,在 Android studio 中执行 Run -> Debug 启动刚才创建的远程调试器,进入动态调试了。

在 Android Studio 选择附加

图二

此时,已经 Attach 到进程中,可以快乐的调试了。通过断点可以查看内存的信息。

设置 AndroidStudio 为远程调试模式

  按下面的步骤操作,将AndroidStudio运行模式设置为远程调试模式。

工程配好了,配置 debug 的端口:即 远程调试配置,Run -> Edit Configuration 进入 Run/Dubug ConfigurationsAdd New Configuration(+符号) -> Remote,将 5005 端口,修改为 8700 端口。

在出现的界面,点击左上角的“+”,在出现的下拉列表中再点击“Remote”,添加一个 remote 调试

设置远程调试的名称和监听远程的端口

在图中1处修改远程调试的名称,不修改直接用默认的也行。在2处修改远程调试服务端监听的端口,一般设置为“8700”,当然也可以设置成当前调试项目所在的端口,可以在 “Monitor”工具中查看项目所在的端口。

( 也可以用 8700 端口,然后启动 ddms 去设置端口号映射,然后 apply -> ok。设置端口号映射后 ddms 监听的 8700 端口和转发的端口都可以接收到数据。 好了,project 方面就准备好了! )

可以看到当前调试的项目有两个远程服务端端口,“8700”端口是每个调试程序默认的远程端口,如果不想每次运行程序都重新设置一下端口,2处设置成“8700”就行了,然后点击OK完成修改。这时你会发现可运行的程序中多出了一个你刚才设置的调试项目名称。

如上图,这里我直接使用默认的调试名称,则可运行的项目中多出了一个“Unnamed”选项,我们选中这个就行了,这时Android Studio就在“8700”端口监听项目的运行了。

以调试模式运行并将进程映射到8700端口

  1. 用 am 命令,使目标程序以调试模式运行。
  2. 将进程映射到 “8700” 端口,使用命令:adb forward tcp:8700 jdwp:进程id

进程 id 可以在 “Monitor” 工具中查看,如下图

也可以运行 adb 命令查看,用 adb 命令查看进程 id 的命令如下:adb shell ps | grep 包名

完成了以上两步,接着在AndroidStudio点击Debug选项,如下图

然后就可以打断点调试项目了。

注:在点击Debug选项运行项目之前,需要先将“Monitor”工具关掉,否则会提示8700端口被占用,无法进行动态调试项目。

接下来需要准备的就是如何连上设备debug了!过程也很简单,启动DDMS
Tools -> Android -> Android Device Monitor 选择你要调试的 apk 的包名

最后,开始 debug。Run->Debug Smali

针对 DDMS 端口转发,也可以手动的制定端口信息,操作如下(smalidea的作者推荐的是ddms的方式):

  1. 拿到 apk 的包名和启动的Activity,把应用启动起来然后等待 debug
    cmd 执行命令:adb shell am start -D -S -W packageName 
    ( packageName 的获取可以反编译 AndroidManifest.xml 里面有包含 )

正常的话,你会看到设备里的应用已经跑起来了,并且有个 Waiting For Debugger 的提示,别关掉它。

  • 1. 拿到程序运行的pid: adb shell ps | grep packageName
  • 2. 端口映射: adb forward tcp:8800 jdwp:5413   这里的 8800 是上一步在配置工程中自定义的端口。

官方步骤如图所示:

以上就是基于smalidea无源码调试的整个过程,有问题的可留言,我们一起交流学习。

结束语

  动态调试步骤是有点复杂,其实熟悉之后就没多大感觉了,简单设置几下就行了。可能有的手机不能root,这时就只能修改“AndroidManifest.xml”文件了,修改之后不用在程序的启动界面添加代码也行,只要以 am 命令运行程序就行了。

安卓逆向_13 --- AndroidStudio + Smalidea 动态调试 smali 代码【APK可调试】、gradle 配置相关推荐

  1. 安卓逆向_12 --- jeb工具的使用 ( 动态调试 smali 代码 【 普通调试 和 debug调试 】)

    From:https://www.52pojie.cn/forum.php?mod=viewthread&tid=742250 jeb 动态调试 smali 代码:https://www.bi ...

  2. 安卓逆向_12 --- jeb工具的使用 ( 动态调试 smali 代码 【 普通调试 和 debug调试 】)...

    From:https://www.52pojie.cn/forum.php?mod=viewthread&tid=742250 jeb 动态调试 smali 代码:https://www.bi ...

  3. 012 动态调试smali代码

    文章目录 前言 配置调试插件 调试插件的使用 总结 常见问题 前言 之前分析游戏进行破解的时候,都是通过字符串和日志的方式来对程序进行静态分析.但是在遇到算法类型的程序时,这种方法就显得效率特别低,所 ...

  4. 安卓逆向分析中常用动态调试方法总结

    安卓逆向之----常用动态调试方法 一. 前言 逆向分析中常用的分析方法有:静态分析.动态调试.HOOK等.动态调试的好处是:1)可以在调试的过程中知道参数或者局部变量的值以及变化过程,2)可以快速履 ...

  5. AndroidStudio+ideasmali动态调试smali汇编

    0x00    前言 之前对于app反编译的smali汇编语言都是静态分析为主,加上一点ida6.6的动态调试,但是ida的调试smali真的像鸡肋一样,各种不爽,遇到混淆过的java代码就欲哭无泪了 ...

  6. 安卓逆向_7 --- 六种快速定位关键 Smali 代码的方法 ( 去掉 RE 广告 )

    哔哩哔哩:https://www.bilibili.com/video/BV1UE411A7rW?p=34 具体用法,看视频教程( 去掉 RE 的 结束广告 ) 6 种定位关键代码的方法,当然还有其他 ...

  7. Android逆向笔记-使用Android Studio调试Smali代码(方式二)

    这里我使用Android Studio写了这样的一个程序: 这里我使用Android Studio写了这样的一个程序: 然后点下BUTTON后: 下面写下如何调试这种程序. 开发环境: win 10: ...

  8. Android逆向笔记-使用Android Studio调试Smali代码(方式一)

    这里我使用Android Studio写了这样的一个程序: 然后点下BUTTON后: 下面写下如何调试这种程序. 开发环境: win 10: 使用的模拟器是雷神模拟器: Android Studio版 ...

  9. DDMS+AndroidStudio实现动态调试

    所需工具 AndroidStudio(用于调试smali代码) monitor.exe(DDMS工具连接手机后可查看进程端口,与AndroidStudio一起使用实现动态调试) AndroidKill ...

最新文章

  1. 技术宝典 | ToB 业务场景下自动化测试的实践及探索
  2. OpenCV从Mat中提取某些行或列
  3. C#调用DLL函数方法
  4. for input string:是什么原因出现的_「汽车空调异味」周期性出现:原因是什么呢?...
  5. 智能电视无法进入服务器,三星智能电视无法连接到服务器怎么办
  6. 中央空调和普通空调区别
  7. 金融相关计算机面试题,银行及其他金融企业笔试和面试经验
  8. 【记录】螺栓连接——预紧力
  9. 2021年中国电池电解液行业出货量及龙头企业对比分析:江苏国泰vs新宙邦vs天赐材料[图]
  10. 如何制作抖音卡点视频,使用预设制作酷炫转场
  11. PHP 函数、类声明和调用
  12. OpenGL透视投影
  13. (MBI)Pt(acac)|(PBI)_2Pt|(t-BuPBIM)_2Pt铂配合物的空间构型
  14. Javascript之js文件延时加载
  15. 计算机数据采集管理系统的结构和功能,数据采集器有哪些功能?仓库作业管理员必备知识...
  16. 如何在ubuntu上写一个类似sl跑火车指令,“跑甜甜圈”
  17. 洛谷 P2768 珍珠项链
  18. 随着计算机技术的发展与普及,计算机方面的中译英,就一段话.随着计算机技术的飞速发展,计算机在各行各业中应用的逐渐普及,校园利用现代化手段进行管理势在...
  19. 苹果logo设计背后的故事
  20. 敏源传感 M601 温度寄存器的测量数据如何理解,具体计算方法怎样?

热门文章

  1. 征稿 | “健康知识图谱”投稿通道开启
  2. 论文浅尝 | 基于超平面的时间感知知识图谱嵌入
  3. Android官方开发文档Training系列课程中文版:打印内容之图像打印
  4. Pycharm使用远程服务器运行代码
  5. Pandas中的元素替换
  6. nlp2-数学基础(信息论,概率论、词义消歧)
  7. 06.动态SQL和foreach
  8. LeetCode 刷题笔记 (树)
  9. python 经典排序算法
  10. bzoj 1596 电话网络