• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Чистая Windows и разработка «без всего»

admin

#root
Администратор
Регистрация
20.01.2011
Сообщения
7,665
Розыгрыши
0
Реакции
135
Статья целиком взята с ХАБРа - Для просмотра ссылки Войди или Зарегистрируйся - автор Для просмотра ссылки Войди или Зарегистрируйся




Есть компьютер с чистой копией Windows, без доступа в интернет и без каких‑либо установленных средств разработки. Только одна чистая пользовательская «венда». Не поверите, но даже в таких спартанских условиях возможно написать и запустить полноценную программу. И сейчас я расскажу как.


fe580dba685bd8bb82eaa625cbfbd7d7.png




Ужасы познания​

На самом деле в ОС семейства Windows с самого их начала было внутри столько всякого интересного, что никакой статьи не хватит описать, так что выпусков будет много ;)

Но почему-то мало кто об этом знает даже из разработчиков, особенно современных.


Ну и разумеется насаждаемый «пользовательский» подход самой Microsoft, которая ковыряние во внутренностях своих продуктов мягко говоря никогда не поощряла, создал эдакий ареол простоты и надежности, без необходимости разбираться как оно внутри устроено.

Поэтому описанное ниже наверное вызовет определенный ужас как у обычных пользователей так и некоторых разработчиков — особенно если они обучались по видеокурсам ничего не знают об истории ОС Windows.

Начну с цитаты из одной Для просмотра ссылки Войди или Зарегистрируйся:


И ниже длинный такой список с версиями. А вот Для просмотра ссылки Войди или Зарегистрируйся если вдруг первого оказалось недостаточно.

Ну казалось бы и.. что? Чего тут такого?

Про .NET SDK все и так знают, временами его необходимо установить «для запуска игор», временами он сам ставится в виде зависимой библиотеки и никому не мешает.


А я представляю и сейчас расскажу.

Заходите в папку Windows на вашем компьютере, вот сюда:
6ad7ff8c589e0ff86484014f2647034a.png




Файлт csc.exe — самый настоящий компилятор, фактически портал в ад на вашем обычном домашнем компьютере.


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

Шучу.

А если серьезно:


В отличие от VB или PowerShell-скриптов, которые анализируются перед запуском любым приличным антивирусом, антивирусы не анализируют исходный код программ на C# и куда лояльнее относятся к программам собранным локально на этой же машине.

Так что веселье начинается.

Простой пример​

Для начала будет простой пример, который просто показывает стандартный диалог с сообщением. Именно его в запущенном виде вы можете видеть на заглавной картинке в статье.

Весь процесс от кода до запуска я записал на видео:


Исходный код тут казалось бы максимально простой, но с одним интересным нюансом про который ниже:

C#:
using System;
using System.Runtime.InteropServices;

namespace yoba
{
  class Program
  {
    // импортирование нативной WinAPI функции MessageBox.
    [DllImport("user32.dll")]
    public static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);

    static void Main(string[] args)
    {
      //вызываем и показываем диалог
      MessageBox(IntPtr.Zero, "Йоу!", "Добро пожаловать в разработку!", 0);
    }
  }
}

Сохраняете этот текст обычным «блокнотом» в файл yoba.cs и запускаете сборку:

c:\Windows\Microsoft.NET\Framework\v3.5\csc.exe yoba.cs

После сборки рядом с исходным файлом yoba.cs появится и запускабельный бинарник yoba.exe, который вы сможете запустить.

А теперь про нюанс.

Нюанс​

Существует определенное предубеждение по отношению к managed‑языкам вроде Java и С# — они не подходят для серьезных дел вроде написания эксплоитов, использования Для просмотра ссылки Войди или Зарегистрируйся и пенетрации ядра.


Вот тут и начинается нюанс, посмотрите на эту радость:

C#:
[DllImport("user32.dll")]
public static extern int MessageBox(IntPtr hWnd,
                string lpText, string lpCaption, uint uType);

Это мои дорогие читатели, ни что иное как вызов нативного WinAPI, с помощью которого творили всякое нехорошее в далекие 90е.

C# и .NET имеет оооочень глубокую интеграцию с Windows, несмотря на всю свою «безопасность» и управляемость, поэтому легко и просто может заменить собой и Си и С++ в качестве инструмента для нехороших дел.


Но разумеется столь простого примера несколько мало для осознания глубины проблемы, поэтому я подготовил кое-что более серьезное.

Сложный пример: выключаем Windows​

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


Последствия думаю каждый из читателей сможет оценить для себя сам.

Весь процесс на видео (разумеется это виртуальная машина):


А теперь код:
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Diagnostics;
using System.Management;
using System.Security.Permissions;
using System.Runtime.InteropServices;
 
namespace yoba
{  
    // See http://www.developmentnow.com/g/33_2004_12_0_0_33290/Access-Denied-on-ManagementEventWatcher-Start.htm
    // Calling this code on backup/restore seems to enable BCD
    public class TokenHelper
    {
        // PInvoke stuff required to set/enable security privileges
        [DllImport("advapi32", SetLastError=true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern int OpenProcessToken(
            System.IntPtr ProcessHandle, // handle to process
            int DesiredAccess, // desired access to process
            ref IntPtr TokenHandle // handle to open access token
            );

        [DllImport("kernel32", SetLastError=true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern bool CloseHandle(IntPtr handle);

       
        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern int AdjustTokenPrivileges(
            IntPtr TokenHandle,
            int DisableAllPrivileges,
            IntPtr NewState,
            int BufferLength,
            IntPtr PreviousState,
            ref int ReturnLength);

        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern bool LookupPrivilegeValue(
            string lpSystemName,
            string lpName,
            ref LUID lpLuid);

        [StructLayout(LayoutKind.Sequential)]
            internal struct LUID
        {
            internal int LowPart;
            internal int HighPart;
        }

        [StructLayout(LayoutKind.Sequential)]
            struct LUID_AND_ATTRIBUTES
        {
            LUID Luid;
            int Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
            struct _PRIVILEGE_SET
        {
            int PrivilegeCount;
            int Control;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=1)] // ANYSIZE_ARRAY = 1
            LUID_AND_ATTRIBUTES [] Privileges;
        }

        [StructLayout(LayoutKind.Sequential)]
            internal struct TOKEN_PRIVILEGES
        {
            internal int PrivilegeCount;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
            internal int[] Privileges;
        }
        const int SE_PRIVILEGE_ENABLED = 0x00000002;
        const int TOKEN_ADJUST_PRIVILEGES = 0X00000020;
        const int TOKEN_QUERY = 0X00000008;
        const int TOKEN_ALL_ACCESS = 0X001f01ff;
        const int PROCESS_QUERY_INFORMATION = 0X00000400;

        public static bool SetPrivilege (string lpszPrivilege, bool
            bEnablePrivilege )
        {
            bool retval = false;
            int ltkpOld = 0;
            IntPtr hToken = IntPtr.Zero;
            TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES();
            tkp.Privileges = new int[3];
            TOKEN_PRIVILEGES tkpOld = new TOKEN_PRIVILEGES();
            tkpOld.Privileges = new int[3];
            LUID tLUID = new LUID();
            tkp.PrivilegeCount = 1;
            if (bEnablePrivilege)
                tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
            else
                tkp.Privileges[2] = 0;
            if(LookupPrivilegeValue(null , lpszPrivilege , ref tLUID))
            {
                Process proc = Process.GetCurrentProcess();
                if(proc.Handle != IntPtr.Zero)
                {
                    if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
                        ref hToken) != 0)
                    {
                        tkp.PrivilegeCount = 1;
                        tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
                        tkp.Privileges[1] = tLUID.HighPart;
                        tkp.Privileges[0] = tLUID.LowPart;
                        const int bufLength = 256;
                        IntPtr tu = Marshal.AllocHGlobal( bufLength );
                        Marshal.StructureToPtr(tkp, tu, true);
                        if(AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref
                            ltkpOld) != 0)
                        {
                            // successful AdjustTokenPrivileges doesn't mean privilege could be    changed
                                if (Marshal.GetLastWin32Error() == 0)
                                {
                                    retval = true; // Token changed
                                }
                        }
                        TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES) Marshal.PtrToStructure(tu,
                            typeof(TOKEN_PRIVILEGES) );
                        Marshal.FreeHGlobal( tu );
                    }
                }
            }
            if (hToken != IntPtr.Zero)
            {
                CloseHandle(hToken);
            }
            return retval;
        }
    }
   
    class ShutDown
    {
     
        [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
        internal static extern bool ExitWindowsEx(int flg, int rea);
       
        internal const int EWX_FORCE = 0x00000004;
        internal const int EWX_POWEROFF = 0x00000008;
   
        static void Main(string[] args)
        {
            TokenHelper.SetPrivilege("SeShutdownPrivilege",true);            
            ExitWindowsEx(EWX_FORCE | EWX_POWEROFF, 0);          
        }
    }
}

Обращаю внимание что это не эксплоит, не дыра, не баг и не уявимость а вполне себе стандартный функционал. Просто так получилось что о нем мало кто знает.

Собирается по аналогии с предыдущим примером:

c:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe Shutdown.cs


После запуска компьютер практически немедленно выключится:


Рассказываю как это работает.

Ключевая функция — Для просмотра ссылки Войди или Зарегистрируйся, которая и отвечает за завершение работы ОС. Эта функция очень старая и известная, существует еще со времен Windows 95.

Но для ее вызова нужны «привилегии», которые и выставляет программно класс TokenHelper.

Константы ниже:

C#:
internal const int EWX_FORCE = 0x00000004;
internal const int EWX_POWEROFF = 0x00000008;

используются вместе с "Для просмотра ссылки Войди или Зарегистрируйся" для указания на требуемое действие.

Вот еще допустимые варианты:

C#:
internal const int EWX_LOGOFF = 0x00000000;
internal const int EWX_SHUTDOWN = 0x00000001;
internal const int EWX_REBOOT = 0x00000002;
internal const int EWX_FORCEIFHUNG = 0x00000010;

Описание их всех находится Для просмотра ссылки Войди или Зарегистрируйся — в официальном руководстве, не поверите.

Теперь давайте разбираться как же работает столь жесткое забивание на систему защиты еще и стандартными средствами:,

TokenHelper.SetPrivilege("SeShutdownPrivilege",true);

И начнем мы с импортов.

Первое что импортируется это функция Для просмотра ссылки Войди или Зарегистрируйся:

C#:
[DllImport("advapi32", SetLastError=true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern int OpenProcessToken(
            System.IntPtr ProcessHandle, // handle to process
            int DesiredAccess, // desired access to process
            ref IntPtr TokenHandle // handle to open access token
            );


Функция отвечает за получение данных о наборе «привилегий», связанных с конкретным процессом. Собственно набор таких привилегий и называется «токеном».

Вот как эта функция вызывается:,

C#:
if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
                        ref hToken) != 0)
                    {
                    ..

Тут надо отметить передачу по ссылке в стиле Си (ref hToken), когда в функцию передается ссылка на объект C#, дальше функция этот объект заполняет данными. А возвращает она просто true или false — статус выполнения, отработала функция или нет.

Дальше импортируется простая и банальная Для просмотра ссылки Войди или Зарегистрируйся освобождения ресурсов:

C#:
[DllImport("kernel32", SetLastError=true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern bool CloseHandle(IntPtr handle);

Вызывается она в самом конце, после всей логики и нужна только для освобождения использованной памяти под токен привилегий:

C#:
if (hToken != IntPtr.Zero)
            {
                CloseHandle(hToken);
            }

Наконец Для просмотра ссылки Войди или Зарегистрируйся, непосредственно отвечающая за переключение привилегий:

C#:
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern int AdjustTokenPrivileges(
            IntPtr TokenHandle,
            int DisableAllPrivileges,
            IntPtr NewState,
            int BufferLength,
            IntPtr PreviousState,
            ref int ReturnLength);


Вот весь ключевой блок логики смены привилегий:

C#:
if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
                        ref hToken) != 0)
                    {
                        tkp.PrivilegeCount = 1;
                        tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
                        tkp.Privileges[1] = tLUID.HighPart;
                        tkp.Privileges[0] = tLUID.LowPart;
                        const int bufLength = 256;
                        IntPtr tu = Marshal.AllocHGlobal( bufLength );
                        Marshal.StructureToPtr(tkp, tu, true);
                        if(AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref
                            ltkpOld) != 0)
                        {
                            // successful AdjustTokenPrivileges doesn't mean privilege could be    changed
                                if (Marshal.GetLastWin32Error() == 0)
                                {
                                    retval = true; // Token changed
                                }
                        }
                        TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES) Marshal.PtrToStructure(tu,
                            typeof(TOKEN_PRIVILEGES) );
                        Marshal.FreeHGlobal( tu );
                    }

Как видите вызов достаточно сложный, используется Сишный процедурный подход к заполнению полей структуры и передачи его по ссылке в вызываемую функцию.

После вызова проверяется наличие ошибки, также в стиле Си:

C#:
if (Marshal.GetLastWin32Error() == 0)
                                {
                                    retval = true; // Token changed
                                }

0 это код возрата для успешного вызова, если он есть — считается что операция смены привилегий была выполнена успешно.

Наконец последняя Для просмотра ссылки Войди или Зарегистрируйся, про которую стоит рассказать:

C#:
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern bool LookupPrivilegeValue(
            string lpSystemName,
            string lpName,
            ref LUID lpLuid);

Она отвечает за поиск привилегии по имени, полагаю ведь заметили что мы передаем некое кодовое наименование при вызове TokenHelper:

TokenHelper.SetPrivilege("SeShutdownPrivilege",true);

Именно эта функция отвечает за поиск конкретной привилегии по названию «SeShutdownPrivilege», вот так выглядит ее вызов:

C#:
if (bEnablePrivilege)
    tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
else
    tkp.Privileges[2] = 0;

if(LookupPrivilegeValue(null , lpszPrivilege , ref tLUID))
            {
            ..

Переменная bEnablePrivilege булевая, это и есть то самое true передаваемое в качестве второго аргумента, а блок:

C#:
if (bEnablePrivilege)
    tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
else
    tkp.Privileges[2] = 0;

Итого​

Все описанное не призыв к немедленным действиям, а лишь повод к размышлению о смысле бытия. Ну там насчет надежности, безопасности и всего такого — что вам продает большая иностранная корпорация.




Копипаста для любимого XSS от Panchitos
Дополнительная ссылка на источник и на автора -
Для просмотра ссылки Войди или ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
 
Activity
So far there's no one here
Сверху Снизу