// Скрипт необходимо запускать стоя на виртуальной точке входа
// Скрипт восстанавливает украденые инструкции и записывает их в конце текущей
// области памяти. Автор: PE_Kill
//----------------------------------------------------------------------------------
var call_VM
var call_cur
//----------------------------------------------------------------------------------
var scan_start
var scan_end
//----------------------------------------------------------------------------------
var base_old
var base_new
//----------------------------------------------------------------------------------
var table_of_jcc
var table_of_jcc_base
var type_of_jcc
//----------------------------------------------------------------------------------
var emule_swich
var emule_last_jamp

var emule_type_jcc_temp
var emule_jcc_temp

var emule_type_jcc
var emule_jcc

var emule_cmp_size
var emule_cmp_get_arg1
var arg1_num
var arg1_display

var emule_cmp_get_arg2
var arg2_num
var arg2_display

var emule_type_cmp
var type_of_cmp
var emule_cmp

var emule_cmp_jcc
var emule_cmp_type_jcc
//----------------------------------------------------------------------------------
var str1
var str2
var str3
//----------------------------------------------------------------------------------
var oep
var temp
var Info
var addr_cur
var iter
var trace_call
//----------------------------------------------------------------------------------
log ""
log "____________________________________"
log ""
log "Virtual Machine Rebuilder by PE_Kill"
log "____________________________________"
log ""
log "Inicialize..."
//jmp @init_hand
  ////////////////////////////////////////////////////////////////////////////////////////
  // Автонастройка скрипта								//
  ////////////////////////////////////////////////////////////////////////////////////////
mov oep,eip			// Сохраняем eip для последующего восстановления
mov addr_cur,eip		// Получаем текущий eip
and addr_cur,FFFFF000		// Выравниваем на 1000h
mov eip,addr_cur		// Переходим на получившийся адрес
mov scan_start,eip		//!Текущий адрес скорее всего будет начальной границей сканирования
mov base_old,eip		//!Текущий адрес будет также базой спертого кода
  //====================================================================================//
  // Цикл поиска келла, ведущего в VM. Отличительной особенностью такого келла является //
  // то, что он выровнен на 1000h. Но мы еще перестрахуемся.				//
  //====================================================================================//
@init_find_VM_call:		
  find addr_cur,#E8#		// Ищем опкод "call"
    cmp $RESULT,0		// Нашли что нибудь?
    je ERR_VM_CALL_NOT_FOUND	// Нет :( Солодовников опять нас перехитрил :( Генерим ошибку и уходим
  mov addr_cur,$RESULT		// Да! Заносим найденый адрес в переменную
  inc addr_cur			// Смещаем указатель непосредственно на адрес келла VM (отбрасываем E8)
  mov call_VM,[addr_cur]	// Заносим в переменную смещение келла VM
  add call_VM,addr_cur		// Вычисляем абсолютный адрес call_VM
  add call_VM,4			//!Корректировка относительно текущего келла.
  mov call_cur,call_VM		// Задействуем другую переменную. Эта останется константой для основного скрипта
  and call_cur,00000FFF		// Обнуляем первые 5 цифр. Получаем выравнивание келла
  cmp call_cur,00000000		// Если он выровнен на 1000h байт, то будем пока считать, что это наш call_VM
jne @init_find_VM_call		// Иначе продолжим поиск
//end @init_find_VM_call
  //====================================================================================//
dec addr_cur			// Смещаем указатель на начало call_VM
mov eip,addr_cur		// Встаем на найденый адрес  call_VM
inc addr_cur			// Увеличиваем указатель на случай, если это не call_VM и придется продолжать поиск
mov trace_call,eip		// Запоминаем текущий адрес для последующей трассировки
sti				// Step into. Заходим в найденый келл.
//======================================================================================//
// call_VM ведет в область памяти, где выполняется инициализация данных перед эмуляцией.//
// Нам эта функция не нужна, но после нее идет прыжок во вторую часть VM. Вот адрес 	//
// второй части VM нам и надо получить. Для этого будем трассировать до тех пор, пока	//
// не войдем в какой либо келл (он долженбыть один) либо пока счетчик проходов не станет//
// равным 300h (тогда это не VM)							//
//======================================================================================//
mov iter,0			// Обнуляем счетчик
@init_trace_prefix:		
  inc iter			// Увеливаем на 1
  cmp iter,300			// Уже 300-я итерация?
  je @init_find_VM_call		// Да, начинаем все сначала.
//!![ Далее, чтобы не засорять скрипт лишними переменными я использую переменные ]!!//
//!![ основного скрипта, так что никакой смысловой нагрузки они не несут.	 ]!!//
  mov call_cur,eip		// Запоминаем текущий адрес
  sti				// Делаем один шаг
  mov temp,[esp]		// Получаем верхнее значение стека
  sub temp,2			// Отнимаем 2, келл, в который мы должны войти: call exx, т.е. 2 байта
  cmp temp,call_cur		// Сравниваем сохраненный адрес с предполагаемым адресом возврата-2
jne @init_trace_prefix		// Не равны? Значит мы еще в области инициализации
//and @init_trace_prefix
//======================================================================================//

//======================================================================================//
// В этой области опять происходит инициализация данных, сравнение хешей и другое,	//
// не интересующее нас дерьмо. Но опять же из этой области управление переходит уже 	//
// непосредственно в ядро виртуальной машины. Переход осуществляется последним келлом	//
// цикла перебора хешей. Если хешь не будет найден, АСПР выдаст ошибку 111, а переход в	//
// ядро VM находится чуть выше келла, выдающего ошибку. Его и будем искать.		//
//======================================================================================//
mov iter,eip			// запоминаем eip
mov addr_cur,eip		// запоминаем eip
@init_find_error111:		// ищем келл, выдающий ошибку 111
  find addr_cur,#68#		// ищем PUSH XXXXXXXX
    cmp $RESULT,0		// не найден?
    je @ERR_INIT_FAILED		// генерим ошибку и уходим
  mov addr_cur,$RESULT		// получаем адрес найденого PUSH'a
  inc addr_cur			// отбрасываем опкод PUSH
  mov call_cur,[addr_cur]	// получаем, значение, которое заносится в стек
  mov temp,[call_cur]		// если это то, что мы ищем, то это указатель на строку "111"
  cmp temp,0D313131		// проверяем, так ли это
jne @init_find_error111		// нет, это не указатель, ищем дальше
//end @init_find_error111

@init_find_emulate_instr:	// теперь будем подниматься вверх, пока не найдем опкод call
  dec addr_cur			// или не дойдем до адреса, с которого начали искать (тогда облом)
  cmp addr_cur,iter		// проверяем, не дошли ли до адреса, откуда пришли
  je @ERR_INIT_FAILED		// если да, генерим ошибку и уходим
  mov temp,[addr_cur]		// получаем, то что нашли
  and temp,FF000000		// обнуляем все байты, кроме первого
  cmp temp,E8000000		// сравниваем первый байт с опкодом call
jne @init_find_emulate_instr	// если не равен, продолжим поиск
//end @init_find_emulate_instr
add addr_cur,3			// корректируем указатель на келл
bp addr_cur			// ставим бряк на адрес найденого келла
run				// запускаем программу
bc addr_cur			// прерываемся и убираем бряк
cmp eip,addr_cur		// проверяем правильно ли приземлились
  jne @ERR_INIT_FAILED		// если нет, генерим ошибку и уходим
sti				// Step into. Входим в келл.
//======================================================================================//

//======================================================================================//
// Ищем инструкции:									//
// SUB AL,2										//
// JB SHORT ХХХХХХХХ									//
// JE SHORT ХХХХХХХХ									//
// Так определяется ощий вид эмулируемой(ых) инструкции(й)				//
//======================================================================================//

find eip,#2C027212743D#		// собственно поиск
cmp $RESULT,0			// найдены?
je @ERR_INIT_FAILED		// нет, генерим ошибку и уходим
bp $RESULT			// найдены! ставим бряк на найденый адрес
run				// запускаем программу
bc $RESULT			// прерываемся и убираем бряк
cmp eip,$RESULT			// проверяем правильно ли приземлились
  jne @ERR_INIT_FAILED		// если нет, генерим ошибку и уходим
mov emule_swich,eip		//!запоминаем, понадобится в основном скрипте
find eip,#FF6020#		// теперь ищем инструкцию JMP DWORD PTR DS:[EAX+20]
cmp $RESULT,0			// нашли?
je @ERR_INIT_FAILED		// нет, генерим ошибку и уходим
add eip,16			// прыгаем на 16 байт вперед (аля эмуляция JB SHORT ХХХХХХХХ)
bp $RESULT			// ставим бряк на найденый адрес
run				// запускаем программу
bc $RESULT			// прерываемся и убираем бряк
cmp eip,$RESULT			// проверяем правильно ли приземлились
  jne @ERR_INIT_FAILED		// если нет, генерим ошибку и уходим
mov emule_last_jamp,eax		// получаем значение eax
add emule_last_jamp,20		// ...
mov emule_last_jamp,[emule_last_jamp]
mov eip,trace_call
bp emule_swich
run
bc emule_swich
add eip,43
@init_find_emule_type_jcc:
  mov temp,eip
  sti
  mov call_cur,[esp]
  mov addr_cur,call_cur
  sub call_cur,2
  cmp temp,call_cur
  jne @init_find_emule_type_jcc
bp addr_cur
run
bc addr_cur
mov emule_type_jcc,eip			//!!!!
find eip,#84C07417#
  cmp $RESULT,0
  je @ERR_INIT_FAILED
mov emule_jcc,$RESULT

mov eip,trace_call
bp emule_swich
run
bc emule_swich
add eip,90
@init_find_emule_cmp:
  mov temp,eip
  sti
  mov call_cur,[esp]
  mov addr_cur,call_cur
  sub call_cur,5
  cmp temp,call_cur
jne @init_find_emule_cmp
bp addr_cur
mov iter,0
@init_find_emule_cmp_get_arg1:
  mov temp,eip
  sti
  mov call_cur,[esp]
  mov addr_cur,call_cur
  sub call_cur,2
  cmp temp,call_cur
jne @init_find_emule_cmp_get_arg1
bp addr_cur
run
bc addr_cur
inc iter
cmp iter,2
jb @init_find_emule_cmp_get_arg1
mov emule_cmp_get_arg1,eip		//!!!!

mov iter,0
@init_find_emule_cmp_get_arg2:
  mov temp,eip
  sti
  mov call_cur,[esp]
  mov addr_cur,call_cur
  sub call_cur,5
  cmp temp,call_cur
  jne @init_arg1_ret
    bp addr_cur
    run
    bc addr_cur
  @init_arg1_ret:
  add call_cur,3
  cmp temp,call_cur
jne @init_find_emule_cmp_get_arg2
bp addr_cur
run
bc addr_cur
inc iter
cmp iter,2
jb @init_find_emule_cmp_get_arg2
mov emule_cmp_get_arg2,eip		//!!!!

@init_find_emule_type_cmp:
  mov temp,eip
  sti
  mov call_cur,[esp]
  mov addr_cur,call_cur
  sub call_cur,5
  cmp temp,call_cur
  jne @init_arg2_ret
    bp addr_cur
    run
    bc addr_cur
  @init_arg2_ret:
  add call_cur,3
  cmp temp,call_cur
jne @init_find_emule_type_cmp
bp addr_cur
run
bc addr_cur
sti
mov emule_type_cmp,eip			//!!!!
mov eax,4
run
bc eip

@init_find_emule_cmp_type_jcc:
  mov temp,eip
  sti
  mov call_cur,[esp]
  mov addr_cur,call_cur
  sub call_cur,2
  cmp temp,call_cur
jne @init_find_emule_cmp_type_jcc
bp addr_cur
run
bc addr_cur
mov emule_cmp_type_jcc,eip		//!!!!
find eip,#84C07417#
  cmp $RESULT,0
  je @ERR_INIT_FAILED
mov emule_cmp_jcc,$RESULT		//!!!!
mov eip,oep
bp oep
ai
bc oep
////////////////////////
mov addr_cur,eip
@find_empty_space:
  find addr_cur,#00000000000000000000000000#
    cmp $RESULT,0
    je @end
  mov table_of_jcc,$RESULT
  mov addr_cur,$RESULT
  mov temp,$RESULT
  add temp,1A
  find temp,#00000000000000000000000000#
    cmp $RESULT,0
    je @end
  mov addr_cur,$RESULT
  cmp temp,$RESULT
jne @find_empty_space
////////////////////////

mov scan_end,FFFFFFFF
ask "Enter new base of this code"
cmp $RESULT,0
je @ERR_INIT_FAILED
mov base_new, $RESULT //006D1000
jmp @init_skip
ret
////////////////////////
@init_hand:
mov oep,eip				// сохраняем eip, чтобы потом восстановить
mov call_VM	   	,00C00000	// !адрес келла виртуальной машины
mov scan_start	   	,00B70CA1	// !начальный адрес сканирования (поиска VM келлов)
mov scan_end	   	,00B73FFE	// !соответственно конец области сканирования
mov base_old	   	,00B70000	// !база кода, где распалагается виртуальная точка входа
mov base_new	   	,0049A000	// !база созданой нами секции, там будет восстаноленый код
mov table_of_jcc   	,00B72A00	// !адрес пустой области памяти, куда будем писать восстановленые jcc
mov emule_swich	   	,00AA88DA	// !адрес команды "SUB AL,2" для определения ощего типа эмуляции (jmp,call,...)
mov emule_type_jcc 	,00AA892D	// !адрес после келла, определяющего тип эмулируемого джампа
mov emule_jcc	   	,00AA893C	// !адрес команды "TEST AL,AL" для jcc
mov emule_cmp_get_arg1	,00AA8786	// !адрес после келла, определяюжего - по регистру или по константе сравниваем (аргумент1)
mov emule_cmp_get_arg2	,00AA87C2	// !адрес после келла, определяюжего - по регистру или по константе сравниваем (аргумент2)
mov emule_type_cmp	,00AA8805	// !адрес после келла+3 (CMP EAX,4) определяющего тип эмулируемой "CMP" 
mov emule_cmp_type_jcc	,00AA898D	// !адрес после келла, определяющего тип эмулируемого джампа (после cmp) 
mov emule_cmp_jcc	,00AA899C	// !адрес команды "TEST AL,AL" для jcc (после CMP)

mov emule_last_jamp	,00C10000	// !адрес заалоченой памяти, где находится "JMP DWORD PTR SS:[ESP-4]"
@init_skip:
find emule_last_jamp,#FF6424FC#
cmp $RESULT,0
je @ERR_EMULE_LAST_JAMP_NOT_FOUND
mov emule_last_jamp,$RESULT

eval "Find VM call at [{scan_start}..{scan_end}].."
mov Info,$RESULT
log Info

/////////////////////////////////////////////////////////////////////////////////////////////
//Данная процедура пытается найти все функции AsProtect'а эмулирующие нормальные инструкции//
/////////////////////////////////////////////////////////////////////////////////////////////
@find_VM_call:			// цикл поиска VM келлов
  cmp scan_start,scan_end	// проверяем, не достигли ли границы сканирования
  ja @end			// если достигли, то заканчиваем поиск
  find scan_start,#E8#		// ищем опкод "call"
  cmp $RESULT,0			// если ничего не нашли, завершаем поиск
  je @end
  mov addr_cur,$RESULT		// получаем адрес найденого опкода
  inc addr_cur			// увеличиваем на один, чтобы получить смещение на келл
  mov scan_start,addr_cur	// устанавливаем начальную границу сканирования, для следующей итерации
  mov temp,[addr_cur]		// получаем смещение на келл
  add temp,addr_cur		// вычисляем абсолютный адрес
  add temp,4			// корректируем относительно нашего келла
  cmp temp,call_VM		// сравниваем адрес найденого келла и искомого
  jne @find_VM_call		// если не равны, продолжаем поиск
  dec addr_cur			// корректируем указатель на келл
  /////////[ DEBUG INFO ]/////////
  eval "Call found at {addr_cur}"
  mov Info,$RESULT
  log Info
  ////////////////////////////////
  mov eip,addr_cur		// устанавливаем eip на начало VM келла
  ////////////////////////////////
  cmp eip,00E41906
  jne @n
  //msg "pause!"
  @n:
  ////////////////////////////////
  jmp @rebuild_VM_call		// прыгаем в процедуру восстановления келлов
//end @find_VM_call
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// Данная процедура определяет тип эмулируемой команды, и в зависимости //
// от этого передаёт управление соответствующей процедуре восстановления//
//////////////////////////////////////////////////////////////////////////
@rebuild_VM_call:
  sub addr_cur,5		// смещаемся на 5 байт выше нашего келла
  mov temp,[addr_cur]		// читаем, что там лежит
  and temp,000000FF		// нам необходимо проверить только 1 байт, остальным присваеваем 0
  cmp temp,00000068		// сравниваем с опкодом 'PUSH'
  je @rebuild_push		// если равно, необходимо преобразовать под новую базу
  add addr_cur,5		// восстанавливаем указатель на VM келл
 @trace_VM: 
  bp emule_swich		// ставим бряк на узел определения емулируемой команды
  run				// запускаем программу
  bc emule_swich		// прерываемся, убираем бряк
  cmp eip,emule_swich		// проверяем, правильно ли приземлились
  jne @ERR_BP_AT_SWICH_NOT_WORK	// если нет, генерируем ошибку и уходим

  mov emule_type_jcc_temp,emule_type_jcc
  mov emule_jcc_temp,emule_jcc
  mov table_of_jcc_base,table_of_jcc

  mov temp,eax			// получаем тип эмулируемой команды
  cmp temp,0			// 0 - эмулится call
  je @trace_call_or_jmp		// переходим на восстановление келла
  cmp temp,1			// 1 - эмулится jmp
  je @trace_call_or_jmp		// переходим на восстановление джампа
  cmp temp,2			// 2 - эмулится 1 из 16 условных джампов
  je @trace_jcc			// переходим на восстановление jcc (jxx)
  cmp temp,3			// 3 - эмулятся сразу 2е команды - cmp + jcc
  je @trace_cmp_and_jcc		// переходим на восстановление cmp + jcc
  msg "[Error!] Unknown emule command!"	// Иначе что-то идет не так генерим ошибку
  jmp @find_VM_call		// и ищем следующий келл
//end @rebuild_VM_call
//////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// Данная процедура переопределяет базу в адресе, который //
// заносится с помощью PUSH'а в качестве адреса возврата  //
////////////////////////////////////////////////////////////
@rebuild_push:
  inc addr_cur				// смещаем указатель на адрес
  mov temp,[addr_cur]			// получаем адрес
  //////[ DEBUG INFO ]//////
  eval "found PUSH {temp}"		
  mov Info,$RESULT
  log Info
  //////////////////////////
  sub temp,base_old			// вычитаем из адреса старую базу
  add temp,base_new			// прибавляем новую базу
  /////////[ DEBUG INFO ]//////////
  eval "converted to PUSH {temp}"
  mov Info,$RESULT
  log Info
  /////////////////////////////////
  mov [addr_cur],temp			// записыаем измененный адрес на место
  add addr_cur,4			// корректируем указатель
  jmp @trace_VM				// прыгаем назад, в проц. восстановления келлов
//end @rebuild_push
////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// Данная процедура восстанавливает эмулируемые команды "jmp" и "call"  //
//////////////////////////////////////////////////////////////////////////
@trace_call_or_jmp:			
  bp emule_last_jamp			// ставим бряк на последний джамп VM
  run					// запускаем программу
  bc emule_last_jamp			// остановились, убрали бряк	
  cmp eip,emule_last_jamp		// проверяем, правильно ли прервались
  jne @ERR_BP_AT_EMULE_LAST_JAMP_NOT_WORK // если нет, генерим ошибку и выходим
  mov temp,esp				// получаем указатель на стек
  sub temp,4				// поднимаемся выше (esp-4)
  mov call_cur,[temp]			// получаем адрес перехода
  /////[ DEBUG INFO ]/////
  eval "Recovering 'jmp {call_cur}'..." 
  mov Info,$RESULT
  log Info
  ////////////////////////
  mov temp,addr_cur			// в темп кладем адрес перехода
  cmp call_cur,base_old			// проверяем, далекий это адрес или нет
  jae @short_call			// если нет, то не преобразовываем
    sub temp,base_old			// отнимаем от адреса текущюю базу
    add temp,base_new			// и прибавляем базу, которая будет в дампе
  @short_call:
  sub call_cur,temp			// отнимаем от адреса перехода адрес VM келла
  sub call_cur,5			// корректируем
  /// Debug!!!
  mov eip,addr_cur
  ////////////////
  //[ вписываем вместо call 00XX0000 -> jmp XXXXXXXX ]//
  mov [addr_cur],E9			
  inc addr_cur				 
  mov [addr_cur],call_cur
  //////////////////////////////////////////////////////
  jmp @find_VM_call
//end @trace_call_or_jmp
//////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////
// Данная процедура восстанавливает эмулируемые команды "jcc" (всего 16) //
///////////////////////////////////////////////////////////////////////////
@trace_jcc:
  bp emule_type_jcc_temp		// ставим бряк на узел получения типа емул. джампа
  run					// запускаем программу
  bc emule_type_jcc_temp		// прерываемся, и убираем бряк
    cmp eip,emule_type_jcc_temp		// проверяем, правильно ли приземлились
    jne @ERR_BP_AT_EMULE_TYPE_JCC_NOT_WORK// если нет, генерим ошибку и уходим
  mov type_of_jcc,eax		// сохраняем тип эмулируемого джампа
  bp emule_jcc_temp			// ставим бряк на эмулятор джампа
  run					// запускаем программу
  bc emule_jcc_temp			// прерываемся, убираем джамп
    cmp eip,emule_jcc_temp		// проверяем правильно ли приземлились
    jne @ERR_BP_AT_EMULE_JCC_NOT_WORK	// если нет, генерим ошибку и уходим
  mov eax,1				// в eax вбиваем единицу, чтобы обмануть эмулятор
  bp emule_last_jamp			// в зависимости от того, что в еах формируется переход
  run					// ставим бряк на последний джамп эмуля и запускаем прогу
  bc emule_last_jamp			// прерываемся и убираем бряк
    cmp eip,emule_last_jamp		// проверяем, правильно ли прервались
    jne @ERR_BP_AT_EMULE_LAST_JAMP_NOT_WORK // если нет, генерим ошибку и выходим
  mov temp,esp				// получаем указатель на стек
  sub temp,4				// поднимаемся выше (esp-4)
  mov call_cur,[temp]			// получаем адрес перехода
  					//
  cmp call_cur,base_old			// проверяем, далекий это адрес или нет
  jae @short_jcc			// если нет, пропускаем преобразование
    mov temp,base_new			// в темп новую базу
    sub temp,call_addr			// отнимаем от новой базы нужный адрес, получаем смещение
    add temp,base_old			// и прибавляем к смещению старую базу
    mov call_cur,temp			// перезаписываем найденый келл
  @short_jcc:
    
//////////////////////////
// Данный блок команд   //
//  ищет эмулируемый    //
//   джамп по типу      //
//////////////////////////
  cmp type_of_jcc,0	//
  mov temp,"jo"		//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,1	//
  mov temp,"jno"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,2	//
  mov temp,"jb"		//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,3	//
  mov temp,"jnb"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,4	//
  mov temp,"je"		//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,5	//
  mov temp,"jnz"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,6	//
  mov temp,"jpe"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,7	//
  mov temp,"jpo"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,8	//
  mov temp,"js"		//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,9	//
  mov temp,"jns"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,A	//
  mov temp,"jbe"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,B	//
  mov temp,"ja"		//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,C	//
  mov temp,"jl"		//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,D	//
  mov temp,"jge"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,E	//
  mov temp,"jle"	//
  je @patch_table	//
//////////////////////////
  cmp type_of_jcc,F	//
  mov temp,"jg"		//
  je @patch_table	//
//////////////////////////

  //////////////////////////////////////////////////////////////////////////
  // Так, как все эмулируемые джампы короткие (2 байта), а наша таблица   //
  // переходов находится довольно далеко, необходимо преобразовывать      //
  // эти джампы и пересчитывать смещения, а мне лень, поэтому, благодаря  //
  // команде EVAL я склеиваю нужную ассемблерную команду и с помощью      //
  // команды ASM я прошу OllyDbg самому сделать это за меня. Пока ни разу //
  // не отказал :)........................................................//
  //////////////////////////////////////////////////////////////////////////

  @patch_table:			
  eval "{temp} {call_cur}"
  asm table_of_jcc,$RESULT	// Заполняем нашу таблицу джампов
  
  add table_of_jcc,6		// Длинна внесенного джампа = 6 байт
  mov eip,addr_cur		// вновь встаем на VM келл, чтобы найти второй адресс

/////////////////////////////////////////////////
cmp emule_type_jcc_temp,emule_cmp_type_jcc
  jne @recover_jcc
////////////////////////
  bp emule_type_cmp
  run
  bc emule_type_cmp
    cmp eip,emule_type_cmp
    jne @ERR_BP_AT_EMULE_TYPE_CMP_NOT_WORK
  mov ebp,00400000
  add esp,10
  mov [esp],00400000
  sub esp,10
////////////////////////
  ////////////////////////////////////////////////////
 @recover_jcc:
  bp emule_jcc_temp			// сразу ставим бряк на эмулятор джампа
  run					// запускаем программу
  bc emule_jcc_temp			// прерываемся убираем бряк
  cmp eip,emule_jcc_temp		// проверяем правильно ли приземлились
  jne @ERR_BP_AT_EMULE_JCC_NOT_WORK	// если нет генерим ошибку и уходим
  mov eax,0				// в еах 0, чтобы получить второй адрес
  bp emule_last_jamp			// ставим бряк на последний джамп эмуля
  run					// запускаем прогу
  bc emule_last_jamp			// прерываемся и убираем бряк
  cmp eip,emule_last_jamp		// проверяем, правильно ли прервались
  jne @ERR_BP_AT_EMULE_LAST_JAMP_NOT_WORK // если нет, генерим ошибку и выходим
  mov temp,esp				// получаем указатель на стек
  sub temp,4				// поднимаемся выше (esp-4)
  mov call_cur,[temp]			// получаем адрес перехода

  cmp call_cur,base_old			// проверяем, далекий это адрес или нет
  jae @short_jmp			// если нет, пропускаем преобразование
    mov temp,base_new			// в темп новую базу
    sub temp,call_addr			// отнимаем от новой базы, найденый адрес
    add temp,base_old			// и прибавляем полученое смещение к старой базе
    mov call_cur,temp			// перезаписываем найденый адрес преобразованым
  @short_jmp:
  eval "jmp {call_cur}"			// склеиваим нужную ассемблерную команду
  asm table_of_jcc,$RESULT		// ассемблируем инструкцию (добавляем переходник)
  
  /// Debug!!!
  mov eip,addr_cur
  /////////////
  mov temp,table_of_jcc_base		// получаем текущее смещение в таблице переходов
  sub temp,addr_cur			// вычисляем смещение относительно VM келла
  sub temp,5				// корректировка
  mov [addr_cur],E9			// вместо call VM вписываем jmp
  inc addr_cur				// встаем на смещение VM
  mov [addr_cur],temp			// вписываем вместо VM смещения адрес табл. переходов
  dec addr_cur				// \ восстанавливаем
  add table_of_jcc,5			// / указатели
  //////////////////////////////////////////////////////
jmp @find_VM_call
//end @trace_jcc
//////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////
// Процедура восстанавливает эмулируемые команды сравнения (CMP) всего 5 //
///////////////////////////////////////////////////////////////////////////
@trace_cmp_and_jcc:
  
  mov table_of_jcc_base,table_of_jcc		// Запоминаем текущюю базу таблицы переходов
  bp emule_cmp_get_arg1				// Ставим бряк после келла, вычисляющего первый аргумент сравнения
  run						// Запускаем программу
  bc emule_cmp_get_arg1				// Брякаемся и убираем бряк
   cmp eip,emule_cmp_get_arg1			// Проверяем, правильно ли приземлились
   jne @ERR_BP_AT_EMULE_CMP_GET_ARG1_NOT_WORK	// Если нет, генерим ошибку и уходим
  mov arg1_num,eax				// Запоминаем первый аргумент
		
  bp emule_cmp_get_arg2				// Ставим бряк после келла, вычисляющего второй аргумент сравнения
  run						// Запускаем программу
  bc emule_cmp_get_arg2				// Брякаемся и убираем бряк
   cmp eip,emule_cmp_get_arg2			// Проверяем, правильно ли приземлились
   jne @ERR_BP_AT_EMULE_CMP_GET_ARG1_NOT_WORK	// Если нет, генерим ошибку и уходим
  mov arg2_num,eax				// Запоминаем второй аргумент
  bp emule_type_cmp				// Ставим бряк на проверку типа сравнения
  run						// Запускаем программу
  bc emule_type_cmp				// Брякаемся и убираем бряк
    cmp eip,emule_type_cmp			// Проверяем, правильно ли приземлились
    jne @ERR_BP_AT_EMULE_TYPE_CMP_NOT_WORK	// Если нет, генерим ошибку и уходим
  mov type_of_cmp,eax				// Запоминаем тип CMP

 // CMP DWORD PTR DS:[????????],????????
 @case0:
  cmp type_of_cmp,0
  jne @case1
  mov str1,"cmp dword ptr ds:["  
  mov str2,"],"
  mov str3,""

  mov arg1_display,"eax"
  mov emule_cmp_size,2
  cmp arg1_num,0
  je  @0_calc_arg2

  mov arg1_display,"ecx"
  mov emule_cmp_size,2
  cmp arg1_num,1
  je  @0_calc_arg2

  mov arg1_display,"edx"
  mov emule_cmp_size,2
  cmp arg1_num,2
  je  @0_calc_arg2

  mov arg1_display,"ebx"
  mov emule_cmp_size,2
  cmp arg1_num,3
  je  @0_calc_arg2

  mov arg1_display,"esp"
  mov emule_cmp_size,3
  cmp arg1_num,4
  je  @0_calc_arg2

  mov arg1_display,"ebp"
  mov emule_cmp_size,3
  cmp arg1_num,5
  je  @0_calc_arg2

  mov arg1_display,"esi"
  mov emule_cmp_size,2
  cmp arg1_num,6
  je  @0_calc_arg2
  
  mov arg1_display,"edi"
  mov emule_cmp_size,2
  cmp arg1_num,7
  je  @0_calc_arg2

  mov arg1_display,ebp
  mov emule_cmp_size,6
 @0_calc_arg2:
  mov arg2_display,"eax"
  cmp arg2_num,0
  je  @end_switch
  
  mov arg2_display,"ecx"
  cmp arg2_num,1
  je  @end_switch

  mov arg2_display,"edx"
  cmp arg2_num,2
  je  @end_switch

  mov arg2_display,"ebx"
  cmp arg2_num,3
  je  @end_switch

  mov arg2_display,"esp"
  inc emule_cmp_size
  cmp arg2_num,4
  je  @end_switch
  dec emule_cmp_size

  mov arg2_display,"ebp"
  inc emule_cmp_size
  cmp arg2_num,5
  je  @end_switch
  dec emule_cmp_size

  mov arg2_display,"esi"
  cmp arg2_num,6
  je  @end_switch

  mov arg2_display,"edi"
  cmp arg2_num,7
  je  @end_switch

  mov arg2_display,esp
  add arg2_display,10
  mov arg2_display,[arg2_display]
  add emule_cmp_size,4
 
  jmp @end_switch
 // CMP ????????,DWORD PTR DS:[????????]
 @case1:  
  cmp type_of_cmp,1
  jne @case2
  mov str1,"cmp "  
  mov str2,",dword ptr ds:["
  mov str3,"]"

  mov arg1_display,"eax"
  mov emule_cmp_size,2
  cmp arg1_num,0
  je  @1_calc_arg2

  mov arg1_display,"ecx"
  mov emule_cmp_size,2
  cmp arg1_num,1
  je  @1_calc_arg2

  mov arg1_display,"edx"
  mov emule_cmp_size,2
  cmp arg1_num,2
  je  @1_calc_arg2

  mov arg1_display,"ebx"
  mov emule_cmp_size,2
  cmp arg1_num,3
  je  @1_calc_arg2

  mov arg1_display,"esp"
  mov emule_cmp_size,2
  cmp arg1_num,4
  je  @1_calc_arg2

  mov arg1_display,"ebp"
  mov emule_cmp_size,2
  cmp arg1_num,5
  je  @1_calc_arg2

  mov arg1_display,"esi"
  mov emule_cmp_size,2
  cmp arg1_num,6
  je  @1_calc_arg2
  
  mov arg1_display,"edi"
  mov emule_cmp_size,2
  cmp arg1_num,7
  je  @1_calc_arg2

  mov arg1_display,ebp
  mov emule_cmp_size,6
 @1_calc_arg2:
  mov arg2_display,"eax"
  cmp arg2_num,0
  je  @end_switch
  
  mov arg2_display,"ecx"
  cmp arg2_num,1
  je  @end_switch

  mov arg2_display,"edx"
  cmp arg2_num,2
  je  @end_switch

  mov arg2_display,"ebx"
  cmp arg2_num,3
  je  @end_switch

  mov arg2_display,"esp"
  cmp arg2_num,4
  je  @end_switch
  
  mov arg2_display,"ebp"
  cmp arg2_num,5
  je  @end_switch
  
  mov arg2_display,"esi"
  cmp arg2_num,6
  je  @end_switch

  mov arg2_display,"edi"
  cmp arg2_num,7
  je  @end_switch

  mov arg2_display,esp
  add arg2_display,10
  mov arg2_display,[arg2_display]
  add emule_cmp_size,4
  jmp @end_switch
  // CMP BYTE PTR DS:[????????],??
 @case2:
  cmp type_of_cmp,2
  jne @case3
  mov str1,"cmp byte ptr ds:["  
  mov str2,"],"
  mov str3,""

  mov arg1_display,"eax"
  mov emule_cmp_size,2
  cmp arg1_num,0
  je  @2_calc_arg2

  mov arg1_display,"ecx"
  mov emule_cmp_size,2
  cmp arg1_num,1
  je  @2_calc_arg2

  mov arg1_display,"edx"
  mov emule_cmp_size,2
  cmp arg1_num,2
  je  @2_calc_arg2

  mov arg1_display,"ebx"
  mov emule_cmp_size,2
  cmp arg1_num,3
  je  @2_calc_arg2

  mov arg1_display,"esp"
  mov emule_cmp_size,4
  cmp arg1_num,4
  je  @2_calc_arg2

  mov arg1_display,"ebp"
  mov emule_cmp_size,4
  cmp arg1_num,5
  je  @2_calc_arg2

  mov arg1_display,"esi"
  mov emule_cmp_size,2
  cmp arg1_num,6
  je  @2_calc_arg2
  
  mov arg1_display,"edi"
  mov emule_cmp_size,2
  cmp arg1_num,7
  je  @2_calc_arg2

  mov arg1_display,ebp
  mov emule_cmp_size,6
 @2_calc_arg2:
  mov arg2_display,"al"
  cmp arg2_num,0
  je  @end_switch
  
  mov arg2_display,"cl"
  cmp arg2_num,1
  je  @end_switch

  mov arg2_display,"dl"
  cmp arg2_num,2
  je  @end_switch

  mov arg2_display,"bl"
  cmp arg2_num,3
  je  @end_switch

  mov arg2_display,"??"
  cmp arg2_num,4
  je  @ERR_EMULE_ARG
  
  mov arg2_display,"??"
  cmp arg2_num,5
  je  @ERR_EMULE_ARG
  
  mov arg2_display,"??"
  cmp arg2_num,6
  je  @ERR_EMULE_ARG

  mov arg2_display,"??"
  cmp arg2_num,7
  je  @ERR_EMULE_ARG

  mov arg2_display,esp
  add arg2_display,10
  mov arg2_display,[arg2_display]
  add emule_cmp_size,1
  jmp @end_switch
  // CMP ??,BYTE PTR DS:[????????]
 @case3:
  cmp type_of_cmp,3
  jne @case4
  mov str1,"cmp "  
  mov str2,",byte ptr ds:["
  mov str3,"]"

  mov arg1_display,"al"
  mov emule_cmp_size,2
  cmp arg1_num,0
  je  @3_calc_arg2

  mov arg1_display,"cl"
  mov emule_cmp_size,2
  cmp arg1_num,1
  je  @3_calc_arg2

  mov arg1_display,"dl"
  mov emule_cmp_size,2
  cmp arg1_num,2
  je  @3_calc_arg2

  mov arg1_display,"bl"
  mov emule_cmp_size,2
  cmp arg1_num,3
  je  @3_calc_arg2

  mov arg1_display,"??"
  mov emule_cmp_size,2
  cmp arg1_num,4
  je  @3_calc_arg2

  mov arg1_display,"??"
  mov emule_cmp_size,2
  cmp arg1_num,5
  je  @3_calc_arg2

  mov arg1_display,"??"
  mov emule_cmp_size,2
  cmp arg1_num,6
  je  @3_calc_arg2
  
  mov arg1_display,"??"
  mov emule_cmp_size,2
  cmp arg1_num,7
  je  @3_calc_arg2

  mov arg1_display,ebp
  mov emule_cmp_size,6
 @3_calc_arg2:
  mov arg2_display,"eax"
  cmp arg2_num,0
  je  @end_switch
  
  mov arg2_display,"ecx"
  cmp arg2_num,1
  je  @end_switch

  mov arg2_display,"edx"
  cmp arg2_num,2
  je  @end_switch

  mov arg2_display,"ebx"
  cmp arg2_num,3
  je  @end_switch

  mov arg2_display,"esp"
  cmp arg2_num,4
  je  @end_switch
  
  mov arg2_display,"ebp"
  cmp arg2_num,5
  je  @end_switch
  
  mov arg2_display,"esi"
  cmp arg2_num,6
  je  @end_switch

  mov arg2_display,"edi"
  cmp arg2_num,7
  je  @end_switch

  mov arg2_display,esp
  add arg2_display,10
  mov arg2_display,[arg2_display]
  add emule_cmp_size,1
  jmp @end_switch
  // CMP ????????,????????
 @case4:
  cmp type_of_cmp,4
  jne @ERR_UNKNOWN_TYPE_CMP
  mov str1,"cmp "  
  mov str2,","
  mov str3,""

  mov arg1_display,"eax"
  mov emule_cmp_size,2
  cmp arg1_num,0
  je  @4_calc_arg2

  mov arg1_display,"ecx"
  mov emule_cmp_size,2
  cmp arg1_num,1
  je  @4_calc_arg2

  mov arg1_display,"edx"
  mov emule_cmp_size,2
  cmp arg1_num,2
  je  @4_calc_arg2

  mov arg1_display,"ebx"
  mov emule_cmp_size,2
  cmp arg1_num,3
  je  @4_calc_arg2

  mov arg1_display,"esp"
  mov emule_cmp_size,2
  cmp arg1_num,4
  je  @4_calc_arg2

  mov arg1_display,"ebp"
  mov emule_cmp_size,2
  cmp arg1_num,5
  je  @4_calc_arg2

  mov arg1_display,"esi"
  mov emule_cmp_size,2
  cmp arg1_num,6
  je  @4_calc_arg2
  
  mov arg1_display,"edi"
  mov emule_cmp_size,2
  cmp arg1_num,7
  je  @4_calc_arg2

  mov arg1_display,ebp
  mov emule_cmp_size,5
 @4_calc_arg2:
  mov arg2_display,"eax"
  cmp arg2_num,0
  je  @end_switch
  
  mov arg2_display,"ecx"
  cmp arg2_num,1
  je  @end_switch

  mov arg2_display,"edx"
  cmp arg2_num,2
  je  @end_switch

  mov arg2_display,"ebx"
  cmp arg2_num,3
  je  @end_switch

  mov arg2_display,"esp"
  cmp arg2_num,4
  je  @end_switch
  
  mov arg2_display,"ebp"
  cmp arg2_num,5
  je  @end_switch
  
  mov arg2_display,"esi"
  cmp arg2_num,6
  je  @end_switch

  mov arg2_display,"edi"
  cmp arg2_num,7
  je  @end_switch

  mov arg2_display,esp
  add arg2_display,10
  mov arg2_display,[arg2_display]
  add emule_cmp_size,4
 
  jmp @end_switch
//////////////////////////////////////////////////////////////////////////////////////
 @end_switch:
  eval "{str1}{arg1_display}{str2}{arg2_display}{str3}"
  asm table_of_jcc,$RESULT
  add table_of_jcc,emule_cmp_size
  ///////////////
  mov ebp,00400000
  add esp,10
  mov [esp],00400000
  sub esp,10
  
  mov emule_type_jcc_temp,emule_cmp_type_jcc
  mov emule_jcc_temp,emule_cmp_jcc
  jmp @trace_jcc
  ///////////////
  pause  

@end:
mov eip,oep
log "____________________________________"
ret

@ERR_BP_AT_SWICH_NOT_WORK:
msg "[Error!] BreakPoint at emule_swich not work!"
jmp @end

@ERR_BP_AT_EMULE_LAST_JAMP_NOT_WORK:
msg "[Error!] BreakPoint at emule_last_jamp not work!"
jmp @end

@ERR_BP_AT_EMULE_TYPE_JCC_NOT_WORK:
msg "[Error!] BreakPoint at emule_type_jcc not work!"
jmp @end

@ERR_BP_AT_EMULE_JCC_NOT_WORK:
msg "[Error!] BreakPoint at emule_jcc not work!"
jmp @end

@ERR_BP_AT_EMULE_CMP_GET_ARG1_NOT_WORK:
msg "[Error!] BreakPoint at emule_cmp_get_argX not work!"
jmp @end

@ERR_BP_AT_EMULE_TYPE_CMP_NOT_WORK:
msg "[Error!] BreakPoint at emule_type_cmp not work!"
jmp @end

@ERR_BP_AT_EMULE_CMP_NOT_WORK:
msg "[Error!] BreakPoint at emule_cmp not work!"
jmp @end

@ERR_BP_AT_EMULE_CMP_TYPE_JCC_NOT_WORK:
msg "[Error!] BreakPoint at emule_cmp_type_jcc not work!"
jmp @end

@ERR_EMULE_LAST_JAMP_NOT_FOUND:
msg "[Error!] Command 'JMP DWORD PTR SS:[ESP-4]' not found!"
jmp @end

@ERR_VM_CALL_NOT_FOUND:
msg "[Error!] VM call not found!"
jmp @end

@ERR_INIT_FAILED:
msg "[Error!] Init failed!"
jmp @end

@ERR_EMULE_ARG:
msg "[Error!] Register not found!"
jmp @end

@ERR_UNKNOWN_TYPE_CMP:
msg "[Error!] Unknown type of cmp instruction!"
jmp @end