в начале shell-кода расположен расшифровщик, расшифровывающий весь остальной код
Первые 8 команд более или менее понятны, а вот дальше начинается явный мусор, типа инструкций IN и OUT, которые при попытке выполнения на прикладном режиме возбуждают исключение. Тут что-то не так! Либо точка входа в shell-код начинается не с первого байта (но это противоречит результатам наших исследований), либо shell-код зашифрован. Присмотревшись к первым восьми командам повнимательнее, мы с удовлетворением обнаруживаем тривиальный расшифровщик в лице инструкции XOR, следовательно, точка входа в shell-код определена нами правильно и все, что нужно — это расшифровать его, а для этого мы должны определить значение регистров EBX и ECX, используемых расшифровщиком.
С регистром ECX разобраться несложно — он инициализируется явно, путем нехитрых математических преобразований: sub ecx,ecxàecx:=0; sub ebx,-50hàadd ecx,50hàecx := 50h, то есть на входе в расшифровщик ECX будет иметь значение 50h – именно столько двойных слов нам предстоит расшифровать.
С регистром EBX все обстоит намного сложнее и чтобы вычислить его значение, необходимо углубиться во внутренние структуры данных сопроцессора. Команда FLDZ помещает на стек сопроцессора константу +0.0, а команда FSTENV сохраняет текущую среду сопроцессора по адресу [esp-0Ch]. Открыв "Intel Architecture Software Developer's Manual Volume 2: Instruction Set Reference", среди прочей полезной информации мы найдем и сам формат среды FPU:
FPUControlWord ß SRC(FPUControlWord);
FPUStatusWord ß SRC(FPUStatusWord);
FPUTagWord ß SRC(FPUTagWord);
FPUDataPointer ß SRC(FPUDataPointer);
FPUInstructionPointer ß SRC(FPUInstructionPointer);
FPULastInstructionOpcode ß SRC(FPULastInstructionOpcode);