→ CODEGATE 2012 Prob #1

| No TrackBacks
I've lost my source code!
Fortunately, I have a test program.
But, Test program is not perfect.
Please, I need your help.

Дан архив 49F69C55C47B4AA87059F3EEF391F5A7 с запакованным ASProtect исполняемым файлом и запароленным ZIP-архивом внутри. Распакуем ASProtect.
Загружаем в Olly, на точке входа видим стартовый код ASProtect:
00401000 >/$ 68 01D04000    PUSH test_pas.0040D001
00401005  |. E8 01000000    CALL test_pas.0040100B
0040100A  \. C3             RETN
0040100B   $ C3             RETN
Ставим бряк на GetProcAddress и хардварный на исполнение на 00401000. Запускаем. Брякаемся на GetProcAddress:
0012FF28   00462090  CALL to GetProcAddress from test_pas.0046208A
0012FF2C   75DC0000  hModule = 75DC0000 (kernel32)
0012FF30   004627A9  ProcNameOrOrdinal = "VirtualAlloc"
0012FF34   0042F000  test_pas.0042F000
0012FF38   00220000
0012FF3C   0040D0FF  RETURN to test_pas.0040D0FF from test_pas.0040D0FF
Пропускаем первые два вызова - получение адресов VirtualAlloc / VirtualFree. Следующий останов - обработка адресов импорта:
0012FF24   00462513  /CALL to GetProcAddress from test_pas.0046250D
0012FF28   75DC0000  |hModule = 75DC0000 (kernel32)
0012FF2C   0045D37C  \ProcNameOrOrdinal = "GetCurrentThreadId"
0012FF30   0045D37C  ASCII "GetCurrentThreadId"
0012FF34   0042F000  test_pas.0042F000
Выходим из GetProcAddress, попадаем в цикл заполнения адресов:
00462513   85C0             TEST EAX,EAX
00462515   5B               POP EBX                         ; test_pas.0045D37C
00462516   75 6F            JNZ SHORT test_pas.00462587
...
00462587   8907             MOV DWORD PTR DS:[EDI],EAX      ; kernel32.GetCurrentThreadId
00462589   8385 51294400 04 ADD DWORD PTR SS:[EBP+442951],4
EDI = 0045D104 указывает на часть IAT. Ставим точки останова на выход из цикла
004625AB   8B85 652A4400    MOV EAX,DWORD PTR SS:[EBP+442A65]
и получаем первый кусок импортов:
0045D104  75E0C410  kernel32.GetCurrentThreadId
0045D108  77949A55  ntdll.RtlDeleteCriticalSection
...
0045D360  768E3EB8  WS2_32.socket
0045D364  768E3AB2  WS2_32.WSAStartup
0045D368  00000000
Восстанавливаем точку останова на GetProcAddress и попадаем на код формирования второго куска импортов:
00457EB4   E8 87CAFDFF      CALL test_pas.00434940         ; JMP to kernel32.GetProcAddress
00457EB9   8907             MOV DWORD PTR DS:[EDI],EAX
00457EBB   83C7 04          ADD EDI,4
00457EBE   83C6 04          ADD ESI,4
00457EC1   83C3 04          ADD EBX,4
00457EC4   FF4D F8          DEC DWORD PTR SS:[EBP-8]
00457EC7  ^75 C2            JNZ SHORT test_pas.00457E8B
00457EC9   68 80804500      PUSH test_pas.00458080         ; ASCII "NTDLL.DLL"
После останова на 00457EC9 получим второй кусок:
0045B9EC  75E0CC94  kernel32.GetProcAddress
0045B9F0  75E0DC65  kernel32.LoadLibraryA
0045B9F4  75E093DB  kernel32.MapViewOfFile
...
0045BA18  75E0C43A  kernel32.VirtualAlloc
0045BA1C  75E16B15  kernel32.VirtualFree
0045BA20  75E0D7B5  kernel32.GetCurrentProcessId
Далее будет происходит формирование 3-го куска импортов, но он размазан в лапше переходов и получения адресов вспомогательных функций. Поэтому, лучше запустить на выполнение, и остановиться по исключению или при выполнении команды по адресу 00401000. Затем запустить ImpRec и воспользоваться автопоиском. ImpRec находит 3-й кусок импортов по адресу 00405000.
00405000  77942D66  ntdll.RtlAllocateHeap
00405004  75E18EAF  kernel32.GetCommandLineA
00405008  75E028F1  kernel32.GetVersion
...
00405084  0044BC38  test_pas.0044BC38
...
004050A8  75E1532E  kernel32.GetStringTypeW
004050AC  00000000
Один адрес съеден ASProtect'ом, идем по 0044BC38:
0044BC38   55               PUSH EBP
0044BC39   8BEC             MOV EBP,ESP
0044BC3B   8B55 0C          MOV EDX,DWORD PTR SS:[EBP+C]
0044BC3E   8B45 08          MOV EAX,DWORD PTR SS:[EBP+8]
0044BC41   3B05 9CA54500    CMP EAX,DWORD PTR DS:[45A59C]
0044BC47   75 09            JNZ SHORT test_pas.0044BC52
0044BC49   8B0495 9CA54500  MOV EAX,DWORD PTR DS:[EDX*4+45A59C]
0044BC50   EB 07            JMP SHORT test_pas.0044BC59
0044BC52   52               PUSH EDX
0044BC53   50               PUSH EAX
0044BC54   E8 E78CFEFF      CALL test_pas.00434940      ; JMP to kernel32.GetProcAddress
0044BC59   5D               POP EBP
0044BC5A   C2 0800          RETN 8
Очевидно, это переходник к GetProcAddress. Фиксим в ImpRec. Добавляем к найденному импорту куски 1 и 2.
Теперь можно дойти до 00401000 по хардварному брейкпоинту, делать дамп, и чинить импорт у дампа в ImpRec. Почти все сделано, осталось найти OEP и пофиксить API, "виртуализованные" протектором.

Открываем дамп в IDA, применяем сигнатуру Microsoft VisualC 2-10/net runtime, получаем точку входа 004011EF (_mainCRTStartup) и main по адресу 00401000.
Восстановим "виртуализованные" API.
0040125E  |. E8 C1180000    CALL test_pas.00402B24
00401263  |. E8 9CED1C01    CALL 015D0004
00401268  |. D8A3 00C74000  FSUB DWORD PTR DS:[EBX+40C700]
...
015D0004   FF0424           INC DWORD PTR SS:[ESP]
015D0007   3E:EB 02         JMP SHORT 015D000C            ; Superfluous prefix
015D000A   CD 20            INT 20
015D000C  -E9 EFFFFEFF      JMP 015C0000
...
015C0000   A1 8C47C075      MOV EAX,DWORD PTR DS:[75C0478C]
015C0005   C3               RETN
Схема работы: 5-ти байтный CALL 015D0004 вызывает переходник, который правит адрес возврата на 1 (INC [ESP]), и отдает управление на начало предыдущего блока (10000h). Другими словами, для 015D000C код "виртуализованной" функции следует искать на 015C0000. Это общая схема. ASProtect может полностью утащить функцию к себе в буфер, если она короткая, или ее начало.

В первом случае, нужно скопировать код (Binary | Binary copy в Olly) и выполнить поиск бинарной строки в памяти, а затем найти полученный адрес в импорте DLL (View | Executable Modules, View Name)
75BCEE6A >A1 8C 47 C0 75 C3 90 90                          ЎЊGАuГђђ

Names in KERNELBA, item 639
 Address=75BCEE6A
 Section=.text
 Type=Export  (Known)
 Name=GetCommandLineA
Во втором случае, когда украдена часть функции, находим адрес внутри API, куда ASProtect отдает управление (75BD3C05), прокручиваем до начала функции (75BD3BFC) и находим ее в экспорте:
004024CC   . E8 33DB2001    CALL 01610004
004024D1   . 6F             OUTS DX,DWORD PTR ES:[EDI]                ;  I/O command
...
01600000   8BFF             MOV EDI,EDI
01600002   55               PUSH EBP
01600003   8BEC             MOV EBP,ESP
01600005   5D               POP EBP
01600006   8BFF             MOV EDI,EDI
01600008   55               PUSH EBP
01600009   8BEC             MOV EBP,ESP
0160000B   837D 08 00       CMP DWORD PTR SS:[EBP+8],0
0160000F   68 053CBD75      PUSH 75BD3C05
01600014   C3               RETN
...
75BD3BFC > 8BFF             MOV EDI,EDI
75BD3BFE   55               PUSH EBP
75BD3BFF   8BEC             MOV EBP,ESP
75BD3C01   837D 08 00       CMP DWORD PTR SS:[EBP+8],0
75BD3C05   0F84 AA670100    JE KERNELBA.75BEA3B5

Names in KERNELBA, item 677
 Address=75BD3BFC
 Section=.text
 Type=Export  (Known)
 Name=TerminateProcess
После определения адреса API, ищем его в кусках импорта, и заменяем вызов переходника на прямой вызов импорта:
CALL 01610004 -> CALL DWORD PTR [00405030]
После правки всех переходников, получаем рабочий сэмпл.

Изучаем распакованный код, main тривиальна и бессмысленна:
int main(int argc, const char **argv, const char **envp)
{
  FILE *f;
  int h;
  size_t length;
  void *buffer;

  f = fopen("password.enc", "rb");
  fopen("password.c", "wb");
  h = fileno(f);
  length = filelength(h);
  buffer = malloc(length);
  malloc(length);
  fread(buffer, length, 1, f);
  return fcloseall();
}


По задумке авторов, задание следовало решать так.

Если посмотреть на окрестности строк "password" в секции данных, то можно заметить, что находится кусок ZIP архива - такой же конец имеет запароленный архив password.enc.
.0040A8F0:  85 3F 51 1D.BE 82 7A 48.00 00 37 EB.00 00 0A 00  Е?Q↔╛ВzH  7ы  ◙
.0040A900:  00 00 00 00.00 00 01 00.20 00 00 00.00 00 00 00        ☺
.0040A910:  70 61 73 73.77 6F 72 64.2E 63 50 4B.05 06 00 00  password.cPK♣♠
.0040A920:  00 00 01 00.01 00 38 00.00 00 B2 48.00 00 00 00    ☺ ☺ 8   ▓H
Поксорив кусок с адреса 00406030 с password.enc получим исполняемый файл, который выводит строку The_Art_of_The_War, которая, судя по всему, и является ответом.

Спасибо PPP за наводку на XOR.

No TrackBacks

TrackBack URL: http://smokedchicken.org/m/mt-tb.cgi/74

About this Entry

This page contains a single entry by Павел Збицкий published on April 7, 2012 7:06 PM.

Team collaboration during CTF competitions was the previous entry in this blog.

PlaidCTF 2012 - Game [100] is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.