All posts by RSR

기가지니

Intro

나는 단지 적당한 스피커가 필요했을 뿐이다. 그리고 중고나라에서 3만원에 CT1100 기가지니 구모델을 구입하였고….

구입했으면 뜯어야지

Specs.

CPU HiSilicon HI3798
RAM Samsung K4A8G165WB x4 =(4Gb)
EMMC Samsung KLM8G1GEME-B041
SPI ROM Macronix MX25L3255EXCI-10G(32Mb)
I2C ROM Fremont FT24C256A(256Kb)
WiFi WM1612-32P
Ethernet RTL8211E (1Gbps)
PowerAMP TPA3118 (30W, 2ch)
Unknown LG Innotech TWBM-B302D

ARM cortex A53 쿼드에 무려 4Gb 램이 들어갔다!
Poplar 보드에서 동일한 코어를 사용하며 많은 정보를 얻을 수 있다. (Egreat A5, Himedia Q10 pro)

UART Bootlog

EMMC 근처에 4pin 짜리 커넥터가 UART였다 baudrate 115200 으로 접근시 다음과 같이 부팅로그메시지가 출력되었다.

Bootrom start
Boot Media: SPI
Decrypt auxiliary code ...OK
Entry boot auxiliary code

Auxiliary code - v1.00
DDR code - V1.1.2 20160205
Build: Nov 17 2016 - 04:17:30

Reg Version:  v134
Reg Time:     2016-12-08 3:09hi3798cv2dmf_hi3798cv200_ddr4_4gbyte_16bitx2_8layers.reg
Reg Name:     hi3798cv2dmf_hi3798cv200_ddr4_4gbyte_16bitx2_8layers.reg
DDRS
Boot auxiliary code success
Bootrom success

INFO:    Move bl33 from 0x200b0b0 to 0x2000000, 8785406 Bytes
NOTICE:  BL31: v1.2(debug):26cbbdd
NOTICE:  BL31: Built : 13:05:11, Dec 19 2018
INFO:    BL31: Initializing runtime services
INFO:    BL31: Initializing BL32
TrustedCore Release Version : iCOS V100R006C10SPC001B001 base iCOS_MAIN_3.1.2  Dec  7 2016.15:21:35
Load Secure Cipher success. Build Time:[Dec  7 2016, 15:21:35]
[ERROR-TEE_DEMUX]:DMX_Drv_ModInit[2971]:@@@@@@@@@@@@@@ Debug for demux 2018_09_20 ##############
1/1/1970 0:0:0.285 TrustedCore Execute Successfully and jump to Linux  kernel
INFO:    BL31: Preparing for EL3 exit to normal world
INFO:    Entry point address = 0x2000000
INFO:    SPSR = 0x1d3
Uncompressing Linux... done, booting the kernel.
 V: f9000000 P: 000f8000 S: 02000000 T: 00000000
hi3798cv2x_map_io               [/innodigital3/work_giga/KT_GG1_rel/device/hisilicon/bigfish/sdk/source/kernel/linux-3.1]
1/1/1970 0:0:1.151 [error] tee_task_entry: no need to config agentid: 0x00000003
 1/1/1970 0:0:1.247 [error] tee_task_entry: no need to config agentid: 0x00000004
 1/1/1970 0:0:56.662 TEE> TA: Nov 23 2018, 18:05:45
1/1/1970 0:2:24.787 TEE> TA: Nov 23 2018, 18:05:45

안타깝게 쉘은 뜨지 않았다.
부팅과정에서 Console을 리다이렉트 시키는것같다.
Recovery부팅은 커널로그가 다 뜨긴하나 sh를 찾지못해 쉘을 못띄워주는것은 확인하였다 ㅠ

EMMC

가능하면 와이어만 날려서 EMMC 덤프뜨려고 열심히 Pinout을찾았다.

실패 ㅋ
그냥  chipoff 해서 덤프하였다.

Partition Info

Offset Name Size
0x00 bootargs1 1M
0x100000 bootargs2 1M
0x200000 deviceinfo 2M
0x400000 baseparam 8M
0xC00000 pqparam 8M
0x1400000 logo 20M
0x2800000 logobak 20M
0x3C00000 fastplay 40M
0x6400000 fastplaybak 40M
0x8C00000 misc 20M
0x8C00000 qbboot 1M
0xA100000 qbdata 300M
0x1CD000 trustedcore 40M
0x1F500000 recovery 40M
0x21D00000 kernel 40M
0x24500000 system 800M
0x56500000 recovery2 40M
0x58D00000 kernel2 40M
0x5B500000 system2 800M
0x8D500000 cache 800M
0xBF500000 tmpdata 32M
0xC1500000 userdata remain

 

Explore

좀 살펴보면 기가지니는 안드로이드를 기반으로 동작하는것을 확인할 수 있다.
system/build.prop 파일을 보면 ro.build.version.release=5.1.1 즉, 롤리팝이 올라가 있는것이 확인된다.

Hidden Menu

system 파티션에서 이것저것 분석하다보면
com.kt.gigagenie.launcher앱 일부 액티비티에서 “2485” 를 확인하는 코드를 발견할 수 있다.

    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == 0) {
            Logger.print((Object) this, "called onKey()-keyCode : " + keyCode + ", event : " + event);
            if (v.getId() == R.id.settingListView) {
                switch (keyCode) {
                    case 9: case 11: case 12: case 15:
                        if (this.currentMenu == 2 && this.currentMenuListIndex == 0) {
                            Logger.print((Object) this, "keycode : " + (keyCode - 7));
                            if (keyCode - 7 == 2) 
                                this.hiddenMenuCommand = "2";
                            else 
                                this.hiddenMenuCommand += "" + (keyCode - 7);
                            Logger.print((Object) this, "hiddenMenuCommand : " + this.hiddenMenuCommand);
                            return true;
                        }
                    case 23:
                        if (this.currentMenu == 2 && this.currentMenuListIndex == 0) {
                            if (!HIDDEN_COMMAND.equals(this.hiddenMenuCommand)) {
                                this.hiddenMenuCommand = "";
                                break;
                            }
                            this.hiddenMenuCommand = "";
                            Logger.print((Object) this, "HIDDEN MENU ON!!!!");
                            showHiddenUpgradePopup();
                            return true;
                        }
... 중략

2485를 누르고 확인을 누르면…. 뭐 별건없고 이것저것 테스트 해볼수 있는 히든메뉴가 뜬다.

StartupService Apk

좀더 보다보면  StartupService앱이 있는데 BOOT_COMPLETED시 아래의 코드를 실행시킨다.

    private void launchExternalFactory(File file) {
        try {getPackageManager().installPackage(Uri.fromFile(file), new Stub() {
                public void packageInstalled(String packageName, int returnCode) throws RemoteException {
                    if (returnCode == StartupService.CONDITION_A_FACTORY_MODE) {
                        StartupService.this.StartActivity(new ComponentName(StartupService.PACKAGE_AFACTORYMODE, "net.quber.afactorymode.AFactoryModeMainActivity"));
                    }}}, CONDITION_DEFAULT_APP_INSTALL, null);
        } catch (Exception e) {            e.printStackTrace();        }
    }

        fileList = new File("/mnt/sda/sda1").listFiles();
        if (!(fileList == null || fileList.length == 0)) {
            length = fileList.length;
            for (i = NOT_EXIST_CONDITION; i < length; i += CONDITION_A_FACTORY_MODE) {
                file = fileList[i];
                Log.d(TAG, "name: " + file.getName());
                if (file.getName().equals("SmartFactory.apk")) {
                    launchExternalFactory(file);
                    return true;
                }        }        }

뭐 대충 sda에서 “SmartFactory.apk” 파일찾아서 그거 설치하고 “net.quber.afactorymode.AFactoryModeMainActivity” 액티비티를 실행시킨다.

적당히 “net.quber.afactorymode”이름의 앱하나 만들어서 USB에 넣고 부팅을 시켜주면…

내가만든 앱이 실행된다.
앱의 SETTING 버튼을 선택하면 설정창이 열린다. ( 다른건 하다가… ㅈㅈ)
설정 -> 홈 -> 지니 런처 삭제 순으로 안드로이드 디폴트 런처가 실행된다.



같은 방법으로 다른 어플리케이션을 설치할 수 있다. (액티비티 이름이 고정이라 실행은 되지 않겠지만..)
ES파일탐색기 이런 앱 깔아두면 편하다.

Bluetooth Sink

기본설치된 앱들 다 지워버리니까 블루투스 스피커로써 역할을 하지 못한다. (페어링은 되나 소리가 나지않음;)
com.kt.gigagenie.mc 앱을 다시 설치해주니 소리가 잘난다.

Getting Root

루트권한 획득까지는 관심이 없었는데….
보다보니 백도어가 보여서 분석을 좀더 진행해 보았다.

부팅후 프로세스 목록에서 좀 찝찝하게 생겨먹은 xx와 yy를 분석해보니
“/var/alticast/fork_socket”를 열고 여기서 오는 패킷을 root권한으로 실행해 주고 있었다.
com.kt.gigagenie.ollehtv 앱의 libalticaptor.so에서는 “/var/alticast/fork_socket”를 열고 명령어를 입력하는 코드가 있으며 뭘하는지는 관심이 없고,
패킷 구조만 확인해서 …! …! 하니
root획득 성공
관련코드 execute_command.c (execute_command)

ADB Shell

Terminal app 설치해서 열심히 찍기 귀찮아서 adbd도 올려보았다.

어디서(출처가 어디었지…) 가져온 adbd 바이너리를 /mnt/ddd로 복사한 뒤 실행권한 주고

setprop ro.kernel.qemu 1
setprop ro.debuggable 1
setprop ro.secure 1
setprop service.adb.root 1
setprop service.adb.tcp.port 5555
/mnt/ddd/excute_command “/mnt/ddd/adbd & ”

하면 ADB  over TCP를 사용할 수 있다. (루트권한으로!)

System Intergraty check

시스템파티션 일부를 수정했더니 부팅이 되지 않았다.
다행히 리커버리로 부팅하여 강제 업데이트를 실행하니 부팅에 성공, 원인 분석을 하였다.

커널의 initrd에 있는 init.rc파일에 다음과 같은 설정이 있다.

on property:sys.system.check=start
system_check

system_check는 initrd의 init에 구현되어 있으며
/system/etc/system_list을 읽어 system 파티션의 무결성을 검증한다.
system_list의 뒷부분에는 전자서명으로 보이는 부분이 있다.

그리고 “sys.system.check” 속성을 start로 바꿔주어야 이게 시작이 되는데,
해당부분은 \system\etc\init.bigfish.sh에서 확인가능하다.

setprop sys.system.check start

그럼뭐 저거 실행시키기전에 !@#$하고나서 system 파티션 원복하고 sys.system.check=start 하면 되겠네 싶어서 해봤는데 역시 부팅불가 (왜?)

Update

KTFirmwareUpdate.apk파일을 잘 보면 https로 어딘가 접속해서 목록을 받아오고 목록에는 업데이트 받는 apk, bin, zip 의 주소가 들어있다.
apk들은 User영역에 설치되는 앱파일들이며 zip은 잘 모르겠고, bin은 펌웨어 파일이다.
이중 KAON_CT1100_xxxx.bin 파일은 AES_CBC로 암호화 되어있으며 내부에는 kernel, recovery이미지와 system파일들이 들어있다.
업데이트 하는것이 목적이 아니라 패스.

Recovery

부팅시 WPS 키를 눌러주면 Recovery모드로 들어가게된다 이때 WPS키를 누르면 강제 업데이트가 가능하다.
강제 업데이트시에 미리 설정된 USB가 있어야 되는데
위의 Update과정에서 확인가능한 KAON_CT1100_xxxx.bin 파일과 설정파일(kaon/fwinfo.txt)을 USB의 특정파일이름으로 위치시켜야 한다.
이부분은 recovery의 initramfs에 있는/sbin/recovery를 분석하면 알 수 있다.(상세내용은 검열삭제)

Kernel Mod

커널을 수정했더니 리커버리도 안들어가지고 계속 재부팅만 한다.
커널은 건들지 않는것으로…

Conclusion

노이즈 때문에 못쓰겠다.
파일 넣어서 직접 재생시키면 조금 덜하긴한데 그래도 화이트 노이즈가 거슬리는 수준.
메쉬는 2.1처럼 보이지만 실제는 1.1채널 모노스피커라 실망이 크다.
음색 또한 저음이 매우 부스팅 되어있어 내가 좋아하는 소리는 아니다.
영상의 경우 Youtube에 있는 4K60f 샘플을 정말 부드럽게 플래이해 냈다. (내 노트북에서는 스로틀링걸리면서 버벅거리는데….)
가장큰 목적은 Netflix 설치 후 시청이었으나, Netflix 앱 설치후 조작이 불편하였고, 재생 해상도 역시 좋지않아(Netflix 미인증 기기) 포기하였다.

나는 더이상 이기기를 사용, 분석할 흥미가 없다.

WinDivert – Windows Packet Divert

사실은 이거 우회하는거 프로그램으로 만드려다 알게된 드라이버

윈도 방화벽은 단순히 포트나 어플리케이션 단위로만 컨트롤이 되서 TCP Flags 기준으로 필터링하지 못한다.
원 드라이버 홈페이지 https://reqrypt.org/windivert.html
여기는 파이썬으로 래핑해놨다.

pip로 바로 설치가능

 pip install pydivert

간단하게 작성한 rst 제거 스크립트

import pydivert

with pydivert.WinDivert("inbound and tcp.Rst and tcp.Ack") as w:
    for packet in w:
        packet.tcp.rst=False
        w.send(packet)

WinDivert를 이용하면 뭔가 나중에 재밌는걸 만들어 볼 수 있을것 같다.

warning.or.kr season 2

엊그제부터 인터넷 도감청이 이슈다.
이전에는 HTTP프로토콜의 HOST 헤더를 확인함을 분석하여 HTTPS를 사용하는경우 우회가 가능하였다. (이전분석)

이번에는 HTTPS를 사용하더라도 접속이 안되는것을 확인, 분석하였다.

유명한 성인사이트 접속시 HTTPS 접속임에도 ERR_CONNECTION_RESET에러가 뜨며 접속이 거부되었다.

Wireshark 패킷을 확인해보니

Client Hello 이후 RST, ACK패킷이 확인된다.
중요한점은 RST 패킷 이후에도 Server Hello가 확인된다.
이는 서버에서 정상적으로 응답하나 중간에 어떤xx가 RST패킷을 보내는것으로 추정된다.
TCP Reset Attack 의 일종이다.

RST패킷을 보내는 기준으로는 TLS패킷의 Server Name Indication extension (SNI)로 보여진다.

GDNS를 통해 원본의 IP주소를 확인하고 /etc/hosts파일을 수정하여 test.com으로 접속시 대상의 서버로 접속하게 만들어 보았다.
이후 https://test.com 접속시 다음과 같은 패킷을 확인할 수 있다.


목적지로부터 정상적인 응답이다.

하지만 서버는 301 Moved Permanently 응답을 보내며 원본의 도메인으로 Redirect 시켜버리며 다시 Client Hello는 reset 되어버린다.

서버는 정상적으로 응답하는것을 재차 확인하였고
중간에 날라오는 RST, ACK를 잘 처리하면 (iptables -I INPUT -p tcp –tcp-flags ALL RST,ACK -j REJECT –reject-with tcp-reset)

우회하여 접속할 수 있다.

Android analysis

disable DM-Verity

mod fstab file in initrd of boot.bin
delete “verify” option in mount option field

set SELinux permissive

add below to kernel cmdline
enforcing=0 androidboot.selinux=permissive

useful adb command

Get Activity List
dumpsys package | grep -i [app_id] | grep Activity

Run Activity
am start -a android.intent.action.MAIN -n [app_id]/.activities.xxxx [-e extra_id extra_value]

extract bootimg
abootimg -x boot.bin

modding kernel ramfs
to extract
cat initrd | gunzip | cpio -vid
to merge
find ./ | sort | cpio -o -H newc | gzip -9 > ../new_initrd
make bootimg
abootimg –create new_boot_su.bin -f bootimg.cfg -k zImage -r new_initrd -s stage2.img

restart zygote
killall zygote ## 가끔 잘안됨
setprop ctl.restart zygote

 

Start adbd via commandline

setprop service.adb.tcp.port 5555
settings put global development_settings_enabled 1
settings put global adb_enabled 1
start adbd

adb push adbkey.pub /data/misc/adb/adb_keys

https://stackoverflow.com/questions/26213954/how-to-solve-adb-device-unauthorized-in-android-adb-host-device

useful tools

jadx
JEB