0x01 前言

前段时间由于给小破站开启了青少年模式,结果密码给忘记了。导致只能看一些学习课堂和书法之类的相关视频。虽然有找回密码选项,但是找回密码需要发送本人手持身份证的照片。而后在输入密码的时候试了多次,除了提示”密码输入错误,请重试”外,没有其他限制,也没有网络请求。由于是4位的数字密码,我想如果我写个自动输入密码的脚本,利用生成0000~9999的密码字典,应该就可以找到正确的密码。

经搜索发现,IOS自动化的框架有很多,如UIAutomation、XCTest、KIF、APPium等。其中UIAutomation和APPium无需源码就可以进行测试,而且APPium支持多种语言,所以这里我选择了APPium

0x02 环境搭建与自动化测试

Appium相关介绍

Appium 是一个开源工具,用于自动化 iOS 手机、 Android 手机和 Windows 桌面平台上的原生、移动 Web 和混合应用。 它使用WebDriver协议驱动iOS,Android和Windows应用程序。Appium 是跨平台的:它允许用同样的 API 对多平台(iOS、Android、Windows)写测试,做到在 iOS、Android 和 Windows 测试套件之间复用代码。而且支持多种语言,java、python、php、Ruby等等。

我这里是在MacOS系统下进行的测试,采用了手机版本为13.5.1(IOS真机),开发语言选择了Python3.8。

下面就开始搭建环境进行测试吧。

首先是APPium的安装。

APPium相关安装

如果没有安装Homebrew,则需要先安装Homebrew

1
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

安装node

1
brew install node

安装相关依赖与软件

1
2
3
4
5
6
7
8
9
10
11
12
13
npm install -g appium # 安装appium
npm install wd
npm install -g appium-doctor
npm i -g mjpeg-consumer
brew install ios-deploy
brew install carthage
brew install lyft/formulae/set-simulator-location
npm i -g opencv4nodejs
brew tap facebook/fb
brew install idb-companion
pip install fb-idb
brew tap wix/brew
brew install applesimutils

安装的话根据可以参考http://appium.io/docs/en/about-appium/getting-started/ 进行安装。

也可以直接安装appium桌面程序https://github.com/appium/appium-desktop/releases

安装后执行appium-doctor --ios指令,可以查看与iOS相关配置是否完整。

如果有哪一项是打叉的,则根据安装说明进行安装就可以了。

下图是全部配置都成功的情况。

由于需要在真机下进行测试所以还需要配置WebDriverAgent

配置WebDriverAgent

下载WebDriverAgent

进入到WebDriverAgent目录,然后执行./Scripts/bootstrap.sh下载安装依赖库。

双击WebDriverAgent.xcodeproj使用xcode打开WebDriverAgent项目

配置WebDriverAgentLibWebDriverAgentRunner以及IntegrationApp的开发者信息

发现有错误,接下来修改WebDriverAgentRunnerIntegrationAppBundle identifier

修改WebDriverAgentRunner

修改IntegrationApp

修改后发现无报错信息。然后数据线连接真机,进行安装。

发现编译失败,将 assign 改成 unsafe_unretained ,然后重新使用command + u进行编译安装测试。

然后访问http://手机IP:8100。如http://172.20.10.1:8100/status

需要手机与电脑处于同一网络下,我这里为了测试,电脑连接的是手机的热点。

此时WebDriverAgent配置成功。如果在Appium中使用时,还需替换Appium中的WebDriverAgent。

我这里的路径为/Applications/Appium.app/Contents/Resources/app/node_modules/appium/node_modules/appium-xcuitest-driver/node_modules/WebDriverAgent

直接替换即可,或者直接修改Appium中的WebDriverAgent,效果一样。

接下来就是自动化脚本的编写。

自动化脚本编写

可以通过使用Appium应用程序查看IOS元素特征。启动Appium,左上角菜单栏选择APPium-New Session Window...(或者快捷键command+N)。

填写对应的启动参数。

一些必要的参数:

  • platformName是测试平台的名称,是ios或者Android。
  • udid在ios真机测试时需要,可以通过ios-deploy --list_bundle_id查询
  • bundleId需测试程序的bundle id,可以通过ios-deploy --list_bundle_id查询

配置完成后,点击Start Session,手机会自动打开哔哩哔哩APP。

可以利用xpath获取元素并进行输入、点击等一系列操作。接下来就可以通过使用Python脚本来进行自动化测试了。

需要安装appium的python依赖库。

1
pip install Appium-Python-Client

启动APP的脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from appium import webdriver #pip install Appium-Python-Client
import time
def main():
desired_caps = dict()
desired_caps['platformName'] = 'iOS'
desired_caps['platformVersion'] = '13.5.1'
desired_caps['deviceName'] = 'xiamo6'
desired_caps['automationName'] = 'XCUITest'
desired_caps['udid'] = '8d4************a3'
desired_caps['bundleId'] = 'tv.danmaku.bilianime'
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

time.sleep(5)
driver.quit()

if __name__ == '__main__':
main()

运行效果如下:

运行效果

接下来需要进入到输入密码界面,依次需要点击我的-青少年模式-修改密码(或者关闭青少年模式)

利用脚本实现就是:

1
2
3
driver.find_element_by_id("我的").click()
driver.find_element_by_xpath("//XCUIElementTypeStaticText[@name=\"青少年模式\"]").click()
driver.find_element_by_id("关闭青少年模式").click()

运行效果如下

运行效果

如果是已经打开app 40分钟,或者非正常时段,则直接显示的是输入密码界面。

运行效果

可以利用xpath获取密码输入框元素,输入密码后并点击“立即验证”按钮,主要代码如下:

1
2
3
4
5
driver.set_value(driver.find_element_by_xpath("//*/XCUIElementTypeStaticText[1]"),pwd[0])
driver.find_element_by_xpath("//*/XCUIElementTypeStaticText[2]").send_keys(pwd[1])
driver.find_element_by_xpath("//*/XCUIElementTypeStaticText[3]").send_keys(pwd[2])
driver.find_element_by_xpath("//*/XCUIElementTypeStaticText[4]").send_keys(pwd[3])
driver.find_element_by_xpath("//XCUIElementTypeButton[@name=\"立即验证\"]").click()

完整的代码如下:

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
from appium import webdriver #pip install Appium-Python-Client
import time

def main():
desired_caps = dict()
desired_caps['platformName'] = 'iOS'
desired_caps['platformVersion'] = '13.5.1'
desired_caps['deviceName'] = 'xiamo6'
desired_caps['automationName'] = 'XCUITest'
desired_caps['udid'] = '8d4904454b********552e976a3'
desired_caps['bundleId'] = 'tv.danmaku.bilianime'
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
for p in range(5091,10000):
try:
pwd = str(p)
driver.set_value(driver.find_element_by_xpath("//*/XCUIElementTypeStaticText[1]"),pwd[0])
driver.find_element_by_xpath("//*/XCUIElementTypeStaticText[2]").send_keys(pwd[1])
driver.find_element_by_xpath("//*/XCUIElementTypeStaticText[3]").send_keys(pwd[2])
driver.find_element_by_xpath("//*/XCUIElementTypeStaticText[4]").send_keys(pwd[3])
driver.find_element_by_xpath("//XCUIElementTypeButton[@name=\"立即验证\"]").click()
print(pwd,'error')
except Exception:
print(pwd,'is right')
break

driver.quit()

if __name__ == '__main__':
main()

运行效果

运行效果

由上面执行的结果来看,每次输入前都会等待一会再输入,所以看起来速度很慢。

运行效果

通过找到上图标记的XCUIElementTypeOther,然后使用Send Keys直接传入一个四位数字。

1
2
driver.find_element_by_xpath("//*/XCUIElementTypeWindow[1]/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther").send_keys(pwd)
driver.find_element_by_xpath("//XCUIElementTypeButton[@name=\"下一步\"]").click()

执行结果如图所示。

运行效果

这里的动图是4倍速播放。

修改过后,发现速度确实有所提升,但是总体速度还是很慢的。经过搜索发现,有帖子说在IOS模拟器上和安卓上会快一些,由于程序无法安装到IOS模拟器,所以这里选择了在安卓设备上进行测试。

安卓环境下测试

安卓的环境需要安装Android SDK、JDK、安卓模拟器(真机也可以,这里采用的是Genymotion模拟器)。安装后配置JAVA_HOMEANDROID_HOME

然后通过执行appium-doctor --android来查看是否配置成功,如图所示。

运行效果

查看android元素信息可以使用uiautomatorviewer。位于Android SDK下的/tools/bin/目录。

启动模拟器并打开测试的APP,然后打开uiautomatorviewer。选择Device Screenshot(从左至右第二个图标)。

运行效果

通过tv.danmaku.bili:id/et_code可以定位到编辑框,按钮“立即验证”可以通过tv.danmaku.bili:id/operate来定位。

这里直接贴使用安卓下自动化爆破的脚本,代码如下:

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
from appium import webdriver
import time

def main():
desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '6.0'
desired_caps['deviceName'] = 'Nexus'
desired_caps['appPackage'] = 'tv.danmaku.bili'
desired_caps['appActivity'] = 'ui.splash.SplashActivity'
desired_caps['noReset'] = True
desired_caps['automationName'] = 'UiAutomator2'

driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

for pwd in range(4011,10000):
try:
driver.find_element_by_id('tv.danmaku.bili:id/et_code').send_keys(pwd)
driver.find_element_by_id("tv.danmaku.bili:id/operate").click()
print(pwd,'error')
except Exception:
print(pwd,'is right')
break
time.sleep(4)
driver.quit()

if __name__ == '__main__':
main()

运行效果如图所示

运行效果

对比之下,在Android下的效率是很快的。而且经过测试,安卓破解100次,耗时约139.14s,而ios破解50次,耗时约262.56s

所以最后我采用了安卓环境下进行破解,最终成功跑出了密码。

运行效果

已经成功找到密码4578。

0x03 思考

由于输入密码的时候并没有发生网络请求,猜测密码应该保存在本地。一般情况下本地存储采用sqlite数据库或者是SharePreferences中。

先查看SharePreferences中的文件,进入/data/data/tv.danmaku.bili/shared_prefs目录。

shared_prefs目录

发现文件bili_teenagers_mode_preferences20449166.xml,根据文件名发现文件就是和青少年模式有关的配置文件。查看里面的内容

teenagers_mode文件

发现一串神秘字符串,通过cmd5查询:c2890d44d06bafb6c7b4aa194857ccbc=md5(4578)。正好是设置的密码。

另外由于登录前可以看到所有的内容,而登录开启青少年模式的账户时,就会进入青少年模式,只能看特定内容。所以退出账户重新登录并抓包,可以看到如下的请求。

抓包

也成功找到经过md5加密的密码。

0x04 总结

这次是因为B站开启了青少年模式,后来密码给忘记了。当时只想着用爆破的方式来解决了,没有想着抓包或者查看保存的数据文件。后来爆破出密码后,又发现可以通过查看配置文件的方式和抓包的方式找到密码。而且之前是打算用ios作为测试的,后来因为效率的问题,才转战安卓。如果使用安卓的话,自动化测试工具也很多,例如UIAutomator2等。

本文其实就是一个流水账,记录一下自己找回青少年模式密码的过程,可能比较粗糙。有关IOS真机调试更详细的教程可以参考使用Appium进行iOS的真机自动化测试