分类 开发随笔 下的文章

开发时的突发奇想

前言

由于搬家到了新的屋子,新的屋子客厅里有个电视,有一天与好友聊到如何利用起这个电视,好友提到他那边有一个闲置的Unifi Plus Box,来自于办理TM家宽的附带产品,历经几日之奔波,这个电视盒子最终到了我手上。

加注

感谢某位网友提供的关键数据,这个分析流程在基于Android12的新版Unifi Plus Box(创维HP43A)也可用,但有可能会被其自带的远控更新设置后变为不可用状态,请恢复出厂设置后再尝试。

特殊

到手后,发现其采用的是Amlogic S905L3B的SoC,有4核心A53,主频为1.8Ghz,2GB的运行内存和16GB的闪存,Android版本为9,在大多数电视盒子中都属于较高配置。
首先尝试执行系统更新,没想到居然接收到了更新到Android 10的大版本更新,对于我这种熟悉中国电视和电视盒子市场的人来说是颇为惊讶的,从来没有在这类型的设备上看到过有Android大版本更新。执行更新之后,发现了更多奇妙,于是则开始了以下的流程。

开发者选项密码?

正常情况下,如果想对这个盒子深挖,首要就是要打开USB调试,不管是无线还是有线调试。
然而当我尝试以正常方式打开开发者选项时,其弹出了一个与AOSP完全不同的密码输入界面。
UTVCK_Password1
当然目前是不知道实际的密码是什么的,其为什么会有个密码也不是很明确,也无法进行后续的分析了。

获取系统包

UTVCK_GetBFg
后来一天与另一位朋友闲聊,他跟我说他逆向了Google的OTA分发系统的API,问我有没有需求来测试一下,我就想到这个盒子系统更新时候的界面十分原生,其内置的阉割版本Google Play商店也可以下载到AIDA64来获取到当前软件版本的编译指纹,于是我就把编译指纹发给了这位朋友,于是得到了以下结果。

{
    "code": 200,
    "message": "success",
    "data": {
        "title": "New software version is now available. ",
        "url": "https://android.googleapis.com/packages/ota-api/package/xxxxxxxxxxxxxxxxxxxxxx.zip",
        "size": "803.6 MB",
        "size_num": 842585223,
        "fail": "Upgrade failed!",
        "inst": "Upgrade successfully!",
        "desc": "<b>A new software update is available.</b> <br>1. Fix issue Error code 01999 of unifi tv application <br> 2. Update Android TV security patch<br> <br><b>Note: The update may take up to 10 minutes to complete. You will not be able to use the TV during the update. Thank you for your patience.</b> <br>"
    }
}

(请私信我以取得完整链接以作他用)
成功拿到了完整的系统更新包!!

逆向TvSettings

首先我们打开获取到的ZIP包,依据个人经验,这类密码一般存在于系统设置之本体。
UTVCK_InsideOfPkg
所以首先从SYSTEM分区镜像中找到TvSettings的apk
UTVCK_GetTvSettings
找到之后,我们需要一个特征来找到这个密码部分
尝试在密码输入框内胡乱输入,其输出了一个有“Password is error!!”的Toast信息,我们就以此入手
UTVCK_PasswordErr
在Jadx中以字符串搜索Password is error这个字符串,很快得到了有效回应
UTVCK_JadxSearchStr
打开这个代码段后,得益于Jadx强大的反编译能力,我们看到了非常清晰的程序逻辑,查看这些判断语句,很容易就得到了密码。
UTVCK_JadxDecompiled
输入进密码输入框,开发者选项成功被启用!
UTVCK_DevSettingsPass

USB调试密码?

但是当尝试打开USB调试时,其又出现了跟前面一样密码输入框,输入上一个判定中得到的密码,又一次提示密码错误。
尝试找到了开发者模式中USB调试开关的逻辑,发现了一个额外的source判定
UTVCK_JadxFindAnotherSource
再混合前面所提到的逻辑语句得到的字符串密码,成功打开了USB调试!
UTVCK_ADBAuthPass

后记

以上则为这个电视盒子如何打开开发者选项和USB调试的办法了
其实历经以上分析,发现这个盒子还有个很特别的点,其几乎用了可谓是”围追堵截“的办法来屏蔽Netflix,关于这个事情如何解决,请等待我的后续更新了!

前言

在本文中,我将使用x86汇编和简易的小小程序片段,其中将会使用最新的C++ 23标准进行代码编写。所以当你开始阅读这篇短文时,希望你有理解C++ 23,内存,指针和标准Win32 API的基本概念的能力。

代码段

#include <iostream>

int main() {
    auto string = "Hello, World! ";
    auto integer = 114514;
    std::cout << string << integer << std::endl;
    system("pause");
    return 0;
}

在这段简单的代码中,很明显这只是一个利用了C++新特性的HelloWorld程序,不难看出它最终会在命令提示符打出”Hello, World! 114514”这个字符串和整数并且传输pause指令给命令提示符,让其暂停运行并显示“按任意键继续”的提示。
提示:在编译时候记得加上-static给CMAKE_EXE_LINKER_FLAGS,这样才可以让编译出的可执行文件独立运行

入口点

当你将编译出的可执行文件拖入IDA的窗口之后,当IDA自动分析完成整个程序的互相调用,会给出一张十分复杂的调用图(如下图)。
IDA_MainGraph
你可能会很好奇为什么,只是一个简单的Hello,World程序而已为什么会存在这么多的调用,并没有在代码中体现出来哇?
放大其中任意一个代码块(如下图)你会发现,他们做的那么多事情,无非就是在准备运行现场,错误处理等现代编程语言自带的功能,在现如今的编程中,这些以往繁琐的操作,都将会被自动处理。
IDA_AnyPreFunc
在这个简单的程序中,入口点是main函数,我们手工构建了一个主函数。在IDA自动生成的调用图中,可以看到许多Win32 API函数被调用。这些函数包括获取当前进程的句柄、获取控制台窗口的句柄、向标准输出打印信息、调用系统命令行窗口实现以实现等待用户按键等。这些函数是必要的,因为它们提供了程序运行所必需的环境和支持。

main?WinMainCRTStartup和mainCRTStartup?

WinMainCRTStartup和mainCRTStartup是CRT(C Runtime)库提供的启动代码。它们都是程序入口点,但是它们的参数和返回值类型不同,适用于不同的应用程序类型以及适配了不同平台的实现。
IDA_mainCRTStartup
由上图可知mainCRTStartup是用于控制台应用程序的入口点,它不带任何参数执行。mainCRTStartup会进行一些必要的初始化工作,然后调用main函数。在main函数执行完毕后,它会自动退出程序,并返回main函数的返回值作为程序的退出码。
IDA_WinmainCRTStartup
在Win的部分中,由于当前程序只有命令行界面,故Win部分的实际实现也与标准入口点相同,其中混合部分Windows特有处理。

反编译的伪代码

IDA_Pseudocode
在IDA中,使用F5可以反编译出伪代码,看以上的伪代码思路要比直接看汇编效率高出很多,非常方便的可以看出这是一个典型的入口点函数,执行的操作也很清晰明了。

总结

在这篇文中,可以大概理解到一个纯命令行程序在Windows下的简易执行逻辑。
1.初始化C/C++运行时环境,包括初始化全局变量、静态变量等。
2.调用MainCRTStartup/WinMainCRTStartup函数,该函数初始化Win32 API环境,并调用Main函数。
3.设置标准输入、标准输出和标准错误流,以便程序可以向控制台输出信息。
4.初始化异常处理机制,包括设置SEH(结构化异常处理)机制*Windows特有。
总之,在现代的程序环境下,大部分与系统底层交互的部分,都存在完备的已封装功能,我们只需要直接调用即可。然而,在一定程度上了解这些内容也是有些必要,对底层更为了解,也更好的优化自己的作品。

这些GUID用于DisplayHdrLevel中的认证项目显示,需要多字符串值(Multi-String)且可叠加使用

VESA DisplayHDR

VESA DisplayHDR 400 (1.0)
20C5A9AF-CD1A-42B1-AA71-4C96A273DEF1
VESA DisplayHDR 400 (1.1)
0D710BC2-6368-4EB2-A829-CBBD45CE3BD2
VESA DisplayHDR 500 (1.1)
C47B4522-B803-47B0-831F-2ED63B56CE79
VESA DisplayHDR 600 (1.0)
D4C5928E-9488-46AF-8DA8-4F996EE4177F
VESA DisplayHDR 600 (1.1)
7134A821-9254-4AF4-9973-95B3FCF720CC
VESA DisplayHDR 1000 (1.0)
78137DFC-3400-412E-B0AF-08120754623A
VESA DisplayHDR 1000 (1.1)
C624859D-304A-4DE8-86DC-8BE82B79527A
VESA DisplayHDR 1400 (1.0)
0D710BC2-6368-4EB2-A829-CBBD45CE3BD2
VESA DisplayHDR 1400 (1.1)
3CA0903F-99E0-46FB-9BEC-DE023507BEF0
VESA DisplayHDR 2000 (1.1)
80931144-16F1-4710-91F4-2E66713B134D
VESA DisplayHDR 400 True Black (1.0)
1A6CBAFB-15FF-4CF0-AD75-12360E9B9F4A
VESA DisplayHDR 400 True Black (1.1)
FB4CB49B-F5A8-4084-800C-EB38E9CA16DE
VESA DisplayHDR 500 True Black (1.0)
86413F8C-0CDB-4D49-81F7-06BB64A8FED1
VESA DisplayHDR 500 True Black (1.1)
3B6DAA9E-3794-4D85-897E-93AE990D275D
VESA DisplayHDR 600 True Black (1.1)
9AD0FB30-006E-49FE-AA15-8F65F28A476B
VESA DisplayHDR 1000 True Black (1.1)
9C5D5F59-1FA8-4D2B-87DD-2E3B2BFF37D5

DOLBY

Dolby Vision
6363AA90-A651-4154-A9E1-2D765C08E68F

NVIDIA

NVIDIA G-SYNC ULTIMATE
35FBD985-74F0-4271-AC61-295F11D71AEF

AMD

AMD Freesync Premium Pro
F9310F0E-93B2-4A58-8642-17358D8CB2E3

最近因为笔记本的SSD出现了奇怪的问题,所以选择了重装系统,在重装系统之后,发现Dolby Atmos这些组件,由于声卡驱动的软件组件的存在,一旦连接到网络,即可通过Windows Update自动重新下载和安装,但是Dolby Vision这类额外组件,则需要手动补全。但在通过Microsoft Store的已购买功能找到Dolby Vision客户端安装后,发现调整设置没有反应,在Dolby Access中直接没有了杜比视界选项,播放Dolby Vision视频也没有了Logo提示和色彩校准,Netflix HDR视频内容也完全是偏色的状态。
FixDBV_MissingVision
在网上大量寻找资料,得知Dolby Vision是需要校准+密钥二合一文件才可在Windows下打开,于是便在网上大量寻找我这个型号的校准+密钥二合一文件,然而联想并未任何单独下载,其他OEM的方案则是通过显示器驱动来解决,然而联想并未给我这个型号提供任何显示器驱动。
由于跟我这个产品的PM有认识,他给出的方法是建议送到附近的维修站重装系统,然而我目前不在国内,并不具备去到服务站的条件。其后,他又给出了一个建议,使用联想海外版本的LDDRS(Lenovo Digital Download Recovery Service)服务,自行制作官方恢复盘进行恢复(可在此处找到)
但在刻录完启动盘之后,从U盘启动,发现其要格式化整个硬盘来重装系统,并且恢复到旧版本的Windows,由于前一次重装系统后数据已经重新安排妥当,所以我没有选择直接恢复,而是开始对恢复盘的文件内容进行逆向。
其恢复盘内的大部分内容通过IMZ文件(本体)+CRI文件(基本信息)来组成
FixDBV_RecoveryFileDetails
由于CRI是文本文件,则很方便的就寻找到了DolbyVision所对应的CRI和IMZ文件
FixDBV_CriInEditor
同时发现其所谓的IMZ,也仅仅只是7z压缩包而已
但是,在解压时,却又出现了问题
FixDBV_PasswordError
发现其中提到的"Password=lenovo"根本不对!这里有坑!所以又继续爬贴,爬Github,终于在一个小众论坛和Github上找到了解决方案(这里这里),在此感谢DenizOezmen的逆向!
那时候觉得Python不够优雅,所以把它给移植到了C++和Javascript,代码和可执行文件如下
网页版

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string PwChars = "k`gybs0vampjd";
    string Password;
    cout << "Lenovo Recovery Image IMZ File Password Analyzer\n(C)Yuu 2022\n";
    cout << "Password in CRI File>>";
    cin >> Password;
    cout << "Real Password: ";
    for (int i = 0; i < Password.length(); i++)
    {
        cout << (char)(PwChars[Password[i] % 13] - (i % 3) + 2);
    }
    cout << endl;
    system("pause");
    return 0;
}

下载可执行文件(Windows-x86)
执行后,成功得到了真实密码!
FixDBV_RealPassword
也终于成功解压出了所有文件!
FixDBV_RealFile
在对setup.cmd进行简单的分析过后,发现其实并不用那么复杂,其全部过程即为
1.先行安装"Headedcode"或"Headlesscode"文件夹内的appx

注:
1.如果你的设备使用DolbyAccess来管理所有杜比组件,则安装Headlesscode中的appx(即为Store中的Dolby Vision Extension)
2.如果你的设备跟我的一样,使用单独的Dolby Vision App来管理,则需要安装Headedcode中的appx

2.再通过管理员权限执行其中的ProvisionDolbyVision.exe,有可能会报错但不影响使用
3.此时重启设备,你会发现你的DolbyVision回来了!
FixDBV_Succeed

注:
通过对ProvisionDolbyVision.exe的分析,发现其只是检测MONITOR/后的字符串,然后从ICMS文件夹中,选定要复制的文件,将文件名改为PQCONFIG.DV后,复制到<SYSTEM_DRIVE>:\WINDOWS\System32\spool\drivers\color目录下而已,如果这个一键程序无法正常工作,则可以通过直接复制的方法来解决问题

去年年底,借着一些关系联系到了小米电视的PM,直接反馈了一些问题之后,小米给我手头这个18年底就断更的机器更新了最终版本的更新,把大部分Bug和缺憾都做了补全,我很是满意。
但是发现从老版本更新到新版本之后,系统原生的设置不见了。还好老版本当中没有odex化系统App,所以可以直接拿出来安装后补全。不知道其他用户有没有需求,所以在此随便记录一下。
测试版本: 4A 32" matrix Android6.0.1 MIUITV1.3.89

过程

1.安装Settings.apk和Starter.apk
2.于我的应用界面会出现以下图标的APP
MITVSettings_Starter
3.点选即可打开设置主页面
MITVSettings_AOSP1
MITVSettings_AOSP2
也可以通过快速点按版本号来打开完整版的开发者选项
MITVSettings_AOSP3

下载

Settings.apk
Starter.apk