Как стать автором
Обновить
103
0
Евгений Гречников @grechnik

Пользователь

Отправить сообщение

Очевидно, что LoadLibrary слишком «громкий» способ внедрения в другой процесс для антивирусного ПО. Подозрительный процесс, вирусный или заражённый вирусом, может воспрепятствовать внедрению в себя со стороны подобным способом одним из множества способов.

Вирусный процесс может запросто и от CreateRemoteThread защищаться (каждый новый поток, в том числе созданный удалённо, вызывает TLS callbacks и DllMain всех dll-ек, пока адрес грядущей передачи управления лежит себе на стеке), и каждые пять секунд сканировать всю свою память на предмет executable-страниц вне dll-ек (палевно, системные библиотеки себе такого не позволяют), и каждую секунду перечислять список потоков в своём процессе... Единственный выигрышный ход — не играть в эту игру и вообще не запускать свой код в контексте чужого процесса, всё остальное — противостояние щита и меча.

UPD: с VS2019 забавнее, для конкретно этого примера /Gy- переупорядочивает в порядке первого объявления, а не реализации. Соответственно, если мы, как добропорядочные сишники, вынесли внешние объявления в отдельный inject.h (чтобы не нарваться на ситуацию "реализацию поменяли, объявления в других файлах .c поменять забыли")

void RemoteThreadProc(void);
void EndOfRemoteThreadProc(void);

в inject.c поместили реализации всех вспомогательных функций между этими двумя

#include <stdio.h>
#include "inject.h"

void DoSomethingUsefulButNotInlinable(int n);

void RemoteThreadProc(void) {
	printf("RemoteThreadProc\n");
	DoSomethingUsefulButNotInlinable(42);
}
void DoSomethingUsefulButNotInlinable(int n) {
	if (n) DoSomethingUsefulButNotInlinable(n - 1);
	printf("DoSomethingUsefulButNotInlinable %d\n", n);
}
void EndOfRemoteThreadProc(void) {
	printf("EndOfRemoteThreadProc\n");
}

и рассчитываем, что в бинарнике они будут идти в таком же порядке и можно смело memcpy-ить всё это куда угодно... нас может спасти только инлайнинг всего подряд, потому что с /Gy- порядок этих функций будет RemoteThreadProc, EndOfRemoteThreadProc, DoSomethingUsefulButNotInlinable, а без него DoSomethingUsefulButNotInlinable, EndOfRemoteThreadProc, RemoteThreadProc (вряд ли стоит копипастить сюда третий ассемблерный листинг подряд только чтобы проиллюстрировать порядок; я бы дал ссылку на godbolt.org, но там управляющая система, судя по всему, не дизассемблирует бинарник, а просит листинг у компилятора через /Fa и не исключено, что ещё и переупорядочивает его потом).

С ключом /Gy- это ещё и от версии компилятора зависит. Вот, например, компилятор из Visual Studio 2010:

C:\temp\vs2010>cl /c /O2 /Gy- /I"E:\programs\compilers\Microsoft Visual Studio 10.0\VC\include" C:\temp\inject.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

inject.c

C:\temp\vs2010>dumpbin /disasm inject.obj
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file inject.obj

File Type: COFF OBJECT

_DoSomethingUseful:
  00000000: 68 00 00 00 00     push        offset ??_C@_0BD@GJCDPNPO@DoSomethingUseful?6?$AA@
  00000005: E8 00 00 00 00     call        _printf
  0000000A: 59                 pop         ecx
  0000000B: C3                 ret
  0000000C: CC                 int         3
  0000000D: CC                 int         3
  0000000E: CC                 int         3
  0000000F: CC                 int         3
_EndOfRemoteThreadProc:
  00000010: 68 00 00 00 00     push        offset ??_C@_0BH@IKBAAHGF@EndOfRemoteThreadProc?6?$AA@
  00000015: E8 00 00 00 00     call        _printf
  0000001A: 59                 pop         ecx
  0000001B: C3                 ret
  0000001C: CC                 int         3
  0000001D: CC                 int         3
  0000001E: CC                 int         3
  0000001F: CC                 int         3
_RemoteThreadProc:
  00000020: 68 00 00 00 00     push        offset ??_C@_0BC@EGMKFOBF@RemoteThreadProc?6?$AA@
  00000025: E8 00 00 00 00     call        _printf
  0000002A: 68 00 00 00 00     push        offset ??_C@_0BD@GJCDPNPO@DoSomethingUseful?6?$AA@
  0000002F: E8 00 00 00 00     call        _printf
  00000034: 83 C4 08           add         esp,8
  00000037: C3                 ret

  Summary

          30 .debug$F
          6C .debug$S
          2F .drectve
          3C .rdata
          38 .text

(тут, кстати, видно, что printf ещё была обычной внешней функцией). В VS2019 для конкретно этого примера ключ /Gy- дополнительно переупорядочивает функции в более ожидаемом порядке. Для более сложных примеров уже детально ковыряться надо (и перспектива получить ответ "ну конечно же никто не будет в столь системном коде столь творчески инстанцировать плюсовые шаблоны" не особенно вдохновляет), мне лень.

Как скажете:

C:\temp>dumpbin /disasm inject.obj
Microsoft (R) COFF/PE Dumper Version 14.29.30138.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file inject.obj

File Type: COFF OBJECT

_DoSomethingUseful:
  00000000: 68 00 00 00 00     push        offset ??_C@_0BD@GJCDPNPO@DoSomethingUseful?6@
  00000005: E8 00 00 00 00     call        _printf
  0000000A: 59                 pop         ecx
  0000000B: C3                 ret

_EndOfRemoteThreadProc:
  00000000: 68 00 00 00 00     push        offset ??_C@_0BH@IKBAAHGF@EndOfRemoteThreadProc?6@
  00000005: E8 00 00 00 00     call        _printf
  0000000A: 59                 pop         ecx
  0000000B: C3                 ret

_RemoteThreadProc:
  00000000: 68 00 00 00 00     push        offset ??_C@_0BC@EGMKFOBF@RemoteThreadProc?6@
  00000005: E8 00 00 00 00     call        _printf
  0000000A: 68 00 00 00 00     push        offset ??_C@_0BD@GJCDPNPO@DoSomethingUseful?6@
  0000000F: E8 00 00 00 00     call        _printf
  00000014: 83 C4 08           add         esp,8
  00000017: C3                 ret

___local_stdio_printf_options:
  00000000: B8 00 00 00 00     mov         eax,offset ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9
  00000005: C3                 ret

__vfprintf_l:
  00000000: FF 74 24 10        push        dword ptr [esp+10h]
  00000004: FF 74 24 10        push        dword ptr [esp+10h]
  00000008: FF 74 24 10        push        dword ptr [esp+10h]
  0000000C: FF 74 24 10        push        dword ptr [esp+10h]
  00000010: E8 00 00 00 00     call        ___local_stdio_printf_options
  00000015: FF 70 04           push        dword ptr [eax+4]
  00000018: FF 30              push        dword ptr [eax]
  0000001A: E8 00 00 00 00     call        ___stdio_common_vfprintf
  0000001F: 83 C4 18           add         esp,18h
  00000022: C3                 ret

_printf:
  00000000: 56                 push        esi
  00000001: 8B 74 24 08        mov         esi,dword ptr [esp+8]
  00000005: 6A 01              push        1
  00000007: E8 00 00 00 00     call        ___acrt_iob_func
  0000000C: 83 C4 04           add         esp,4
  0000000F: 8D 4C 24 0C        lea         ecx,[esp+0Ch]
  00000013: 51                 push        ecx
  00000014: 6A 00              push        0
  00000016: 56                 push        esi
  00000017: 50                 push        eax
  00000018: E8 00 00 00 00     call        ___local_stdio_printf_options
  0000001D: FF 70 04           push        dword ptr [eax+4]
  00000020: FF 30              push        dword ptr [eax]
  00000022: E8 00 00 00 00     call        ___stdio_common_vfprintf
  00000027: 83 C4 18           add         esp,18h
  0000002A: 5E                 pop         esi
  0000002B: C3                 ret

  Summary

          90 .chks64
          60 .debug$F
          64 .debug$S
          2F .drectve
          3C .rdata
          85 .text$mn

Есть такая гарантия: компилятор генерирует сущности машинного кода в строгом соответствии с тем, кем они фигурировали в исходном файле.

Да ну?

C:\temp>type main.c
#include <stdio.h>

extern void RemoteThreadProc(void);
extern void EndOfRemoteThreadProc(void);

int main()
{
        printf("%p %p\n", &RemoteThreadProc, &EndOfRemoteThreadProc);
        return 0;
}

C:\temp>type inject.c
#include <stdio.h>

void RemoteThreadProc(void);
void DoSomethingUseful(void);
void EndOfRemoteThreadProc(void);

void RemoteThreadProc(void) {
        printf("RemoteThreadProc\n");
        DoSomethingUseful();
}
void DoSomethingUseful(void) {
        printf("DoSomethingUseful\n");
}
void EndOfRemoteThreadProc(void) {
        printf("EndOfRemoteThreadProc\n");
}
C:\temp>cl /O2 main.c inject.c
Оптимизирующий компилятор Microsoft (R) C/C++ версии 19.29.30138 для x86
(C) Корпорация Майкрософт (Microsoft Corporation).  Все права защищены.

main.c
inject.c
Создание кода...
Microsoft (R) Incremental Linker Version 14.29.30138.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:main.exe
main.obj
inject.obj

C:\temp>main.exe
00901070 00901060
> не помню название…
AntiWPA, скорее всего.
На MDL говорят, что телефонная активация кейгенится без патчей forums.mydigitallife.net/threads/is-there-any-way-to-crack-decrypt-the-winxp-consumer-activation-system-to-generate-activation-ids.80133/page-2#post-1640340, но готового кейгена не выкладывают.
Сливали SP1. Это кодовая база ещё до пресловутого W32/Blaster с компанией и, соответственно, до субботника месячника годичника безопасности в MS, после которого SP3 уже местами сильно другой продукт.
internaldev в качестве пароля лучше подходит © boards.4channel.org/g/thread/77980953. И там не рандом, там перелопаченные файлы из EVE Online.
Balling в вашей статье тоже пароль лучше поменять
Потому что именно при размере 20 проявляется заметная глазу структура на гифке из статьи.
При размере строки 6:
...
(1800) 110111
(1806) 100001
(1812) 001011
(1818) 110000
(1824) 011000
(1830) 010010
(1836) 111100
(1842) 000110
(1848) 001001
(1856) 110001
...

Какая-то мешанина нулей и единиц.
При размере строки 20:
...
(1800) 11011110000100101111
(1820) 00000110000100101111
(1840) 00000110001001110001
(1860) 00000110000100101111
(1880) 00000110000100101111
(1900) 00000110000100101111
(1920) 00000110001010110011
(1940) 00000110000100101111
(1960) 00000110000100101111
(1980) 00000110000100101111
...

Уже явно какая-то регулярная структура.
После окончания CTF организаторы выложили исходники: github.com/koriakin/cpuadventure. Да, эмулятор.
Кроме того, наверняка есть более эффективные алгоритмы решения этой задачи, но представленный результат непродолжительных раздумий тоже сойдёт для этого небольшого воскресного приключения.

Конечно, есть. Сумма делителей мультипликативна в смысле теории чисел: если n=ab и a,b взаимно просты, то сумма делителей n равна произведению сумм делителей для a и b. Так что достаточно посчитать суммы для степеней простых и перемножить:
    std::vector<int64_t> arr;
    arr.resize(n + 1, 1);

    int64_t p;
    for (p = 2; p * p <= n; p++) if (arr[p] == 1) {
        int64_t ppow = p;
        int64_t sum = 1;
        do {
            sum += ppow;
            int64_t q, residue = 1;
            for (int64_t q = ppow; q <= n; q += ppow, residue++)
                if (residue == p)
                    residue = 0;
                else
                    arr[q] *= sum;
            ppow *= p; // no overflow here if n**(3/2) < 2**63; we get std::bad_alloc long before this otherwise
        } while (ppow <= n);
    }
    for (; p <= n; p++) if (arr[p] == 1) {
        for (int64_t q = p; q <= n; q += p)
            arr[q] *= 1 + p;
    }
    std::cout << arr.back() << std::endl;

Получается быстрее в 2-3 раза.
Просто перехватывать SEH-исключения на С и C++ можно было ещё с момента появления этих самых SEH-исключений в Windows 95, через синтаксис __try / __except / __finally.
Какие ещё «амбиции компиляторщиков»? В VC++ есть преобразование SEH -> C++ exception: _set_se_translator.
В Word просто пошли по другому пути: не делать сложных манипуляций при access violation — тем более, что если AV вообще произошло, то в данных вполне может быть каша, а получить документ с кашей намного неприятнее, чем потерять последний сеанс работы — а периодически делать временную копию, пока данные гарантированно целы, при AV вылетать, а после перезапуска предлагать её восстановить.
Отдельного внимания заслуживает использование регистра rdi. Вызывающий код обнуляет edi – половину rdi, а вызываемый код ДОВОЛЬНО НЕОЖИДАННО – использует наполовину обнуленный rdi.

Инструкция xor edi,edi в 64-битном режиме обнуляет весь rdi, так что нет никакого «наполовину обнулённого» регистра.
В проекте он, конечно, описан, но ведь документацию читают только ламеры ©
Есть ограниченное логирование всех системных вызовов штатными средствами: флаг EVENT_TRACE_FLAG_SYSTEMCALL в https://msdn.microsoft.com/en-us/library/windows/desktop/aa363784(v=vs.85).aspx. «Ограниченное» значит «только адреса вызываемых функций и возвращаемые значения, без аргументов».
В сыром виде выглядит примерно так:
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039A5E30
[2]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF96000127A6C
[2]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = NTSTATUS=      50
[2]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039F3090
[2]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [Thread] Context Switch from 4836. to 0.
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = 0xc0000034(STATUS_OBJECT_NAME_NOT_FOUND)
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039C6430
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = STATUS_SUCCESS
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039A5E30
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = 0xc0000034(STATUS_OBJECT_NAME_NOT_FOUND)
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039A7190
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = STATUS_SUCCESS
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF80003990C40
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = STATUS_SUCCESS
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039C6430
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = STATUS_SUCCESS
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039A5E30
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = STATUS_SUCCESS
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039A5E30
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = 0xc0000034(STATUS_OBJECT_NAME_NOT_FOUND)
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039A5E30
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = STATUS_SUCCESS
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Enter SysCallAddress=0xFFFFF800039ACAD0
[0]FFFFFFFF.FFFFFFFF::08/02/2016-21:25:37.851 [PerfInfoEvent]System Call Exit NtStatus = 0x8000001a(STATUS_NO_MORE_ENTRIES)
Конкретно это — выдача tracelog -start -eflag 1 0x90 && [пауза] && tracelog -stop && tracefmt -tmf system.tmf LogFile.Etl в формате [CPU]PID.TID::time event. PID/TID, впрочем, придётся отслеживать руками по событиям Context Switch.
«Всякие принтеры» и в винде могут работать в юзерспейсе через UMDF. Когда я последний раз проверял, UMDF-драйверы принудительно подписывать было не нужно.
В ...\Image File Execution Options\<filename>.exe\ можно создать значение "UseFilter"=dword:00000001 и произвольное количество подключей с произвольными именами, внутри подключей — значение "FilterFullPath"="C:\<полный путь к filename>.exe", тогда настройки будут браться из подключа с FilterFullPath, совпавшим с полным путём (если такого не нашлось, то, как и раньше, из самого IFEO\<filename>.exe\).
Если же входил, то ответом для данного i будет k – 1 + gi, где k — порядковый номер данного друга в оптимальном ответе.
Это же неправда. Контрпример: Малыш живёт на этаже 1000, рост Карлсона 1 (чтобы не заморачиваться с вопросами инициализации), на первом этаже друг роста 4, на втором — друг роста 998, на третьем — друг роста 1000, на четвёртом — друг роста 999, на каждом следующем этаже m — друг роста m+1. Оптимальная стратегия, очевидно, — две пересадки на первом и на третьем этажах. Если же поссориться с другом на третьем этаже, то g3=3 (если доступны только два первых этажа, придётся просить друзей на 2,998,999 этажах), k-1+g3=4, но Карлсон вполне может добраться за три пересадки, этажи 1,4,999.
Разная семантика inline. Программа из двух файлов. test1.c:
inline void f() {}
int main() { f(); return 0; }

test2.c:
void f() {}

Валидно в C, ошибка линковки в C++. Если второй файл убрать, то валидно в C++, ошибка линковки в C (точнее, программа не соответствует стандарту, и может быть ошибка линковки в зависимости от настроения компилятора — будет с gcc -O0, не будет с gcc -O2).
На Википедии есть пример с sizeof('x').
Молчаливое преобразование к/из void*, как выше уже привели пример, отнюдь не ограничивается «ручным» выделением памяти.
Ну а если смотреть на новые стандарты, то они вообще по-разному развиваются: _Bool вместо bool, float _Complex, всякие
#define mysqrt(X) _Generic((X), long double: sqrtl, default: sqrt, float: sqrtf)

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Работает в
Дата рождения
Зарегистрирован
Активность