CherryTalk 상세 분석 보고서

CherryTalk 양자 나노 단위 분석 종합 보고서

분석일: 2025-04-11 ~ 2026-02-13

분석자: 주식회사 아크링크 (사업자등록번호: 378-88-03382)

분석 목적: 피싱/스미싱 [피해자 정보 비공개] 위한 악성앱 심층 분석

보고서 버전: v3.0 (양자 나노 단위 분석 + pinonaoshsfpaohjgadsf 클러스터 통합)

위협 등급: HIGH

분석 Phase: 12단계 완료

목차

1. [개요](#1-개요)

2. [분석 여정 (타임라인)](#2-분석-여정-타임라인)

3. [앱 기본 정보](#3-앱-기본-정보)

4. [전체 클래스 통계 및 smali 패키지 맵핑](#4-전체-클래스-통계-및-smali-패키지-맵핑)

5. [코드 구조 분석](#5-코드-구조-분석)

6. [ContactUtils 9메서드 정밀 분석](#6-contactutils-9메서드-정밀-분석)

7. [초대코드 반전 로직 (T001)](#7-초대코드-반전-로직-t001)

8. [네트워크/C2 분석](#8-네트워크c2-분석)

9. [pinonaoshsfpaohjgadsf 클러스터 분석](#9-pinonaoshsfpaohjgadsf-클러스터-분석)

10. [SMS 휴면코드 정밀 분석 (T040)](#10-sms-휴면코드-정밀-분석-t040)

11. [이미지 압축 파이프라인](#11-이미지-압축-파이프라인)

12. [방어기제 분석](#12-방어기제-분석)

13. [데이터 탈취 분석](#13-데이터-탈취-분석)

14. [우회 결과](#14-우회-결과)

15. [MALBIO 유전체](#15-malbio-유전체)

16. [피해 영향 평가](#16-피해-영향-평가)

17. [IOC (침해 지표)](#17-ioc-침해-지표)

18. [결론 및 권고사항](#18-결론-및-권고사항)

· [부록 A: Kotlin 메타데이터 / R8 Synthetic 분석](#부록-a-kotlin-메타데이터--r8-synthetic-분석)

· [부록 B: Variant A/B vs CherryTalk 아키텍처 비교](#부록-b-variant-ab-vs-cherrytalk-아키텍처-비교)

1. 개요

1.1 분석 목적

본 보고서는 CherryTalk (Cherryj.apk) 악성앱에 대한 양자 나노 단위 분석 결과를 정리한 것입니다. "양자 나노 단위 분석"이란 앱의 바이트코드를 가장 작은 단위(smali 명령어)까지 분해하여 동작 원리를 완전히 파악하는 심층 분석 기법입니다.

CherryTalk은 Wavegrid 패밀리에 속하는 연락처/이미지 탈취형 악성앱으로, 특히 pinonaoshsfpaohjgadsf C2 클러스터에 소속된 3개 앱(CherryTalk, Flirting_95, Y) 중 가장 최신 기술 스택(Kotlin 1.9 + EasyHttp)을 채택한 변종입니다.

1.2 분석 범위

항목 · 수치

**총 분석 클래스** · 9,490개 (smali/ 7,350 + smali_classes2/ 2,140)

**핵심 앱 클래스** · 12개 (com.dynamics.textmessage.*)

**분석 smali 파일** · 28개 (핵심 코드 경로)

**API 엔드포인트** · 3개

**데이터 수집 메서드** · 9개 (ContactUtils)

**네트워크 스택** · 2개 (EasyHttp + OkHttp 이중)

**분석 Phase** · 12단계

1.3 분석 환경

항목 · 내용

**디컴파일러** · apktool 2.9.3

**분석 도구** · smali 코드 수동 분석 + Java 역컴파일 교차검증

**정적 분석** · 전체 9,490 클래스 구조 분석

**네트워크 분석** · C2 서버 오프라인 (정적 분석만 수행)

**MALBIO** · 27차원 유전체 벡터 분석

1.4 핵심 발견 요약

# · 발견 사항 · 심각도 · 섹션

1 · 초대코드 반전 로직: code==200이면 FAILURE · **Critical** · 7

2 · SMS 휴면코드: Calendar 필터 생성하나 쿼리 미적용 · **High** · 10

3 · pinonaoshsfpaohjgadsf 3앱 클러스터 확인 · **High** · 9

4 · EasyHttp + OkHttp 이중 네트워크 스택 · Medium · 8

5 · Luban + compressImage + saveFile 3단계 이미지 파이프라인 · Medium · 11

6 · PermissionX 라이브러리 포함이나 미사용 · Low · 12

7 · DebugProbesKt.bin 릴리스 빌드에 디버그 정보 포함 · Low · 부록 A

8 · 1회 전송 메커니즘 (XOR 연산 기반 버튼 비활성화) · Medium · 13

2. 분석 여정 (타임라인)

Phase 1: 초기 디컴파일 (2025-04-11)

```

목표: APK 구조 파악

수행 작업:

├─ APK 디컴파일 (apktool 2.9.3)

├─ AndroidManifest.xml 추출

│ └─ 패키지명: com.wuidad.uwhjhzfd (난독화)

│ └─ 메인 액티비티: MainActivity

├─ 리소스 구조 확인

│ └─ res/, assets/ 추출

└─ 기본 파일 목록 확인

결과: 디컴파일 성공, Jiagu 미적용 확인

```

Phase 2: smali 코드 정밀 분석 (2026-02-05)

```

목표: C2 서버 및 통신 구조 파악

수행 작업:

├─ smali 코드 전수 분석

│ ├─ HttpUrl.smali → C2 URL 추출 ([비공개])

│ ├─ Myapplication.smali → EasyHttp 초기화 확인

│ └─ ContactUtils.smali → 데이터 수집 메서드 식별

├─ 내부 패키지명 발견

│ └─ com.dynamics.textmessage (외부 난독화명과 불일치)

└─ 네트워크 스택 식별

└─ EasyHttp(로그인/연락처) + OkHttp(이미지) 이중 구조

결과: C2 서버 URL 및 통신 구조 완전 파악

```

Phase 3: API 엔드포인트 확인 + Wavegrid 분류 (2026-02-05)

```

목표: API 구조 분석 및 패밀리 분류

수행 작업:

├─ API 엔드포인트 3개 확인

│ ├─ POST /webapp/login (JSON)

│ ├─ POST /webapp/saveAddressBook (JSON)

│ └─ POST /webapp/image/{userName} (Multipart)

├─ ML.apk과 API 구조 100% 일치 확인

│ └─ 동일 [비공개] 경로 패턴

├─ RuoYi 프레임워크 식별

│ └─ [비공개] prefix = RuoYi Spring Boot 표준

└─ Wavegrid 패밀리 분류 확정

결과: Wavegrid 패밀리 소속 확정, ML.apk 동일 인프라

```

Phase 4: 전체 공격 흐름 분석 (2026-02-05)

```

목표: 데이터 탈취 전체 체인 분석

수행 작업:

├─ 로그인 → 권한 요청 → 데이터 수집 → 전송 흐름 분석

├─ ContactUtils 5개 메서드 초기 분석

├─ SMS 미사용 코드 발견 (getMsgs 존재, 호출 안 됨)

├─ IOC 추출 (도메인, URL, 패키지명, SharedPreferences 키)

└─ 1회 전송 메커니즘 확인 (IS_UPLOAD 플래그)

결과: 공격 흐름 분석 완료, IOC 추출 완료

```

Phase 5: 11섹션 표준 포맷 업그레이드 (2026-02-12)

```

목표: Gold Standard 보고서 포맷 적용

수행 작업:

├─ MALBIO 27차원 유전체 벡터 계산

├─ 피해 영향 평가 (6대 카테고리)

├─ Wavegrid 패밀리 비교 분석

└─ IOC 체계화 (6개 카테고리)

결과: v2.0 보고서 완성 (640줄)

```

Phase 6: 전체 클래스 통계 분석 (2026-02-13)

```

목표: 9,490 클래스 전수 구조 파악

수행 작업:

├─ smali/ 디렉토리: 7,350 클래스 열거

│ ├─ com.dynamics.textmessage (핵심 앱)

│ ├─ com.zhouyou.http (EasyHttp)

│ ├─ com.blankj.utilcode (AndroidUtilCode)

│ ├─ com.alibaba.fastjson (FastJSON)

│ └─ okhttp3, io.reactivex, com.gyf.immersionbar 등

├─ smali_classes2/ 디렉토리: 2,140 클래스 열거

│ ├─ top.zibin.luban (Luban 이미지 압축)

│ ├─ com.permissionx (PermissionX - 미사용)

│ └─ androidx.* (Support Library)

└─ R8 synthetic 클래스 카운팅

결과: 총 9,490 smali 클래스 구조 맵핑 완료

```

Phase 7: ContactUtils 9메서드 정밀 분석 (2026-02-13)

```

목표: 데이터 수집 엔진 명령어 단위 분석

수행 작업:

├─ getContact() → ContactsContract.CommonDataKinds.Phone 쿼리

├─ getContacts() → 2단계 쿼리 (has_phone_number → 하위 쿼리)

│ └─ trim() + replace(" ", "") + replace("-", "") 정규화

├─ getContactss() → content://com.android.contacts/data/phones

├─ getImg() → MediaStore.Images, date_modified DESC

├─ getAlbum() → MediaStore.Images, projection=null

├─ getMsgs() → SMS 휴면코드 정밀 분석 (Calendar 미적용 발견!)

├─ getOperatorName() → TelephonyManager.getSimOperatorName()

├─ compressImage() → JPEG quality 100→-10 루프, 100KB 목표

└─ saveFile() → externalCacheDir + "/Ask", JPEG 80%

결과: 9개 메서드 바이트코드 레벨 완전 분석

```

Phase 8: 초대코드 반전 로직 발견 (2026-02-13)

```

목표: 로그인 응답 처리 로직 정밀 분석

수행 작업:

├─ MainActivity$toLogin$2.smali 바이트코드 분석

│ ├─ line 164: const/16 v1, 0xc8 (= 200)

│ ├─ line 165: if-eq v0, v1, :cond_2 (code == 200 → FAILURE)

│ └─ line 197: code != 200 → SUCCESS (UUID/INVITE_CODE 저장)

├─ Wavegrid 패밀리 반전 패턴 교차 확인

│ └─ ze → zine 진화에서 최초 도입된 패턴

└─ 반전 로직 문서화

핵심 발견: HTTP 200 = 실패, HTTP 200이 아닌 응답 = 성공!

표준 HTTP 의미론을 의도적으로 뒤집은 안티 분석 기법

결과: T001 (Invitation Code Logic Reversal) 기법 확인

```

Phase 9: pinonaoshsfpaohjgadsf 클러스터 분석 (2026-02-13)

```

목표: 동일 C2 도메인명 사용 앱 그룹 교차 분석

수행 작업:

├─ CherryTalk: [비공개] (HTTP, Kotlin+EasyHttp)

├─ Flirting_95: [비공개] (HTTP, Java+OkHttp, Gen3 SMS)

├─ Y: [비공개] (HTTP, Java+OkHttp, Gen3 SMS)

├─ 기술 스택 비교 분석

│ └─ CherryTalk = 최신 스택 (Kotlin+EasyHttp+MVVM)

│ └─ Flirting_95/Y = 레거시 스택 (Java+OkHttp)

└─ 동일 운영자/조직 확정

결과: 3앱 클러스터 확인, 동일 TLD패턴 다른 확장자

```

Phase 10: SMS 휴면코드 정밀 분석 (2026-02-13)

```

목표: getMsgs() 메서드 바이트코드 레벨 분석

수행 작업:

├─ Calendar.getInstance() 호출 확인

│ └─ Calendar.add(Calendar.DATE, -1) → 1일 전 타임스탬프 생성

├─ ContentResolver.query() 파라미터 분석

│ └─ selection=null, selectionArgs=null → WHERE절 없음!

├─ AndroidManifest.xml READ_SMS 권한 확인

│ └─ 미선언 → SecurityException 발생

└─ Wavegrid 템플릿 잔여 코드 확정

핵심 발견: Calendar 필터 코드는 존재하지만 쿼리에 미적용

→ ALL SMS 접근 시도 코드이나 권한 미선언으로 비활성

결과: T040 (SMS Dormant Code) 기법 확인

```

Phase 11: 이미지 압축 파이프라인 분석 (2026-02-13)

```

목표: 3단계 이미지 처리 체인 분석

수행 작업:

├─ Stage 1: Luban 라이브러리 압축

│ └─ ExternalSyntheticLambda 콜백 2개 확인

├─ Stage 2: compressImage() 자체 압축

│ └─ JPEG quality 100 시작, 100KB 초과 시 -10씩 감소

├─ Stage 3: saveFile() 최종 저장

│ └─ externalCacheDir + "/Ask", JPEG 80% 품질

└─ OkHttp Multipart 전송 연결

결과: 3단계 이미지 압축 파이프라인 완전 분석

```

Phase 12: 양자 나노 보고서 통합 (2026-02-13)

```

목표: v3.0 Gold Standard 보고서 통합

수행 작업:

├─ Phase 6~11 신규 발견 사항 통합

├─ 부록 A: Kotlin 메타데이터 / R8 Synthetic 분석

├─ 부록 B: Variant A/B 아키텍처 비교

├─ pinonaoshsfpaohjgadsf 클러스터 교차 참조

└─ 보고서 v2.0 → v3.0 업그레이드

결과: 양자 나노 단위 분석 보고서 v3.0 완성 (800줄+)

```

3. 앱 기본 정보

항목 · 내용

**파일명** · Cherryj.apk (cherrytalk.apk)

**패키지명 (외부)** · `com.wuidad.uwhjhzfd` (난독화)

**패키지명 (내부)** · `com.dynamics.textmessage`

**앱 표시명** · CherryTalk

**버전** · 1.0 (versionCode: 1)

**Min SDK** · 26 (Android 8.0 Oreo)

**Target SDK** · 34 (Android 14)

**Compile SDK** · 34 (Android 14)

**언어** · Kotlin 1.9.0

**프레임워크** · EasyHttp + OkHttp + ViewBinding + MVVM

**보호 방식** · 없음 (평문 코드, Jiagu 미적용)

**패밀리** · **Wavegrid**

**C2 클러스터** · **pinonaoshsfpaohjgadsf** (3앱 공유)

**UI 라이브러리** · ImmersionBar (몰입형 상태바), Luban (이미지 압축)

**직렬화** · FastJSON + Gson

**유틸리티** · BlankJ AndroidUtilCode (SPUtils, GsonUtils 등)

**빌드 타입** · `app_release`

**R8/ProGuard** · 적용 (mapping-90543.bin 존재)

**Kotlin 메타데이터** · mv = {0x1, 0x9, 0x0} = Kotlin 1.9.0

**특이사항** · `DebugProbesKt.bin` 포함 - 릴리스 빌드에 디버그 프로브 잔존

3.1 내부 패키지 구조

```

com.dynamics.textmessage/

├── Myapplication.kt # Application 클래스 (EasyHttp 초기화, C2 URL 설정)

├── model/

│ ├── CollectBean.java # 연락처 데이터 (name: String, number: String)

│ ├── InfoBean.java # 범용 데이터 (content, date, displayName, path, type)

│ └── ResultSuccess.java # API 응답 모델

├── ui/

│ ├── base/

│ │ └── BaseMVVMActivity.kt # MVVM 베이스 (리플렉션 기반 ViewBinding)

│ ├── MainActivity.kt # 로그인 (초대코드 입력 + 반전 로직)

│ └── UpActivity.kt # 데이터 수집 및 전송 (1회 전송)

├── viewmodel/

│ ├── MainActivityVM.kt # 로그인 ViewModel

│ └── UpActivityVM.kt # 업로드 ViewModel

├── util/

│ ├── HttpUrl.kt # API 엔드포인트 정의 + OkHttp 파일 업로드

│ ├── ContactUtils.java # 데이터 수집 엔진 (9메서드)

│ ├── GlobleValue.kt # 전역 상수 (IF_FIRST, INVITE_CODE, IS_UPLOAD, UUID_VALUE)

│ └── GeneralDialog.java # 권한 요청 커스텀 다이얼로그

└── databinding/

├── ActivityMainBinding # 로그인 화면 바인딩

├── ActivityUpBinding # 업로드 화면 바인딩

└── DiaologPermissionBinding # 권한 다이얼로그 바인딩 (오타: "Diaolog")

```

3.2 컴포넌트 목록 (AndroidManifest.xml)

타입 · 클래스 · 역할

Application · `com.dynamics.textmessage.Myapplication` · HTTP 클라이언트 초기화, C2 URL 설정

Activity · `com.dynamics.textmessage.ui.MainActivity` · LAUNCHER - 로그인/초대코드 입력

Activity · `com.dynamics.textmessage.ui.UpActivity` · 데이터 수집 + 서버 전송

Activity · `com.blankj.utilcode.util.UtilsTransActivity4MainProcess` · BlankJ 유틸 (투명)

Activity · `com.blankj.utilcode.util.UtilsTransActivity` · BlankJ 유틸 (멀티프로세스)

Provider · `com.blankj.utilcode.util.UtilsFileProvider` · 파일 공유 provider

Service · `com.blankj.utilcode.util.MessengerUtils$ServerService` · IPC 메신저 서비스

Provider · `androidx.startup.InitializationProvider` · Emoji2 + Lifecycle + ProfileInstaller 초기화

Receiver · `androidx.profileinstaller.ProfileInstallReceiver` · 프로파일 설치 수신기

3.3 데이터 모델 비교

모델 · 필드 · 용도

**CollectBean.java** · `name: String`, `number: String` · 연락처 전용 (이름 + 전화번호)

**InfoBean.java** · `content: String`, `date: String`, `displayName: String`, `path: String`, `type: String` · 범용 (연락처/이미지/SMS 겸용)

**ResultSuccess.java** · API 응답 필드 · 서버 응답 파싱

설계 분석: CollectBean은 연락처 저장에 최적화된 경량 모델, InfoBean은 5개 필드로 연락처/이미지/SMS를 모두 담을 수 있는 범용 모델. 두 모델이 병존하는 것은 ContactUtils의 getContact()/getContacts()/getContactss()가 서로 다른 반환 타입을 사용하기 때문.

3.4 권한 분석

3.4.1 요청 권한 목록

# · 권한 · 위험도 · 피해 시나리오

1 · `READ_CONTACTS` · **위험** · 피해자의 전체 연락처(이름, 전화번호) 탈취. 공격자가 지인에게 협박 메시지 전송 가능

2 · `READ_PHONE_STATE` · **위험** · IMEI, 기기 모델, 통신사 정보 수집. [피해자 정보 비공개] 및 추적에 활용

3 · `READ_PHONE_NUMBERS` · **위험** · [피해자 정보 비공개] 전화번호 수집. `TelephonyManager.getLine1Number()` 호출 확인

4 · `READ_MEDIA_IMAGES` · **위험** · Android 13+ 사진 접근. [피해자 정보 비공개] 전체 탈취 후 협박 소재로 활용

5 · `READ_MEDIA_VIDEO` · **위험** · Android 13+ 동영상 접근. 영상 탈취 후 유포 협박

6 · `READ_EXTERNAL_STORAGE` · **위험** · Android 12 이하 전체 저장소 접근 (maxSdkVersion=32)

7 · `WRITE_EXTERNAL_STORAGE` · 중간 · 파일 저장 (캐시 디렉토리 `/Ask` 폴더)

8 · `MOUNT_UNMOUNT_FILESYSTEMS` · 중간 · 파일시스템 마운트 (외부 저장소 접근 보조)

9 · `INTERNET` · 일반 · C2 서버와의 네트워크 통신

10 · `ACCESS_NETWORK_STATE` · 일반 · 네트워크 연결 상태 확인

11 · `DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION` · 시스템 · 내부 브로드캐스트 수신기 보호 (signature 레벨)

3.4.2 권한 요청 흐름 (코드 기반)

smali 코드 분석 결과, UpActivity.requestPermission() 메서드에서 4개 권한을 일괄 요청:

```java

// UpActivity.requestPermission(int permissionSize)

String[] permissions = {

"android.permission.READ_CONTACTS",

"android.permission.READ_PHONE_STATE",

"android.permission.READ_PHONE_NUMBERS",

"android.permission.READ_EXTERNAL_STORAGE"

};

requestPermissions(permissions, permissionSize);

```

· onCreate() 시점에 requestPermission(0x4a) 호출 (requestCode=74)

· 권한 거부 시 showActiveDialog() 호출 -> 설정 화면으로 유도

· 권한 허용 시 즉시 uploadAllData() 호출 -> 데이터 탈취 시작

중요: PermissionX 라이브러리가 smali_classes2에 포함되어 있으나(com/permissionx/), UpActivity는 PermissionX를 사용하지 않고 직접 requestPermissions() 시스템 API를 호출. PermissionX는 라이브러리 종속성으로만 포함, 실제 코드에서 미활용.

3.4.3 Wavegrid 패밀리 권한 비교

권한 · CherryTalk · ML.apk · Chinso · Nyang

READ_CONTACTS · O · O · O · O

READ_PHONE_STATE · O · O · O · O

READ_PHONE_NUMBERS · O · O · O · -

READ_MEDIA_IMAGES · O · - · - · -

READ_MEDIA_VIDEO · O · - · - · -

READ_EXTERNAL_STORAGE · O · O · O · O

CAMERA · - · O · - · -

차이점: CherryTalk은 CAMERA 권한 없이 READ_MEDIA_IMAGES/VIDEO (Android 13+ 세분화 권한)를 사용. Wavegrid 패밀리 중 가장 최신 Android API 호환성을 보유.

4. 전체 클래스 통계 및 smali 패키지 맵핑

4.1 DEX 파일 구조

DEX 파일 · 디렉토리 · 클래스 수 · 내용

`classes.dex` · `smali/` · **7,350** · 핵심 앱 코드 + 주요 라이브러리

`classes2.dex` · `smali_classes2/` · **2,140** · 보조 라이브러리 + AndroidX

**합계** · - · **9,490** · -

4.2 smali/ 패키지 맵핑 (classes.dex, 7,350 클래스)

# · 패키지 · 추정 클래스 수 · 설명

1 · `com.dynamics.textmessage` · ~12 · **핵심 악성 코드** (앱 로직 전체)

2 · `com.zhouyou.http` · ~150 · EasyHttp 네트워크 라이브러리

3 · `com.blankj.utilcode` · ~300 · BlankJ AndroidUtilCode 유틸리티

4 · `com.alibaba.fastjson` · ~200 · FastJSON 직렬화

5 · `com.google.gson` · ~100 · Gson 직렬화

6 · `com.gyf.immersionbar` · ~30 · ImmersionBar 상태바 라이브러리

7 · `okhttp3` · ~400 · OkHttp HTTP 클라이언트

8 · `okio` · ~200 · Okio I/O 라이브러리 (OkHttp 의존)

9 · `io.reactivex` · ~500 · RxJava 비동기 라이브러리

10 · `kotlin` · ~2,000 · Kotlin 표준 라이브러리

11 · `kotlinx.coroutines` · ~500 · Kotlin Coroutines

12 · 기타 (`org.intellij.lang`, `org.jetbrains` 등) · ~2,958 · Kotlin 컴파일러 생성 코드

4.3 smali_classes2/ 패키지 맵핑 (classes2.dex, 2,140 클래스)

# · 패키지 · 추정 클래스 수 · 설명

1 · `top.zibin.luban` · ~15 · **Luban 이미지 압축** (사진 탈취 파이프라인)

2 · `com.permissionx` · ~20 · PermissionX (**포함되나 미사용**)

3 · `androidx.core` · ~300 · AndroidX Core

4 · `androidx.appcompat` · ~200 · AndroidX AppCompat

5 · `androidx.lifecycle` · ~100 · AndroidX Lifecycle (ViewModel 지원)

6 · `androidx.fragment` · ~80 · AndroidX Fragment

7 · `androidx.emoji2` · ~50 · Emoji2 라이브러리

8 · `androidx.startup` · ~10 · App Startup

9 · `com.google.android.material` · ~800 · Material Design Components

10 · 기타 AndroidX · ~565 · 기타 지원 라이브러리

4.4 핵심 앱 클래스 상세 (12개)

```

com.dynamics.textmessage/

├── Myapplication.kt # 앱 진입점, EasyHttp 초기화

├── model/

│ ├── CollectBean.java # 연락처 DTO (name, number)

│ ├── InfoBean.java # 범용 DTO (5필드)

│ └── ResultSuccess.java # API 응답 DTO

├── ui/

│ ├── base/BaseMVVMActivity.kt # MVVM 베이스 클래스

│ ├── MainActivity.kt # 로그인 (+ $toLogin$2 콜백)

│ │ └── MainActivity$toLogin$2.smali # ★ 반전 로직 핵심

│ └── UpActivity.kt # 데이터 수집 (+ 람다 4개)

│ ├── UpActivity$$ExternalSyntheticLambda0~3 # R8 desugaring

│ └── getAlbumDataUpload$1 # Luban 콜백 (+ 람다 2개)

├── viewmodel/

│ ├── MainActivityVM.kt # 로그인 VM

│ └── UpActivityVM.kt # 업로드 VM

└── util/

├── HttpUrl.kt # C2 URL 상수 + OkHttp 업로드

├── ContactUtils.java # ★ 데이터 수집 엔진 (9메서드)

├── GlobleValue.kt # 전역 상수

└── GeneralDialog.java # 권한 다이얼로그

```

5. 코드 구조 분석

5.1 아키텍처 패턴

· MVVM (Model-View-ViewModel): BaseMVVMActivity<VB, VM> 제네릭 베이스 클래스

· ViewBinding: 리플렉션 기반 자동 inflate (BaseMVVMActivity.onCreate에서 inflate 메서드 리플렉션 호출)

· EasyHttp + RxJava: 비동기 네트워크 통신 (io.reactivex.disposables.Disposable 반환)

· OkHttp Multipart: 파일 업로드 시 직접 OkHttp 사용 (HttpUrl.postFile)

5.2 주요 클래스 상세 분석

5.2.1 Myapplication (Application 클래스)

```kotlin

// Myapplication.kt (smali 역컴파일)

class Myapplication : Application() {

override fun onCreate() {

super.onCreate()

EasyHttp.init(this)

EasyHttp.getInstance()

.setBaseUrl("[비공개])

.setRetryCount(3) // 실패 시 3회 재시도

}

}

```

핵심 발견: C2 URL이 하드코딩되어 있으며, 재시도 횟수 3회로 설정. 네트워크 불안정 상황에서도 데이터 전송을 보장하려는 의도.

5.2.2 MainActivity (로그인)

```kotlin

// 주요 로직 흐름

class MainActivity : BaseMVVMActivity<ActivityMainBinding, MainActivityVM>() {

private var uuid: String = ""

override fun initView() {

// IF_FIRST 플래그 확인 -> true이면 즉시 UpActivity로 이동

if (SPUtils.getInstance().getBoolean("IF_FIRST", false)) {

startUp() // 재실행 시 바로 데이터 수집

return

}

uuid = SPUtils.getInstance().getString("UUID_VALUE", "")

}

// 로그인 버튼 클릭

private fun onLoginClick(view: View) {

val code = mBinding.etCode.text.toString()

if (TextUtils.isEmpty(code)) {

Toast.makeText(this, getString(R.string.login_title), 0).show()

return

}

if (TextUtils.isEmpty(uuid)) {

uuid = UUID.randomUUID().toString()

}

toLogin(uuid, code) // uuid = cId, code = name (초대코드)

}

private fun toLogin(uuid: String, code: String) {

mBinding.loading.visibility = View.VISIBLE

val params = HashMap<String, String>().apply {

put("name", code) // 초대코드

put("cId", uuid) // 기기 UUID

}

EasyHttp.post("webapp/login")

.upJson(GsonUtils.toJson(params))

.execute(callback) // MainActivity$toLogin$2 (반전 로직!)

}

}

```

5.2.3 UpActivity (데이터 수집)

```kotlin

// 핵심 탈취 메서드

class UpActivity : BaseMVVMActivity<ActivityUpBinding, UpActivityVM>() {

private var phoneNumber: String = ""

private var code: String = "" // 초대코드

private var isUpload: Boolean = false // 업로드 완료 플래그

override fun initView() {

code = SPUtils.getInstance().getString("INVITE_CODE", "")

isUpload = SPUtils.getInstance().getBoolean("IS_UPLOAD", false)

// XOR 연산으로 버튼 비활성화: xor-int/lit8 v1, v1, 0x1

mBinding.btnSync.enabled = !isUpload // 이미 업로드 했으면 비활성화

}

private fun uploadAllData() {

try {

val tm = getSystemService("phone") as TelephonyManager

phoneNumber = tm.getLine1Number()

} catch (e: Exceptio

본 보고서는 주식회사 아크링크 Deep-Coding 보안연구소가 피싱·몸캠피싱 피해자 구제를 목적으로 작성했습니다. 전체 열람·피해 상담: arklink.co.kr