연합뉴스 기사
조선일보 기사

 

금일(21.03.23.) 안드로이드 스마트폰 이용자 사이에서 앱(카카오톡 등) 실행이 중단되는 이슈가 발생했고, 곧바로 핫픽스 업데이트로 해결되었다.

해당 원인(Google 형님들의 깜찍한 실수가...무엇인지)을 알아보고자 패치 전후 파일을 비교하며 원인 분석을 해보았다.

 

분석 대상

- 문제의 APK : Android System WebView 89.0.4389.90

- 패치된 APK : Android System WebView 89.0.4389.105

 

분석 전 작업

- 문제 파일 : libwebviewchromium.so

 

패치 전 vs 후

 

2개의 라이브러리(libwebviewchromium.so)를 가지고 먼저, IDA에서 바이너리 읽어와서 BinDiff로 비교해보았다.

 

 

크로미움 라이브러리 아니랄까봐 엄청 걸린다...
4시간째 Diffing .... o-<-<
DIff 완료
유사도

 

분석

- 90.so (sub_29D69E4)

int __fastcall sub_29D69E4(int a1, int a2, int a3, _DWORD *a4)
{
  unsigned __int64 v8; // kr00_8
  int v9; // r7
  int v10; // r1
  int result; // r0
  int v12; // r1
  __int64 v13; // r0
  int v14; // r1
  unsigned __int64 v15; // kr08_8
  int v16; // r1
  unsigned int v17; // r5
  int v18; // r10
  unsigned int v19; // r8
  unsigned int v20; // r3
  unsigned __int64 v21; // r0
  unsigned __int64 v22; // r0
  int v23; // r0
  unsigned int v24; // r1
  int v25; // r1
  unsigned __int64 v26; // r2
  int v27; // r5
  int v28; // r1
  int v29; // [sp+18h] [bp-E0h]
  char v30[12]; // [sp+24h] [bp-D4h] BYREF
  char v31[8]; // [sp+30h] [bp-C8h] BYREF
  _BYTE v32[192]; // [sp+38h] [bp-C0h] BYREF

  if ( !(*(int (__fastcall **)(int))(*(_DWORD *)a2 + 12))(a2) )
  {
    v12 = sub_20FA49C(2);
    result = 0;
    if ( !v12 || !a1 )
      return result;
    sub_20FA4A0(v31, "../../third_party/crashpad/crashpad/snapshot/memory_snapshot.cc", 32, 2);
    v13 = ((__int64 (__fastcall *)(int))*(_DWORD *)(*(_DWORD *)a2 + 8))(a2);
    goto LABEL_10;
  }
  if ( !(*(int (__fastcall **)(int))(*(_DWORD *)a3 + 12))(a3) )
  {
    v14 = sub_20FA49C(2);
    result = 0;
    if ( !v14 || !a1 )
      return result;
    sub_20FA4A0(v31, "../../third_party/crashpad/crashpad/snapshot/memory_snapshot.cc", 38, 2);
    v13 = ((__int64 (__fastcall *)(int))*(_DWORD *)(*(_DWORD *)a3 + 8))(a3);
LABEL_10:
    sub_1C6A35C(v30, "invalid empty range at 0x%llx", v13, HIDWORD(v13));
    goto LABEL_11;
  }
  v8 = ((__int64 (__fastcall *)(int))*(_DWORD *)(*(_DWORD *)a2 + 8))(a2);
  v9 = (*(int (__fastcall **)(int))(*(_DWORD *)a2 + 12))(a2);
  if ( !__CFADD__(__CFADD__(v9, (_DWORD)v8), HIDWORD(v8)) )
  {
    v15 = ((__int64 (__fastcall *)(int))*(_DWORD *)(*(_DWORD *)a3 + 8))(a3);
    v29 = (*(int (__fastcall **)(int))(*(_DWORD *)a3 + 12))(a3);
    if ( __CFADD__(__CFADD__(v29, (_DWORD)v15), HIDWORD(v15)) )
    {
      v16 = sub_20FA49C(2);
      result = 0;
      if ( !v16 || !a1 )
        return result;
      sub_20FA4A0(v31, "../../third_party/crashpad/crashpad/snapshot/memory_snapshot.cc", 54, 2);
      sub_1C6A35C(v30, "invalid range at 0x%llx, size %zu", v15, HIDWORD(v15), v29);
      goto LABEL_11;
    }
    if ( v9 )
    {
      v18 = v29;
      v17 = v15;
      v19 = HIDWORD(v8);
      v20 = HIDWORD(v15);
      v21 = v8 + (unsigned int)v9;
      if ( v15 < v21 && v29 && v8 < v15 + (unsigned int)v29 )
        goto LABEL_29;
    }
    else
    {
      v19 = HIDWORD(v8);
      v21 = v8;
      v20 = HIDWORD(v15);
      v17 = v15;
      v18 = v29;
    }
    HIDWORD(v21) ^= v20;
    if ( v21 != v17 )
    {
      v22 = __PAIR64__(v20, v17) + (unsigned int)v18;
      HIDWORD(v22) ^= v19;
      if ( v22 != (unsigned int)v8 )
      {
        v28 = sub_20FA49C(2);
        result = 0;
        if ( !v28 || !a1 )
          return result;
        sub_20FA4A0(v31, "../../third_party/crashpad/crashpad/snapshot/memory_snapshot.cc", 63, 2);
        sub_1C6A35C(v30, "ranges not overlapping or abutting: (0x%llx, size %zu) and (0x%llx, size %zu)", v8, v19, v9);
LABEL_11:
        sub_1CAA466(v32, v30);
        sub_1BA02A4(v30);
        goto LABEL_12;
      }
    }
LABEL_29:
    if ( a4 )
    {
      v23 = 0;
      v24 = v19;
      if ( __PAIR64__(v20, v17) < __PAIR64__(v19, v8) )
        v24 = v20;
      a4[1] = v24;
      v25 = v8;
      if ( __PAIR64__(v20, v17) < __PAIR64__(v19, v8) )
        v25 = v17;
      v26 = __PAIR64__(v20, v17) + (unsigned int)v18;
      v27 = v8 + v9;
      *a4 = v25;
      if ( __PAIR64__(v19, v8) + (unsigned int)v9 < v26 )
        v23 = 1;
      if ( v23 )
        v27 = v26;
      a4[2] = v27 - v25;
    }
    return 1;
  }
  v10 = sub_20FA49C(2);
  result = 0;
  if ( v10 && a1 )
  {
    sub_20FA4A0(v31, "../../third_party/crashpad/crashpad/snapshot/memory_snapshot.cc", 45, 2);
    sub_1C6A35C(v30, "invalid range at 0x%llx, size %zu", v8, v9);
    sub_1CAA466(v32, v30);
    sub_1BA02A4(v30);
LABEL_12:
    sub_20FA4A4(v31);
    result = 0;
  }
  return result;
}

 

- 105.so (sub_194CC50)

// attributes: thunk
int __fastcall sub_194CC50(int a1, int a2)
{
  return sub_21F7436(a1, a2);
}

 

패치 전후 코드 Diff 비교해보니 몇몇 함수에서 코드 날리고 콜백 함수가 추가되었지만, 강제 종료될만한 문제를 찾지 못했다. 그래서 문제의 APK를 설치해서 Crash 덤프를 살펴보았다.

 

- 카카오톡 Crash Info

$ adb logcat -b crash -v tag

--------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
E/AndroidRuntime: Process: com.kakao.talk, PID: 5694
E/AndroidRuntime: android.view.InflateException: Binary XML file line #22 in com.kakao.talk:layout/fragment_shopping_tab: Binary XML file line #22 in com.kakao.talk:layout/fragment_shopping_tab: Error inflating class com.kakao.talk.activity.shop.commerce.CommerceCustomWebView
E/AndroidRuntime: Caused by: android.view.InflateException: Binary XML file line #22 in com.kakao.talk:layout/fragment_shopping_tab: Error inflating class com.kakao.talk.activity.shop.commerce.CommerceCustomWebView

...

E/AndroidRuntime: Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: "/data/app/~~kTTfUTIJkSRazMbBfuk5kw==/com.google.android.webview-ATG1yhB-CsB259OEQSIu-A==/lib/arm/libwebviewchromium.so" is 32-bit instead of 64-bit
E/AndroidRuntime:       at java.lang.Runtime.loadLibrary0(Runtime.java:1087)
E/AndroidRuntime:       at java.lang.Runtime.loadLibrary0(Runtime.java:1008)
E/AndroidRuntime:       at java.lang.System.loadLibrary(System.java:1664)
E/AndroidRuntime:       at wH.j(chromium-SystemWebViewGoogle.aab-stable-438909000:17)
E/AndroidRuntime:       ... 57 more

 

 ... dlopen failed: ... is 32-bit instead of 64-bit

아... 그냥 64bit 안드로이드 기기여서 32bit 라이브러리를 제대로 불러오지 못해 Runtime 에러가 발생한 것이다.
그러므로 카카오톡을 실행할 때 WebView 앱 또한 실행되면서 동시에 강제 종료되는 점이 원인이 될 수 있다.

그렇기 때문에 WebView 배포 시 64bit 안드로이드를 위해 앱 라이브러리(lib)에 arm64-v8a가 존재하지 않아 문제가 생겼다. (구글 형님들의 코딩에 의한 실수는 아녔다...😅)

 

arm64-v8a....응? 있는데??? 아...?

 

APK 미러 사이트에서는 arm64-v8a가 보여서 해당 앱을 설치한 결과 강제 종료되는 현상이 보이지 않았고 다양한 기기에 테스팅 못해 찝찝한 마음으로 분석을 끝냅니다. (여담으로 배포는 03.14.인데 오류는 03.23.에 나온 거지?)

 

요약

1. 앱 배포 시 64bit 안드로이드에서 arm64-v8a에 맞추지 않아도 WebView 앱은 설치가 가능하다.

2. 하지만, 64bit 안드로이드에서 32bit 라이브러리를 불러오는 과정에서 Runtime 에러 발생

3. 2019년 8월 1일부터는 Google Play에 앱 등록 시 64bit 라이브러리를 지원해야 하므로 개발할 때 유의하자.

 

 

Reference

64비트 아키텍처 지원 : developer.android.com/distribute/best-practices/develop/64-bit?hl=ko
APKMirror : www.apkmirror.com/