이 프로젝트에서는 GE WiFi로 연결된 스마트 전구를 제어하기 위해 "C by GE" 앱을 리버스 엔지니어링했습니다. 이를 위해 Android 앱을 디컴파일한 다음 앱이 서버와 통신하는 데 사용하는 바이너리 프로토콜을 리버스 엔지니어링했습니다. 자세한 내용은 GE의 리버스 엔지니어링 C를 참조하세요.
이 프로젝트의 최종 제품은 다음과 같습니다.
면책 조항: 이 코드는 리버스 엔지니어링의 결과이며 프로토콜 사양에 의해 통보되지 않았습니다. 결과적으로 계속 작동하거나 모든 네트워크 또는 스마트 장치에서 작동한다는 보장은 없습니다. 어떤 경우에는 다른 사람들이 이 API를 성공적으로 사용했지만 코드가 모든 사용 사례에서 적용되지 않는 잘못된 가정을 할 수도 있습니다.
서버 디렉터리는 C by GE 전구용 독립형 웹앱 및 JSON API 엔드포인트입니다. 웹사이트는 다음과 같습니다:
-email
및 -password
인수를 사용하여 웹사이트를 실행하면 웹사이트를 처음 로드할 때 웹사이트에 2단계 인증 페이지가 표시됩니다. 버튼을 누르고 이메일로 전송된 인증 코드를 입력하세요. 또는 계정 정보에 -email
및 -password
플래그를 설정하고 login_2fa 명령을 실행하여 미리 로그인할 수 있습니다. 이 명령은 2FA 인증 코드를 입력하라는 메시지를 표시합니다. 이 코드를 입력하면 명령이 세션 정보를 JSON blob으로 표시합니다. 그런 다음 이 JSON을 서버의 -sessinfo
인수에 전달할 수 있습니다(예: -sessinfo 'JSON HERE'
. 세션의 일부는 일주일 후에 만료되지만 세션의 만료 가능한 부분은 장치를 열거하는 데 한 번만 사용되므로 실행 중인 서버 인스턴스는 이 시간 이후에도 계속 작동합니다.
최신 계정에는 이중 인증을 사용해야 합니다. 2FA 핸드셰이크를 수행하여 다음과 같이 세션을 생성할 수 있습니다.
callback , err := cbyge . Login2FA ( "my_email" , "my_password" , "" )
// Handle error...
sessionInfo , err := callback ( "2FA code from email" )
// Handle error...
session , err := cbyge . NewController ( sessionInfo , 0 )
// Handle error...
이전에 2FA를 사용한 적이 없는 오래된 계정의 경우 직접 로그인할 수 있습니다.
session , err := cbyge . NewControllerLogin ( "my_email" , "my_password" )
// Handle error...
세션이 있으면 다음과 같이 장치를 열거할 수 있습니다.
devs , err := session . Devices ()
// Handle error...
for _ , x := range devs {
fmt . Println ( x . Name ())
}
다음과 같이 전구를 제어할 수 있습니다.
x := devs [ 0 ]
session . SetDeviceStatus ( x , true ) // turn on
session . SetDeviceLum ( x , 50 ) // set brightness
session . SetDeviceCT ( x , 100 ) // set color tone (100=blue, 0=orange)
전구의 현재 설정을 쿼리할 수도 있습니다.
status , err := session . DeviceStatus ( x )
// Handle error...
fmt . Println ( status . IsOn )
fmt . Println ( status . ColorTone )
이 섹션에서는 C by GE 프로토콜의 일부를 어떻게 리버스 엔지니어링했는지 살펴보겠습니다.
첫 번째 단계는 Apktool을 사용하여 Android 앱을 분해하는 것이었습니다. 그러면 앱에 대한 Smali 분해가 생성됩니다. 여기저기 돌아다니며 URL과 도메인 이름을 검색했습니다. 처음에는 다음을 발견했습니다.
.field public static final API_VERSION : L java/lang/String ; = " v2/ "
.field public static final BASE_URL : L java/lang/String ; = " https://api-ge.xlink.cn:443/ "
이 API 엔드포인트가 사용된 위치를 확인하면 로그인, 장치 나열 등을 위한 일련의 JSON 기반 HTTP 호출이 빠르게 표시됩니다. 그러나 이 엔드포인트는 1) 장치 상태를 가져오는 방법을 제공하지 않는 것 같습니다. 2) 장치의 색상이나 밝기를 업데이트합니다.
앱이 스마트 전구와 통신하는 다른 방법이 있어야 했습니다. 하지만 분해에는 블루투스와 LAN 통신을 위한 코드가 가득했고, 전구를 제어하기 위한 글로벌 API 엔드포인트가 없어서 조금 걱정이 되었습니다. 더 나쁜 것은 Bluetooth를 껐다가 사용하려고 할 때마다 C by GE 앱이 불평을 한다는 것입니다. 그러나 결국에는 앱을 열어서 작동하게 한 다음 전구를 제어하면서 Bluetooth와 WiFi를 끌 수 있다는 것을 알게 되었습니다. 내가 해야 할 일은 앱이 "위치 추적 켜기"(블루투스와 WiFi의 이상한 이름)를 묻는 팝업을 열 때마다 안드로이드 "뒤로" 버튼을 누르는 것뿐이었습니다.
이 시점에서 나는 앱이 다른 신비한 HTTP(S) 연결을 만들고 있지 않다는 것을 상당히 확신했습니다. 하지만 흥미롭게도 Smali 코드의 다른 곳에서 "xlink.cn" 도메인을 찾았습니다.
.field public static final CM_SERVER_ADDRESS : L java/lang/String ; = " cm-ge.xlink.cn "
.field public static final CM_SERVER_PORT : I = 0x5ce2
이런, 이게 원시 소켓 기반 프로토콜이 될 수 있을까요? 시도해 보았는데 확실히 cm-ge.xlink.cn:23778
에 대한 TCP 연결을 열 수 있었습니다. 그러나 Smali에는 UDP 패킷에 대한 논리도 포함되어 있었기 때문에 앱이 어떤 프로토콜을 사용할지 확신할 수 없었습니다. 이를 염두에 두고 패킷 프록시를 생성하고 포트 23778에서 수신 대기하도록 설정했습니다. 그런 다음 도메인 cm-ge.xlink.cn
Smali 코드의 내 IP 주소로 바꾸고 앱을 APK로 다시 컴파일한 다음 설치했습니다. 내 전화.
확실히, 패치된 C by GE 앱은 즉시 패킷 프록시 인스턴스에 연결되어 채팅을 시작했습니다. 특히 블루투스와 WiFi가 꺼진 경우에만 이 작업이 수행되었습니다. 그렇지 않으면 스마트 전구와 로컬로 통신하는 것 중 하나를 선호하는 것 같았습니다.
앱이 사용하기로 선택한 프로토콜은 처리하기 가장 쉬운 결과였습니다. 1) UDP가 아닌 TCP였고, 2) 완전히 암호화되지 않았습니다. 첫 번째 메시지에는 내 계정에 대해 결코 변경되지 않는 인증 토큰이 포함되어 있기 때문에 암호화가 부족하다는 점은 돌이켜보면 다소 놀라운 일입니다.
나는 앱에서 서버로 보내는 메시지가 효과적으로 "재생"될 수 있다는 것을 발견했습니다. 조명을 켜고 끄는 데 사용되는 바이트(또는 패킷 프록시 덕분에 "패킷")를 파악한 후에는 새 소켓을 열고 동일한 패킷을 보내 동일한 결과를 얻을 수 있습니다. 이것은 좋은 징조였습니다. 최악의 시나리오는 아주 일반적이지 않더라도 내가 원하는 것을 스스로 구현할 수 있는 방법이 이미 있다는 것입니다.
이 시점에서는 프로토콜에 대해 더 깊이 파고들 시간이었습니다. 패킷 프록시를 사용한 실험과 Smali 디스어셈블리를 결합한 후에 나는 어떤 통신이 일어나고 있는지에 대해 상당히 일반적인 이해를 얻었습니다. 내가 가장 먼저 알아차린 것은 유형과 길이 필드(빅 엔디안)로 시작하는 "메시지"에서 통신이 이루어진다는 것입니다. 다음은 어떤 패킷 유형이 어디에 있는지, 그리고 최종적으로 특정 패킷 자체가 어떻게 인코딩되었는지 파악하는 것이었습니다. 다음은 세 장치의 상태를 포함하는 서버의 패킷 예입니다.
73 00 00 00 60 47 e2 be ab 00 37 00 7e 00 01 00 00 f9 52 4e
00 03 00 00 00 03 00 03 00 81 01 00 00 81 01 00 00 00 00 35
00 00 00 27 00 00 00 00 00 00 00 02 00 00 01 00 00 00 01 00
00 00 00 35 00 00 00 27 00 00 00 00 00 00 00 01 00 00 01 00
00 00 01 00 00 00 00 35 00 00 00 27 00 00 00 00 00 00 00 c8
7e
프로토콜이 충분히 작동되면 이를 위한 API를 만들었습니다. 이 API는 장치를 나열하고, 상태를 가져오고, 장치의 다양한 속성(예: 밝기 및 색상 톤)을 업데이트할 수 있습니다. 놀랍게도 내 API가 앱 자체보다 훨씬 빠르고 안정적이라는 것을 알았습니다. 원격 서버로 돌아가기 전에 Bluetooth나 WiFi를 사용하려고 하면 앱이 예상보다 훨씬 불안정하고 안정성이 떨어지는 것 같습니다.
마지막으로, 저는 이 앱이 지원하는 모든 장치를 소유하고 있지 않기 때문에 이러한 장치가 어떻게 작동하는지 리버스 엔지니어링할 동기가 없었습니다(또는 쉽게 할 수 없었습니다). 예를 들어, 같은 회사는 스마트 콘센트, 센서, 조명 스위치를 생산합니다.