Сегодня у нас день больших компаний (это я к тому, что маленькие и средние в этой рубрике тоже всегда приветствуются). Гранды отечественной IT-индустрии задают нашим читателям задачи и публикуют решения. А вот победителей пока не славим — в связи с ранней сдачей этого номера (для тебя новый год уже наступил, и ты уже даже от него потихоньку отходишь, а в нашей реальности до него еще месяц). Победителей задач от Parallels жди в следующем номере, а пока выпей рассола и насладись новой партией квестов!
Задачи от «Лаборатории Касперского»
Задача 1
При анализе, возможно, вредоносного семпла под Linux натолкнулись на такую функцию. Необходимо определить, что именно возвращает функция.
int what_sz = 3;
char what[] = "\xff\x14\x85";
void *
abcdefh(void)
{
void *tmp;
uint8_t **ptr;
struct idtr idtr;
struct idt *idt;
__asm__("sidt %0" : "=m" (idtr));
idt = (struct idt *) (idtr.base + (0x80 * 8));
tmp = (void *)((idt->off2 << 16) | idt->off1);
ptr = memmem(tmp, 0x100,
what, what_sz);
if (ptr == NULL)
return NULL;
ptr += 3;
return *ptr;
}
Задача 2
Три программы написаны на несуществующем языке программирования, скомпилированы в несуществующем компиляторе и выполняются в несуществующей среде.
Вопрос: какие изменения в файловой системе производит третья программа?
Первая программа:
HEX View: 04 00 00 00 0C 00 54 65 73 74 46 69 6C 65 4E 61 6D 65 09 00 46 69 72 73 74 4C 69 6E 65 0E 00 54 65 73 74 46 69 6C 65 48 61 6E 64 6C 65 0A 00 53 65 63 6F 6E 64 4C 69 6E 65 01 00 00 00 09 03 00 00 00 00 00 05 00 00 00 68 01 00 00 00 69 01 00 00 00 01 00 00 00 6B 01 00 00 00 02 00 00 00 6B 01 00 00 00 04 00 00 00 6A 01 00 00 00
TEXT View: ......TestFileName..FirstLine..TestFileHandle..SecondLine...............h....i........k........k........j....
Вторая программа:
HEX View: 04 00 00 00 0C 00 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 19 00 43 6F 6E 63 61 74 65 6E 61 74 65 64 53 74 72 69 6E 67 45 78 61 6D 70 6C 65 11 00 46 69 72 73 74 20 73 74 72 69 6E 67 20 70 61 72 74 12 00 53 65 63 6F 6E 64 20 73 74 72 69 6E 67 20 70 61 72 74 01 00 00 00 05 02 00 00 00 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 65 01 00 00 00 03 00 00 00 65 01 00 00 00 04 00 00 00 66 01 00 00 00 67 01 00 00 00
TEXT View: ......Hello World!..ConcatenatedStringExample..First string part..Second string part.........(.............................................e........e........f....g....
Третья программа:
HEX View: 07 00 00 00 0B 00 44 72 65 61 6D 77 6F 72 6C 64 21 09 00 4B 61 73 70 65 72 73 6B 79 0A 00 47 72 65 65 74 69 6E 67 73 20 04 00 74 68 65 20 04 00 54 65 6D 70 05 00 66 72 6F 6D 20 02 00 48 46 02 00 00 00 09 07 00 00 00 00 00 05 05 00 00 00 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 65 02 00 00 00 03 00 00 00 65 02 00 00 00 06 00 00 00 65 02 00 00 00 04 00 00 00 65 02 00 00 00 01 00 00 00 68 02 00 00 00 69 01 00 00 00 02 00 00 00 6C 01 00 00 00 02 00 00 00 6A 01 00 00 00
TEXT View: ......Dreamworld!..Kaspersky..Greetings ..the ..Temp..from ..HF................".......................................e........e........e........e........h....i........l........j....
Ответы на задачи компании Parallels
Задача 1
В два раза. Если человек побежит вперед, то к моменту, когда он добежит до середины туннеля (то есть пробежит еще 1/4 туннеля), поезд доедет до начала туннеля (из условия 1). Соответственно, человек пробежит половину туннеля за то же время, что поезд проедет весь (из условия 2). То есть он бежит в два раза медленнее поезда.
Задача 2
Ответ: математических решений — бесконечное количество. Оптимальных и уникальных — два (а не одно, как многие предполагают).
- Начать с перевозки козы. Вернуться, перевезти волка, оставить его, забрать козу и отвезти обратно. Оставить ее и перевезти к волку капусту. Вернуться и перевезти козу.
- Начать с перевозки козы. Вернуться, взять капусту, отвезти ее на другой берег, оставить там и вернуть на первый берег козу. Затем перевезти на другой берег волка, вернуться за козой и снова отвезти ее на другой берег. В этом случае количество рейсов (7) точно такое же, как и в первом варианте.
Задача 3
Стандартный ответ незадумавшегося человека: «Конечно, Hello», ведь печать стоит до вызова fork() и вызывается только один раз. На самом деле ситуация более интересная. Дело в том, что в libc потоки ввода/вывода буферизуются, а в реальности вывод на экран происходит в момент печати символа перевода строки или закрытия файлового дескриптора. Таким образом, вывод на самом деле происходит в момент завершения процесса, а это событие уже происходит два раза. Правильный ответ — HelloHello.
Задача 4
Эта программа несколько сложнее. Здесь человек сначала сильно задумывается о том, как именно и почему программа вообще что-то напечатает. Эта часть задачи решается относительно легко, в родительском процессе fork() возвращает идентификатор дочернего процесса, и этот идентификатор печатается в обработчике сигнала, который присылается в момент завершения дочернего процесса. Но это неправильный ответ. Дело в том, что на самом деле поведение этой программы не детерминировано, и именно это хочется услышать от собеседуемого. Здесь с точки зрения родительского процесса выполняются две атомарные операции: системный вызов fork() и присвоение результатов этого системного вызова глобальной переменной pid. Соответственно, сигнал об окончании дочернего процесса может прийти между этими операциями, и это действительно происходит, примерно в одном случае из десяти.
Задача 5
Очень хочется услышать от кандидата про выравнивание полей в структуре и про упакованные структуры. Достаточно частый неправильный ответ — 32 байта, альтернативный неправильный ответ — зависит от компилятора. На самом деле эта ситуация четко регламентируется стандартом языка, эта структура ВСЕГДА будет занимать 16 байт, для обеспечения правильного выравнивания достаточно вставить один байт паддинга после поля 'a'. Ну и конечно, хочется услышать, что сделано так в силу архитектуры RISC процессоров, которые требуют адрес, выравненный на размер считываемого целого. Сама же структура может быть выровнена как на 4 байта, так и на 8. Это уже зависит от компилятора и архитектуры. На 64 битах выравнивание на 8 обязательно, на 32 — нет. И именно в этом моменте MSVC отличается от GCC, один структуру выравнивает на 64 бита, второй — на 32.
Задача 6
Очень интересно посмотреть, как именно человек решает такую задачу, хотя тут вполне подходит ответ, что описан "seq_lock".
Задача 7
Ответ: 24