本合集共7部分,本篇为第三部分
根据这些头文件,看起来它们提供了一种非常方便的方式与车辆进行交互,提供了查询里程表、电池电压等信息的函数,以及执行启动发动机或锁定/解锁车门等操作的功能。
在名为 HBody.h 的一个 ccOS 头文件中,包含了类 HBody。HBody 是一个单例类,其中包含一个静态方法来获取 HBody 的实例。HBody 本身包含一个名为 isDoorOpened 的方法,用于判断特定门是开着还是关着。
HBody 中的所有查询函数都返回一个 HResult,指示它是否成功查询了对象,或者是什么错误阻止了查询。每个查询方法还接受一个输出类型的引用,以提供实际的查询结果。
isDoorOpened 函数接受一个 HDoorPosition 枚举来指定你要查看的门是开着还是关着(前左/右、后左/右或尾门),以及一个 HTriState 的引用,用于指示门是否打开(False、True、Invalid)。
以下是我编写的代码:
#include <iostream>
#include <vector>
#include <string>
#include "HBody.h"
using namespace std;
const char* HResultToString(ccos::HResult result)
{
switch (result)
{
case ccos::HResult::INVALID:
return "INVALID";
case ccos::HResult::OK:
return "OK";
case ccos::HResult::ERROR:
return "ERROR";
case ccos::HResult::NOT_SUPPORTED:
return "NOT_SUPPORTED";
case ccos::HResult::OUT_OF_RANGE:
return "OUT_OF_RANGE";
case ccos::HResult::CONNECTION_FAIL:
return "CONNECTION_FAIL";
case ccos::HResult::NO_RESPONSE:
return "NO_RESPONSE";
case ccos::HResult::UNAVAILABLE:
return "UNAVAILABLE";
case ccos::HResult::NULLPOINTER:
return "NULLPOINTER";
case ccos::HResult::NOT_INITIALIZED:
return "NOT_INITIALIZED";
case ccos::HResult::TIMEOUT:
return "TIMEOUT";
case ccos::HResult::PERMISSION_DENIED:
return "PERMISSION_DENIED";
case ccos::HResult::ALREADY_EXIST:
return "ALREADY_EXIST";
case ccos::HResult::SOME_UNAVAILABLE:
return "SOME_UNAVAILABLE";
case ccos::HResult::INVALID_RESULT:
return "INVALID_RESULT";
case ccos::HResult::MAX:
return "MAX";
default:
return "Other";
}
}
int main()
{
cout << "Ioniq Test Application";
cout << endl;
ccos::vehicle::general::HBody *body = ccos::vehicle::general::HBody::getInstance();
ccos::vehicle::HTriState doorState;
ccos::HResult doorOpenedResult = body->isDoorOpened(ccos::vehicle::HDoorPosition::FRONT_LEFT, doorState);
if (doorOpenedResult == ccos::HResult::OK) {
cout << "Door Result: " << (doorState == ccos::vehicle::HTriState::TRUE ? "Open" : "Closed");
cout << endl;
} else {
cout << "isDoorOpened did not return OK. Actual return: " << HResultToString(doorOpenedResult);
cout << endl;
}
cout << "Finished door test";
cout << endl;
}
All I had to do is… (Don’t do this)
我启动了反向 shell,将二进制文件复制到 /tmp/ 目录,将其标记为可执行,并运行了它。
#include <iostream>
#include <string>
#include "HBody.h"
#include "HChassis.h"
using namespace std;
namespace ccOSUtils
{
const char *HTriStateToString(ccos::vehicle::HTriState state)
{
switch (state)
{
case ccos::vehicle::HTriState::FALSE:
return "False";
case ccos::vehicle::HTriState::TRUE:
return "True";
case ccos::vehicle::HTriState::INVALID:
return "INVALID";
case ccos::vehicle::HTriState::MAX:
return "MAX";
default:
return "Other";
}
}
const char *HResultToString(ccos::HResult result)
{
switch (result)
{
case ccos::HResult::INVALID:
return "Invalid";
case ccos::HResult::OK:
return "OK";
case ccos::HResult::ERROR:
return "ERROR";
case ccos::HResult::NOT_SUPPORTED:
return "NOT_SUPPORTED";
case ccos::HResult::OUT_OF_RANGE:
return "OUT_OF_RANGE";
case ccos::HResult::CONNECTION_FAIL:
return "CONNECTION_FAIL";
case ccos::HResult::NO_RESPONSE:
return "NO_RESPONSE";
case ccos::HResult::UNAVAILABLE:
return "UNAVAILABLE";
case ccos::HResult::NULLPOINTER:
return "NULLPOINTER";
case ccos::HResult::NOT_INITIALIZED:
return "NOT_INITIALIZED";
case ccos::HResult::TIMEOUT:
return "TIMEOUT";
case ccos::HResult::PERMISSION_DENIED:
return "PERMISSION_DENIED";
case ccos::HResult::ALREADY_EXIST:
return "ALREADY_EXIST";
case ccos::HResult::SOME_UNAVAILABLE:
return "SOME_UNAVAILABLE";
case ccos::HResult::INVALID_RESULT:
return "INVALID_RESULT";
case ccos::HResult::MAX:
return "MAX";
default:
return "Other";
}
}
}
int main(int argc, char *argv[])
{
cout << "Ioniq Advanced Test Application" << endl;
if (argc == 1)
{
cout << "Provide at least 1 argument (doorStatus, doorLock, status, test)" << endl;
return 0;
}
ccos::vehicle::general::HBody *body = ccos::vehicle::general::HBody::getInstance();
string command = argv[1];
if (command == "doorStatus")
{
if (argc != 3)
{
cout << "Expected arguments: doorStatus {fl/fr/rl/rr}" << endl;
return 0;
}
string doorStr = argv[2];
ccos::vehicle::HDoorPosition doorPosition = ccos::vehicle::HDoorPosition::FRONT_LEFT;
if (doorStr == "fl")
{
doorPosition = ccos::vehicle::HDoorPosition::FRONT_LEFT;
}
else if (doorStr == "fr")
{
doorPosition = ccos::vehicle::HDoorPosition::FRONT_RIGHT;
}
else if (doorStr == "rl")
{
doorPosition = ccos::vehicle::HDoorPosition::REAR_LEFT;
}
else if (doorStr == "rr")
{
doorPosition = ccos::vehicle::HDoorPosition::REAR_RIGHT;
}
ccos::vehicle::HTriState doorState;
ccos::HResult doorOpenedResult = body->isDoorOpened(doorPosition, doorState);
if (doorOpenedResult == ccos::HResult::OK)
{
cout << "Door Result: " << (doorState == ccos::vehicle::HTriState::TRUE ? "Open" : "Closed");
cout << endl;
}
else
{
cout << "isDoorOpened did not return OK. Actual return: " << ccOSUtils::HResultToString(doorOpenedResult);
cout << endl;
}
}
else if (command == "doorLock")
{
if (argc != 3)
{
cout << "Expected arguments: doorLock {true/false}" << endl;
return 0;
}
string shouldBeLockedStr = argv[2];
ccos::HBool shouldBeLocked = false;
if (shouldBeLockedStr[0] == 't')
{
shouldBeLocked = true;
}
cout << "Setting Door Locks to: " << (shouldBeLocked ? "Locked" : "Unlocked") << endl;
ccos::HResult doorLockResult = body->requestDoorLock(shouldBeLocked);
if (doorLockResult == ccos::HResult::OK)
{
cout << "Door Lock Success" << endl;
}
else
{
cout << "Door Lock Failure: " << ccOSUtils::HResultToString(doorLockResult) << endl;
}
}
else if (command == "status")
{
ccos::vehicle::general::HChassis *chassis = ccos::vehicle::general::HChassis::getInstance();
ccos::HFloat odometerReading = 0;
chassis->getOdometer(odometerReading);
ccos::HFloat batteryVoltage = 0;
chassis->getBatteryVoltage(batteryVoltage);
ccos::HUInt8 percentBatteryRemaining = 0;
chassis->getRemainBattery(percentBatteryRemaining);
cout << "Vehicle Status:" << endl;
cout << "tOdometer: " << odometerReading << endl;
cout << "tBattery Voltage: " << batteryVoltage << "V" << endl;
cout << "tBattery Remaining: " << percentBatteryRemaining << "%" << endl;
}
else if (command == "test")
{
cout << "Testing methods that might not work" << endl;
ccos::HResult testResult;
cout << "tTesting Wireless Charging Pad State" << endl;
ccos::HUInt8 wirelessChargingPadState = 0;
testResult = body->getWirelessChargingPadState(wirelessChargingPadState);
cout << "tt" << ccOSUtils::HResultToString(testResult) << " - State: " << wirelessChargingPadState << endl;
cout << "tTesting Window State (Driver)" << endl;
ccos::vehicle::HWindowType windowType = ccos::vehicle::HWindowType::DRIVER;
ccos::vehicle::HTriState windowState;
ccos::HUInt8 windowDetail;
body->getWindowOpenState(windowType, windowState, windowDetail);
cout << "tt" << ccOSUtils::HResultToString(testResult) << " - State: " << ccOSUtils::HTriStateToString(windowState) << "Detail?: " << windowDetail << endl;
cout << "Completed testing methods that might not work" << endl;
}
else
{
cout << "Unknown Command" << endl;
}
cout << "Completed" << endl;
return 0;
}
GUI
然后,我尝试编译 Qt5,事实上尝试了几次,但每次都遇到不同的错误。我发现的第一个错误之一是 Qt 正在将各种文件安装到我的 IVI 的挂载系统根镜像中,但默认情况下,该镜像没有足够的空间来容纳它们。我使用以下命令使用 dd 命令将 system.img 的大小增加了 1GB,然后使用 resize2fs 调整 system.img 中的文件系统大小,以便能够使用新增的空间:
dd if=/dev/zero count=4 bs=256M >> system.imgsudo mount system.img system_imageFULL_SYSROOT_DIR=$(realpath system_image)SYSROOT_MOUNT_DEVICE=$(df | grep $FULL_SYSROOT_DIR | awk '{print $1}')sudo resize2fs $SYSROOT_MOUNT_DEVICE
cp system_image/usr/lib/libGLESv2.so.2 system_image/usr/lib/libGLESv2.so
./configure -device arm-generic-g++ -device-option CROSS_COMPILE=/home/greenluigi1/QtDev/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi- -opensource -confirm-license -sysroot /home/greenluigi1/QtDev/system_image -skip declarative -nomake examples -skip virtualkeyboard
该脚本将下载并安装正确的交叉编译器、QtCreator,并编译正确版本的 Qt。由于系统镜像的挂载是临时的,我还包含了挂载脚本,以便在重新启动前和开发前重新挂载系统镜像。
现在,我就快完成了,我只需要设置 QtCreator 来使用我的设置即可。
我浏览了系统中的所有 GUI 应用程序,并寻找最小的一个。我选择了位于 /usr/share/AMOS/EProfilerApp 中的 EProfilerApp。根据名称,它是一个简单的 GUI 应用程序,用于查看/管理 AMOS,即内置的系统性能分析工具。我将 EProfilerApp 导入到 IDA 中,并开始分析其二进制代码:
每个 Helix 应用程序的 main() 函数看起来像这样:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyApplication myApplication = MyApplication();
myApplication.init(app, argc, argv);
return app.exec();
}
1. 应用视图(App Views)
– 表示一个用户可以与之交互的 Qt5 屏幕/窗口。这包括全屏窗口以及弹出窗口。
– 负责创建、显示、隐藏和销毁普通的 Qt5 窗口。
2. 应用服务(App Services)
– 表示一个后台进程。
3. 事件接收器(Event Receivers)
– 表示用于处理系统中可能发出的各种事件的处理程序。
该应用程序具有一个名为“ExampleGuiAppView”的单个 AppView,其组件名称为“com.greenluigi1.guiExample.TestAppView”。这个 AppView 创建了一个简单的窗口,其中包含 4 个按钮:
锁定车辆所有的门。
Unlock
解锁车辆的门。
表现得像钥匙的解锁一样。按下一次将解锁驾驶员侧门,按下两次将解锁所有门。
Test
向 Logcat 日志打印一条测试消息。
Exit
使用 finish() 函数退出应用程序。
但在此之前,还有一些事情需要做。我们需要“注册”该应用程序,以便 Helix 能够识别它。Helix 的应用程序管理器通过读取 /etc/appmanager/appconf 目录中的 .ini 文件来工作。每个 .ini 文件告诉 Helix 应用程序的组件名称,列出每个 AppView、AppService 和 EventReceiver,并指定您的 EventReceivers 监听的事件。
一个典型的应用程序配置看起来像这样:
[Application]
Name=com.company.grouping.appName
Exec=/usr/share/app-appName-1.0.0/appName
[TestAppService]
#ComponentName : com.company.grouping.appName.TestAppService
Type=AppService
[TestAppView]
#ComponentName : com.company.grouping.appName.TestAppView
Type=AppView
[TestEventReceiver]
#ComponentName : com.company.grouping.appName.TestEventReceiver
Type=EventReceiver
Event=com.mobis.caudio.ACTION.POWER_OFF
在“[Application]”组之后,可以跟随任意数量的组。每个组代表一个新的组件。组的名称指示组件的名称,例如“[TestAppView]”表示它正在定义一个名为“TestAppView”的组件,或者在这种情况下更具体地说是:“com.company.grouping.appName.TestAppView”。在组件组下是该组件的特定设置。每个组件组都有一个类型,可以是 AppView、AppService 或 EventReceiver。每种类型的组件都可以有自己的设置,例如 EventReceiver 类型具有 Event 属性,该属性是 Receiver 订阅的事件的逗号分隔列表。以“#”开头的行是注释,被 Helix 忽略。
我只需要制作自己的 .appconf 文件,以便启动我的应用程序。这是我想到的内容:
[Application]
Name=com.greenluigi1.guiExample
Exec=/appdata/guiExample
[TestAppView]
# ComponentName : com.greenluigi1.guiExample.TestAppView
Type=AppView
我将已编译的应用程序及其配置文件复制到了我的 USB 驱动器上,并启动了我的反向 shell。我将根目录挂载为读/写,以便可以修改配置文件夹。然后,我将我的 GuiExampleApp.appconf 配置文件复制到 /etc/appManager/appconf/ 文件夹,并将应用程序本身复制到 /appdata/ 文件夹。
然后我发送了重启命令,等待 IVI 重新启动。
现在我只需要启动我的应用程序,但我应该如何做呢?从命令行运行应用程序时,它不会自行执行任何操作。我们需要告诉 Helix 来启动它。
幸运的是,在之前的调查中,我发现了一个已经安装在机器上的命令行工具,可以做到这一点:appctl。appctl 是一个小程序,允许您:
appctl startAppView com.greenluigi1.guiExample.TestAppView
原文始发于微信公众号(安全脉脉):HowIHackedMyCar 2021款 现代IONIQ (三) Making Software