Post

메세지 미리보기 실패 부분 역공학

이미지 미리보기 실패에 관하여 살짝 살펴보기
메세지 미리보기 실패 부분 역공학

README

해당 게시글은 버그 및 기능에 대한 연구/분석 목적이며 그 외로 활용할 경우 책임지지 않습니다.

분석

메세지를 미리보기로 읽을 수 있는 앱이 있다.

너무 잘 사용하고 있는 꽤 재밌는 앱이다. 어느 순간 사진을 프리뷰 형태로 볼 수 없어서 패치되길 기다렸는데 수정이 되지 않아 한번 그 내부에 어떤 문제가 있는지 살펴보았다.

이때 기존 사용하는 구글의 정적 분석 툴과 자주 사용하던 툴이 있었는데 조금 변형하여 버그 찾기에 용이하도록 패치 후 사용했다.

앱 버전은 다음과 같다.

1
2
android:versionCode="418" 
android:versionName="1.8.2.2"

미리보기 실패한 사진 부분

메니페스트에 알림을 리스너 영역을 확인해본다.

1
2
3
4
5
  <service android:exported="true" android:name="com.alpha.beta.redacted" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
    <intent-filter>
      <action android:name="android.service.notification.NotificationListenerService"/>
    </intent-filter>
  </service>

아래에서 알림이 올 때 호출되는 Posted 부분을 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    @Override  // android.service.notification.NotificationListenerService
    public void onNotificationPosted(StatusBarNotification statusBarNotification0) {
        Notification notification0 = statusBarNotification0.getNotification();
        if(notification0 != null && ((notification0.flags & 2) == 0 && (notification0.flags & 0x20) == 0 && (notification0.flags & 0x200) == 0 && notification0.extras != null)) {
            String s = statusBarNotification0.getPackageName();
            if(!a.b(this, "allow_all_app", false) && (("android".equals(s)) || !"com.kakao.talk/jp.naver.line.android/kr.co.vcnc.android.couple/com.instagram.android/com.whatsapp/com.nhn.android.band/com.facebook.katana/com.facebook.orca/com.viber.voip/org.telegram.messenger/com.tencent.mobileqq/com.tencent.mobileqqi/com.tencent.mm/com.discord/com.twitter.android/com.kakao.talk".contains(s))) {
                return;
            }

            try {
                this.i(notification0, s);
            }
            catch(IllegalArgumentException illegalArgumentException0) {
                com.google.firebase.crashlytics.a.a().d(illegalArgumentException0);
            }

            return;
        }
    }

중간에 this.i() 를 실행하는 걸 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    private void i(Notification notification0, String s) {
        Context context0 = this.getApplicationContext();
        if(context0 == null) {
            return;
        }

        Bitmap bitmap0 = m.d(context0, notification0.extras);
        this.f();
        this.c.execute(new Runnable() {
            @Override
            public void run() {
                d d0 = redacted.this.a;
                m.i(context0, notification0, s, bitmap0, d0).g(new g() {
                    public void a(c c0) {
                        redacted.this.e(com.alpha.beta.redacted.b.this.b, c0, com.alpha.beta.redacted.b.this.d);
                    }
                }).e(new s4.f() {
                    @Override  // s4.f
                    public void c(Exception exception0) {
                        com.google.firebase.crashlytics.a.a().d(exception0);
                    }
                });
            }
        });
    }

실행 후 내부 m.i() 를 진입하는 걸 확인한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    public static j i(Context context0, Notification notification0, String s, Bitmap bitmap0, d d0) {
        s4.b b0 = new s4.b();
        s4.k k0 = new s4.k(b0.b());
        if(context0 == null) {
            k0.b(new NullPointerException("Context is null"));
            return k0.a();
        }

        Bundle bundle0 = notification0.extras;
        if(bundle0 == null) {
            k0.b(new NullPointerException("Bundle is null"));
            return k0.a();
        }

        c c0 = new c(bundle0, notification0.when, s, notification0.tickerText);
        if(!c0.O() && !c0.M(d0) && !m.j(c0.F())) {
            // 영어와 한글에 대한 조건문 분류
            if((c0.L("photo")) || (c0.L("사진")) || (bundle0.containsKey("android.wearable.EXTENSIONS")) && !"com.google.android.gm".equals(s)) {
                File file1 = context0.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
                if(file1 != null) {
                    c0.Y(context0, notification0, file1.getAbsolutePath(), "photo");
                }
            }
            else if((c0.L("emoticon")) || (c0.L("이모티콘"))) {
                File file0 = context0.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
                if(file0 != null) {
                    c0.Y(context0, notification0, file0.getAbsolutePath(), ">>EMO>");
                }
            }

            m.g(c0, m.e(context0, c0, bitmap0));
            k0.c(c0);
            return k0.a();
        }

        b0.a();
        return k0.a();
    }

위에서 보면 사진에 대한 메세지를 가져오는 부분이 있다.

처음에 보면 특별한 문제가 없어 보이지만 첫 사진에 봤을 때 Upper case Photo 인 것을 볼 수 있다.

디바이스 언어 환경이 영어일 때 알림을 읽는 부분에서 알림 API 에서 Photo 와 Emoticon 을 주는데 조건 필터링이 Lower case 를 취하고 있어 case 가 맞지 않아 사진 미리보기가 정상적으로 진행되지 않은 것을 확인할 수 있다.

실제로 한국어로 디바이스를 세팅하면 사진 읽는데 문제가 없다.

정상적인 사진 미리보기

문제에 대하여 개발자에 메일로 첨부하였고 현재는 패치된 버전이 플레이스토어에 배포되었다.

This post is licensed under CC BY 4.0 by the author.
If you find any errors, please let me know by comment or email. Thank you.

© Ruffalo. Some rights reserved.

I'm

Using the Chirpy theme for Jekyll.