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

  • Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Вопрос к реверсерам или к Гуру подсистем win32 win64 (NTVDM|win16)

ProMiNick

One Level
20.03.2020
4
2
BIT
0
Немножко контекста проблемы:
Решил я пописать несколько программок под win16, написал потестил - на 32битных Windows запускаются, на 64битных естественно (а так ли это естественно) нет - падают с ошибкой, мол они валидные, но для другой архитектуры(машины).
В общем натолкнулся на исполнимый файл формата NE-executable для подсистемы win16, самое удивительное в отличие от всех других NE файлов в 64битном окружении он запускался.
Этот экзотический файл acmboot.exe был взят из embedded Visual C++ 4.0. (Это просто файл загрузчик, он наверняка есть и в других дистрибутевах MS от того времени)
73кб - не самый удобный размер, поэтому было решено пойти по следующему алгоритму:
1. минимизировать логику программы с сохранением возможности запуска в 64разрядном окружении, а для этого
1.а. исключить из исполнения ЯВУ инициализации (их 3 последовательных вызова cinit, setargv, setenvp)
1.б. оставить в качестве полезной логики только вывод сообщения (благо такой кусочек кода, и при том очень удобный есть, он конечно использует глобальные переменные, но есть другой кусок где эти переменные загружаются из ресурса таблицы строк, и то и другое не требует ничего что было отсечено на шаге 1.а.)
2. залить неиспользуемые участки кода значением -1 (в случае PE файлов можно 0 заливать, но в случае NE формата релокации формируются в цепочки, а маркер конечного звена таких цепочек -1) - это будет гарантировать, что код который мы явно подразумеваем как явно не используемый не будет как-нибудь использован неявно - иначе после такого патча программа вылетит и это будет свидетельствовать о том что проблему нужно копать глубже
3. убрать ненужные релокации туда где все залито минус единицами
4. Сжать файл сократив размеры секций за счет неиспользуемого кода на этапах 2 и за счет сокращения размеров релокаций этапа 3, естественно сжимать не бездумно - править содержимое NE структуры, так чтоб ссылки на начала секций указывали на их новые начала, править их размеры, все прочие ссылки на начала других таблиц также править.
Алгоритм показался простым - планировалось сжатие подопытного до 3-10кб (я причислял возможность запускаться в 64битной среде к "правильному" содержимому NE заголовка и, возможно, какому то особенному содержимому других вспомогательных таблиц, на которые этот заголовок ссылается.

В общем как патчил
Код:
cseg01:001A start           proc near
cseg01:001A
cseg01:001A ; FUNCTION CHUNK AT cseg01:0012 SIZE 00000008 BYTES
cseg01:001A ; FUNCTION CHUNK AT cseg01:1BC8 SIZE 00000039 BYTES
cseg01:001A
cseg01:001A                 xor     bp, bp
cseg01:001C                 push    bp
cseg01:001D                 call    INITTASK
cseg01:0022                 or      ax, ax
cseg01:0024                 jz      short _error
cseg01:0026                 mov     word_CA58, es
cseg01:002A                 add     cx, 256
cseg01:002E                 jb      short _error
cseg01:0030                 mov     StackSize, cx
cseg01:0034                 mov     hPrevInstance, si
cseg01:0038                 mov     hInstance_0, di
cseg01:003C                 mov     word ptr lpszCmd, bx
cseg01:0040                 mov     word ptr lpszCmd+2, es
cseg01:0044                 mov     cmdShow, dx
cseg01:0048                 mov     ax, -1
cseg01:004B                 push    ax              ; UINT
cseg01:004C                 call    LOCKSEGMENT
cseg01:0051                 call    GETVERSION
cseg01:0056                 xchg    al, ah
cseg01:0058                 mov     version, ax
cseg01:005B
cseg01:005B loc_5B:
cseg01:005B                 mov     ah, DOS_GET_DOS_VERSION
cseg01:005D
cseg01:005D loc_5D:
cseg01:005D                 test    cs:lpWinFlags, WF_PMODE
cseg01:0064                 jz      short loc_6D
cseg01:0066                 call    DOS3CALL
cseg01:006B                 jmp     short loc_6F
cseg01:006D ; ---------------------------------------------------------------------------
cseg01:006D
cseg01:006D loc_6D:                                 ; CODE XREF: start+4Aj
cseg01:006D                 int     21h             ; DOS -
cseg01:006F
cseg01:006F loc_6F:                                 ; CODE XREF: start+51j
cseg01:006F                 mov     DOSversion, ax
cseg01:0072                 xchg    al, ah
cseg01:0074                 mov     DOSversionInverted, ax
cseg01:0077                 test    cs:lpWinFlags, WF_PMODE
cseg01:007E                 jnz     short loc_85
cseg01:0080                 mov     al, FALSE
cseg01:0082                 mov     PROTECTED_MODE, al
cseg01:0085
cseg01:0085 loc_85:                                 ; CODE XREF: start+64j
cseg01:0085                 xor     ax, ax
cseg01:0087                 push    ax
cseg01:0088                 call    WAITEVENT
cseg01:008D                 push    hInstance_0
cseg01:0091                 call    INITAPP
cseg01:0096                 or      ax, ax
cseg01:0098                 jnz     short loc_9D ; идеальное место для патча надо прыжок не в loc_9D, а в  loc_AC
cseg01:009A                 jmp     _error
cseg01:009D ; ---------------------------------------------------------------------------
cseg01:009D
cseg01:009D loc_9D:                                 ; CODE XREF: start+7Ej
cseg01:009D                 call    __cinit
cseg01:00A2                 call    far ptr __setargv
cseg01:00A7                 call    __setenvp
cseg01:00AC                 nop
cseg01:00AD                 nop
cseg01:00AE                 nop
cseg01:00AF                 push    word_CA9A       ; int
cseg01:00B3                 push    word_CA98       ; int
cseg01:00B7                 push    word_CA96       ; int
cseg01:00BB                 push    word_CA94       ; int
cseg01:00BF                 push    word_CA92       ; int
cseg01:00C3                 call    __stubmain
cseg01:00C8                 add     sp, 0Ah
cseg01:00CB                 push    ax              ; int
cseg01:00CC                 call    _exit
cseg01:00CC start           endp ; sp-analysis failed
сказвно сделано(хекс редактор и...):
Код:
cseg01:0098                 jnz     short loc_AC
cseg01:009A                 jmp     loc_12
cseg01:009A ; ---------------------------------------------------------------------------
cseg01:009D                 db 0Dh dup(0FFh) ;блок кода вызывающий ЯВУ инициализации сразу затер минус единицами FF
cseg01:00AA                 dw seg cseg01     ; на место одной затертости села релокация, но это не сделало файл невалидным -1 то это окончание цепи релоков
cseg01:00AC ; ---------------------------------------------------------------------------
cseg01:00AC
cseg01:00AC loc_AC:                                 ; CODE XREF: start+7Ej

__stubmain не делает ничего полезного, только вызывает WINMAIN, параметры самого __stubmain никак не используются, поэтому нет ничего страшного, что убит код где они инициализируются.
конечно и у __stubmain и у WINMAIN по 5 параметров и все они берутся из секции данных можно было бы перебить релоки и параметров и вызова так чтоб вместо __stubmain сразу вызывался WINMAIN. Но проще использовать пока как есть вызывать__stubmain, а он вызывает WINMAIN.

перед WINMAIN есть вкусный участок кода:
Код:
cseg01:8B31 ; ---------------------------------------------------------------------------
cseg01:8B31
cseg01:8B31 loc_8B31:                               ; CODE XREF: ShowErrorMessage+34j
cseg01:8B31                                         ; ShowErrorMessage+51j
cseg01:8B31                 push    hwnd            ; hWnd
cseg01:8B35                 push    ds
cseg01:8B36                 push    offset Text     ; lpText
cseg01:8B39                 push    ds
cseg01:8B3A                 push    offset Caption  ; lpCaption
cseg01:8B3D                 push    MB_ICONHAND     ; uType
cseg01:8B3F                 call    MESSAGEBOX
cseg01:8B44                 xor     ax, ax
но Text и Caption не инициализированы,
зато в самой WINMAIN есть код их инициализации, почти сразу же после входа в функцию
Код:
cseg01:8B91                 mov     cx, [bp+hInstance]
cseg01:8B94                 mov     hInstance, cx
cseg01:8B98                 push    cx              ; hInstance
cseg01:8B99                 push    22              ; uID
cseg01:8B9B                 push    ds
cseg01:8B9C                 push    offset Text     ; lpBuffer
cseg01:8B9F                 push    _MAX_PATH       ; cchBufferMax
cseg01:8BA2                 call    LOADSTRING
cseg01:8BA7                 or      ax, ax
cseg01:8BA9                 jz      short loc_8BD3
cseg01:8BAB                 push    hInstance       ; hInstance
cseg01:8BAF                 push    23              ; uID
cseg01:8BB1                 push    ds
cseg01:8BB2                 push    offset Caption  ; lpBuffer
cseg01:8BB5                 push    _MAX_PATH       ; cchBufferMax
cseg01:8BB8                 call    LOADSTRING
cseg01:8BBD                 or      ax, ax
cseg01:8BBF                 jz      short loc_8BD3
cseg01:8BC1                 push    hInstance       ; hInstance
cseg01:8BC5                 push    24              ; uID
cseg01:8BC7                 push    ds
cseg01:8BC8                 push    offset byte_C7DC ; lpBuffer
cseg01:8BCB                 push    _MAX_PATH       ; cchBufferMax
cseg01:8BCE                 call    LOADSTRING
итак патчим:
Код:
cseg01:8B31 ; ---------------------------------------------------------------------------
cseg01:8B31 ; START OF FUNCTION CHUNK FOR WINMAIN
cseg01:8B31
cseg01:8B31 loc_8B31:                               ; CODE XREF: cseg01:89F2j
cseg01:8B31                                         ; cseg01:8A0Fj ...
cseg01:8B31                 push    word_C5D2       ; HWND
cseg01:8B35                 push    ds
cseg01:8B36                 push    offset byte_C5D4 ; LPCSTR
cseg01:8B39                 push    ds
cseg01:8B3A                 push    offset byte_C6D8 ; LPCSTR
cseg01:8B3D                 push    10h             ; UINT
cseg01:8B3F                 call    MESSAGEBOX
cseg01:8B44                 xor     ax, ax
cseg01:8B46
cseg01:8B46 loc_8B46:                               ; CODE XREF: cseg01:89D7j
cseg01:8B46                                         ; cseg01:8B2Fj
cseg01:8B46                 mov     sp, bp ; сюда дописываем выход из процедуры
cseg01:8B48                 pop     bp
cseg01:8B49                 retf
cseg01:8B49 ; END OF FUNCTION CHUNK FOR WINMAIN
cseg01:8B49 ; ---------------------------------------------------------------------------
cseg01:8B4A                 db 4 dup(0)
Код:
cseg01:8B4E ; =============== S U B R O U T I N E =======================================
cseg01:8B4E
cseg01:8B4E ; Attributes: bp-based frame
cseg01:8B4E
cseg01:8B4E ; int __pascal __far WINMAIN(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPCSTR lpCmdLine, int nShowCmd)
cseg01:8B4E WINMAIN         proc far                ; CODE XREF: __stubmain+1EP
cseg01:8B4E
cseg01:8B4E nShowCmd        = word ptr  6
cseg01:8B4E lpCmdLine       = dword ptr  8
cseg01:8B4E hPrevInstance   = word ptr  0Ch
cseg01:8B4E hInstance       = word ptr  0Eh
cseg01:8B4E
cseg01:8B4E ; FUNCTION CHUNK AT cseg01:8B31 SIZE 00000019 BYTES
cseg01:8B4E
cseg01:8B4E                 push    bp ; а здесь был сложный вход в процедуру с выделением места под локальные переменные, но нам нужно максимально просто
cseg01:8B4F                 mov     bp, sp
cseg01:8B51                 jmp     short loc_8B91 ; наши столь нужные переходы
cseg01:8B51 ; ---------------------------------------------------------------------------
cseg01:8B53                 db 2 dup(90h)
cseg01:8B55 ; ---------------------------------------------------------------------------
cseg01:8B55
cseg01:8B55 loc_8B55:                               ; CODE XREF: WINMAIN:loc_8BD3j
cseg01:8B55                 jmp     short loc_8B31 ; наши столь нужные переходы
cseg01:8B55 ; ---------------------------------------------------------------------------
cseg01:8B57                 db 0D8h,81h,0ECh,88h,6,57h,56h,8Eh,6,0Ah,0Bh,26h,0A1h
cseg01:8B57                 db 0E2h,0B6h,26h,8Bh,16h,0E4h,0B6h,89h,86h,36h,0FFh,89h
cseg01:8B57                 db 96h,38h,0FFh,33h,0C0h,0B9h,4Eh,0,8Dh,0BEh,3Ah,0FFh
cseg01:8B57                 db 16h,7,0F3h,0ABh,2Bh,0C9h,89h,4Eh,0ECh,89h,4Eh,0EAh
cseg01:8B57                 db 89h,46h,0E6h,89h,46h,0E8h,0A2h,56h,4Ah
cseg01:8B91 ; ---------------------------------------------------------------------------
cseg01:8B91
cseg01:8B91 loc_8B91:                               ; CODE XREF: WINMAIN+3j
cseg01:8B91                 mov     cx, [bp+hInstance]
cseg01:8B94                 mov     hInstance, cx
cseg01:8B98                 push    cx              ; hInstance
cseg01:8B99                 push    22              ; uID
cseg01:8B9B                 push    ds
cseg01:8B9C                 push    offset Text     ; lpBuffer
cseg01:8B9F                 push    _MAX_PATH       ; cchBufferMax
cseg01:8BA2                 call    LOADSTRING
cseg01:8BA7                 or      ax, ax
cseg01:8BA9                 jz      short loc_8BD3
cseg01:8BAB                 push    hInstance       ; hInstance
cseg01:8BAF                 push    23              ; uID
cseg01:8BB1                 push    ds
cseg01:8BB2                 push    offset Caption  ; lpBuffer
cseg01:8BB5                 push    _MAX_PATH       ; cchBufferMax
cseg01:8BB8                 call    LOADSTRING
cseg01:8BBD                 or      ax, ax
cseg01:8BBF                 jz      short loc_8BD3
cseg01:8BC1                 push    hInstance       ; hInstance
cseg01:8BC5                 push    24              ; uID
cseg01:8BC7                 push    ds
cseg01:8BC8                 push    offset byte_C7DC ; lpBuffer
cseg01:8BCB                 push    _MAX_PATH       ; cchBufferMax
cseg01:8BCE                 call    LOADSTRING

cseg01:8BD3
cseg01:8BD3 loc_8BD3:                               ; CODE XREF: WINMAIN+5Bj
cseg01:8BD3                                         ; WINMAIN+71j
cseg01:8BD3                 jmp     short loc_8B55 ; наши столь нужные переходы
cseg01:8BD3 WINMAIN         endp

в 32битных системах патч привел к тому, что отображается MessageBox с заголовком "Setup initialization error" и текстом "Insufficent memory" эти строки как раз содержимое ресурса таблиц строк под номерами 22 и 23.
НО!!!!!!!!!!!
в 64битных системах логика программы осталась неизменной выводится MessageBox с заголовком "Setup initialization error" и текстом "Setup cannot access the required initialization file '...\ACMBOOT.LST'." (потому что идет проверка есть ли данный файл - НО МЫ УБИЛИ ВСЮ ЛОГИКУ ПРОВЕРКИ, она была в WINMAIN но мы ее изрешетили jmp`ами так, чтоб никаких проверок, а сразу MessageBox), более того если создадим ACMBOOT.LST с произвольным содержимым, то выводится MessageBox с заголовком "Setup initialization error" и текстом "This Setup program is not intended to be used with your version of Windows." , и даже если перебъем ресурс таблицы строк другим текстом, текст отображается именно этот.
Пробовал патченный файл запускать в 64битном окружении новой совершенно чистой свежепоставленной виртуальной машиной - то же самое - функционал откуда то подхватывается.

Вот теперь сам вопрос знатокам, acmboot.exe как то эмулировано прямо в NTVDM на 64битных системах? Откуда система берет код в 64битном окружении? спасибо.

П.С. где достать этот acmboot.exe я уже написал (можно в официальном источнике, и пропатчить так как было изложено выше), а для ленивых(и смелых) пропатченный экзешник и IDAPro`шные *.idb к нему и к оригиналу - в приложенном архиве. до winXP включительно просто запускается, на поздних windows wowexec требует админских привелегий. (Этот патченный экзешник содержит только 16разрядный код, который в 32разрядном окружении ведет себя как задумано патчем, а в 64разрядном так как предусмотрела микрософт независимо от патча (у него нет никакого кода ни для win32 ни для win64))
 

Вложения

  • donor.zip
    534,9 КБ · Просмотры: 149
Последнее редактирование:

ProMiNick

One Level
20.03.2020
4
2
BIT
0
Чтож, надо было смотреть в запускаемых процессах(((((((
При запуске в 64разрядном окружении у файла NE формата проверяется ресурс версия, если содержимое этого ресурса соответствует каким-то сигнатурам, то вместо acmboot.exe запускается SysWOW64\setup16.exe, он обычный PE 32, параметры окружения запускаемого:
Path: C:\WINDOWS\SysWOW64\setup16.exe
Command line: "...\acmboot.exe" -m "...\acmboot.exe" ;коммандная строка и текущая директория наследуются от acmboot.exe, появляется интересный ключ -m
Current directory: "..."

Грааль найти не получилось(
Еще раз убедился что 16 разрядные приложения не запускаются под 64разрядном окружением. Спасибо Process Explorer (Sysenternals) его скриншот.
 

Вложения

  • setup16.JPG
    setup16.JPG
    51,3 КБ · Просмотры: 169

ProMiNick

One Level
20.03.2020
4
2
BIT
0
Чтобы автору вопроса закрыть свой тикет (сменить префикс "Проблема" на "Решено") .Просто нажмите значок 🏆 в левой части сообщения, слева от аватарки. А что делать когда автор сам допетрил?
Маркируйте сами (админы) как "Решено".
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!