Когда потребовалось написать модуль для сплайсинга определенных функций, и вот для него потребовался дизассемблер длин инструкций. В принципе функции которые надо было сплайсить не юзали mmx и sse а только x86 команды. Когда начал искать диасм длин, то находил довольно объемный, а иногда и стрёмный код, к томуже содержащий не нужный баласт в роле примочек связанных с mmx. При этом был найден довольно маленький диасм длин написанный на ассемблере. И так вот: за основу был взят VirXasm32 v1.5b (X) Malum 2006, код был портирован под Си (для MS Visual C++) в виде асм вставки и функции обертки для более удобного использования. Кстати, код поддерживает и MMX инструкции вроде как. потому что нормально определял их длинну Выкладываю сюда, может комунить пригодиться. Предоставляются 2 функции 1) int MDAL_GetOpcodesLenByNeedLen(BYTE* opcode, int NeedLen); На входе: * адрес начала кода * минимальная требуемая длинна. На выходе - На выходе: кол-во байт больше минимально требуемого, но составляющих целое кол-во инструкций. Или 0 - ошибка. 2) int _cdecl MDAL_GetOpcodeLen(BYTE* opcode); На входе: адрес кода На выходе: длинна первой инструкции. 0 = ошибка Вот непосредственно код: Code: #include <windows.h> #define _SALC 0xD6 #define _AAM 0xD4 #define NRM_TAB_LEN 53 #define DB __asm _emit int MDAL_GetOpcodesLenByNeedLen(BYTE* opcode, int NeedLen) { int FullLen = 0; int len; do { len = MDAL_GetOpcodeLen(opcode + FullLen); if (!len) { return 0; } FullLen += len; } while (FullLen < NeedLen); return FullLen; } __declspec(naked) int _cdecl MDAL_GetOpcodeLen(BYTE* opcode) { _asm { mov esi, [esp + 4] pushad push 000001510h push 0100101FFh push 0FFFFFF55h push 0FFFFFFF8h push 0F8FF7FA0h push 00F0EC40Dh push 007551004h push 001D005FFh push 0550D5D55h push 0555F0F88h push 0F3F3FFFFh push 00A0C1154h mov edx, esi mov esi, esp push 11001b push 10110000101011000000101110000000b push 10111111101100011111001100111110b push 00000000000100011110101001011000b mov ebx, esp sub esp, 110 mov edi, esp cld push 100 pop ecx xa_nxtIndx: bt [ebx], ecx DB _SALC jnc xa_is0 lodsb xa_is0: stosb loop xa_nxtIndx mov esi, edx push 2 pop ebx mov edx, ebx xa_NxtByte: lodsb push eax push eax cmp al, 66h cmove ebx, ecx cmp al, 67h cmove edx, ecx cmp al, 0EAh je xa_jmp cmp al, 09Ah jne xa_nocall inc esi xa_jmp: lea esi, [esi+ebx+3] xa_nocall: cmp al, 0C8h je xa_i16 and al, 0F7h cmp al, 0C2h jne xa_no16 xa_i16: inc esi inc esi xa_no16: and al, 0E7h cmp al, 26h pop eax je xa_PopNxt cmp al, 0F1h je xa_F1 and al, 0FCh cmp al, 0A0h jne xa_noMOV lea esi, [esi+edx+2] xa_noMOV: cmp al, 0F0h je xa_PopNxt xa_F1: cmp al, 64h xa_PopNxt: pop eax je xa_NxtByte mov edi, esp push edx push eax cmp al, 0Fh jne xa_Nrm lodsb xa_Nrm: pushfd DB _AAM DB 10h xchg cl, ah cwde cdq xor ebp, ebp popfd jne xa_NrmGroup add edi, NRM_TAB_LEN jecxz xa_3 xa_1: bt [edi], ebp jnc xa_2 inc edx xa_2: inc ebp loop xa_1 jc xa_3 DB _SALC cdq xa_3: shl edx, 1 jmp xa_ProcOpcode xa_NrmGroup: sub cl, 4 jns xa_4 mov cl, 0Ch and al, 7 xa_4: jecxz xa_4x xa_5: adc dl, 1 inc ebp bt [edi], ebp loop xa_5 jc xa_ProcOpcode xa_4x: shr al, 1 xa_ProcOpcode: xchg cl, al lea edx, [edx*8+ecx] pop ecx pop ebp bt [edi+2], edx jnc xa_noModRM lodsb DB _AAM DB 8 shl ah, 4 jnc xa_isModRM js xa_enModRM xa_isModRM: pushfd test ebp, ebp jnz xa_addr32 sub al, 6 jnz xa_noSIB mov al, 5 xa_addr32: cmp al, 4 jne xa_noSIB lodsb and al, 7 xa_noSIB: popfd jc xa_iWD js xa_i8 cmp al, 5 jne xa_enModRM xa_iWD: add esi, ebp inc esi xa_i8: inc esi xa_enModRM: test ah, 60h jnz xa_noModRM xchg eax, ecx cmp al, 0F6h je xa_ti8 cmp al, 0F7h jne xa_noModRM add esi, ebx inc esi xa_ti8: inc esi xa_noModRM: shl edx, 1 bt [edi+2+17], edx jnc xa_Exit inc edx bt [edi+2+17], edx jnc xa_im8 adc esi, ebx xa_im8: inc esi xa_Exit: add esp, 110+64 sub esi, [esp+4] mov [esp+7*4], esi popad ret } } Размер непосредственно функции подсчета длинны текущей команды - 352 байта. Так что очень удобно юзать во всяком малваре, без особого увеличения размера Для особых извращенцев в аттаче лежит готовая DLL (1024 байта размер). Экспортирующая функцию int __stdcall GetOpcodeLen(unsigned char * opcode, int NeedLen); где 1) opcode - адрес начала кода 2) NeedLen - минимальное кол-во байт которое должно быть. Если поставить 0, то функция вернет длину первой команды. 3) функция возвращает или длину инструкции/й или 0 в случае ошибки В делфи можно юзать так: function GetOpcodeLen(Opcode : pointer; NeedLen : integer):integer; stdcall; external 'opclen.dll' name 'GetOpcodeLen';
2slesh с /O1 esi сбивается после вызова MDAL_GetOpcodeLen у меня в црт вызывались конструкторы а в них ставились хуки при ините проги а там как раз цикл пробегается по массиву указателей после вызова первого остальные отдыхали если оптимизацию включал Code: void f0() { /* вот тут без оптимизации спасает что компиль сохраняет сам esi */ call MDAL_GetOpcodeLen /* после вызова он сбит но в дебаге восстанавливается */ } voif f1() { /* before use esi */ call f0(); /* after use bad esi тут глючит код с /O1 скомпиленный */ } я так изменил и юзаю void f0() { __asm { push esi ... call MDAL_GetOpcodeLen ... pop esi } }
о а ща по ситуации плюсую, вообщем в базонезависимом шеллкоде нужен сплайс, я сперва подумал, один хрен почти везде в апи mov edi,edi push ebp mov ebp,esp; но для универсальности добавлю, грац!