14.11.2010, 23:25
Без проблем.
Ошибка по адресу 0x004417A4 (MOV EAX, [ESI+34h]) - попытка прочесть память по адресу 34h.
Ещё - отклонение, вызвавшее, скорее всего, эту ошибку - неправильная работа функции sub_5242E0, вызванной по адресу 0x00440064. Там идёт работа со стеком напрямую и извлекается неверное значение (по адресу 0x0052430F (MOV EBX, [EBP+arg_0]; arg_0 = 8])).
Но фишка-то в том, что всё это только при отладке. Просто в игре ничего такого нет.
P.S. Без включенного режима немодифицируемости (отключить - 32-й бит переменной v9501 = 1) ошибка будет в любом случае (правда, разная в игре и отладчике), но с этим я как-нибудь сам разберусь.
Код: (Click to View)
LIBRARY HookDamage;
{!INFO
MODULENAME = 'Damage'
VERSION = '1.0'
AUTHOR = 'Sav'
}
USES Win, Utils, SysUtils, VPUtils;
CONST
(* HookCode constants *)
C_HOOKTYPE_JUMP = FALSE;
C_HOOKTYPE_CALL = TRUE;
C_OPCODE_JUMP = $E9;
C_OPCODE_CALL = $E8;
C_UNIHOOK_SIZE = 5;
TYPE
THookRec = RECORD
Opcode: BYTE;
Ofs: INTEGER;
END;
VAR
Temp: INTEGER;
PROCEDURE WriteAtCode(P: POINTER; Buf: POINTER; Count: INTEGER);
BEGIN
Win.VirtualProtect(P, Count, PAGE_READWRITE, @Temp);
Win.CopyMemory(P, Buf, Count);
Win.VirtualProtect(P, Count, Temp, NIL);
END;
PROCEDURE HookCode(P: POINTER; NewAddr: POINTER; UseCall: BOOLEAN);
VAR
HookRec: THookRec;
BEGIN
IF UseCall THEN BEGIN
HookRec.Opcode:=C_OPCODE_CALL;
END // .if
ELSE BEGIN
HookRec.Opcode:=C_OPCODE_JUMP;
END; // .else
HookRec.Ofs:=INTEGER(NewAddr)-INTEGER(P)-C_UNIHOOK_SIZE;
WriteAtCode(P, @HookRec, 5);
END;
//Модификация удара с флагом бить всех вокруг.
//Вместо вычисления 8-ми атакованных позиций, проверяется каждый из 42 стеков на необходимость атаки по нему (записанную в v-переменных).
//$890AD8 - v9501 - номера целевых стеков атакующего (побитно), $890ADC - v9502 - номера целевых стеков защищающегося (побитно).
//Модификация условия выполнения прохода цикла (в функции $440030).
//Запись 0 (что значит - все позиции вокруг могут быть атакованы).
//Без модификации значение - начение - смежные позиций побитно от правой нижней против часовой стрелки (0 - доступна).
PROCEDURE Damage_ability; ASSEMBLER; {$FRAME-}
ASM
//Если 32-й бит в v9501 = 1, модифицируем.
PUSH EAX
MOV EAX, DWORD PTR DS:[$890AD8]
SHR EAX, 31
TEST EAX, EAX
POP EAX
JNZ @@Modify
//Восстановление изначального кода: получение всех позиций рядом, на которых кто-то стоит побитно (Запись в ECX).
MOV ECX, [EBP+8]
JMP @@Exit
//Модифицирование кода: запись 0 (будто на всех клетках рядом кто-то стоит) в возвращаемый результат.
@@Modify:
MOV ECX, 0
@@Exit:
TEST ECX, EAX//Восстанавливаем испорченную команду.
PUSH $440057
END;
//Модификация вызова функции, вычисляющей позицию текущего атакованного (в функции $440030).
//Автоматическое возвращение 1 (легальной позиции) (в EAX), т. к. позиция может быть вычислена неправильно, если будет больше 8 атакованных.
PROCEDURE Damage_position; ASSEMBLER; {$FRAME-}
ASM
//Если 32-й бит в v9501 = 1, модифицируем.
MOV EAX, DWORD PTR DS:[$890AD8]
SHR EAX, 31
TEST EAX, EAX
JNZ @@Modify
//Восстановление изначального кода: вызов функции, вычисляющей позицию текущего атакованного (Запись в EAX).
MOV EAX, $5242E0
CALL EAX//Внутри функции используется ESP - нельзя использовать PUSH перед ней.
JMP @@Exit
//Модифицирование кода: запись 1 (легальной позиции) в возвращаемый результат.
@@Modify:
MOV EAX, 1
@@Exit:
PUSH $440069
END;
//Модификация вызова функции вычисления адреса конструкций для атакованного на определённой позиции (в функции $440030).
//Вместо вычисления стека по позиции, смотрим в бите v-переменной, надо ли атаковать текущий проверяемый стек.
//Если надо, возвращаем (в EAX) $699420 + 1352*номер стека + 21708, иначе 0.
PROCEDURE Damage_stack; ASSEMBLER; {$FRAME-}
ASM
//Если 32-й бит в v9501 = 1, модифицируем.
MOV EAX, DWORD PTR DS:[$890AD8]
SHR EAX, 31
TEST EAX, EAX
JNZ @@Modify
//Восстановление изначального кода: вызов функции, вычисляющей адрес конструкций для атакованного на определённой позиции (запись в EAX).
MOV EAX, $4E7230
CALL EAX
JMP @@Exit
//Модифицирование кода: проверка, надо ли атаковать текущий стек и запись адреса его конструкций или 0 в EAX.
@@Modify:
//Проверяем, какой стороне принадлежит проверяемый стек.
CMP EBX, 21
JAE @@Def_st
//Если стек принадлежит атакующей стороне, помещаем в EAX соответствующий стеку бит из v9501.
MOV EAX, DWORD PTR DS:[$890AD8]
MOV CL, BL
SHR EAX, CL
JMP @@All_st
//Если стек принадлежит защищающейся стороне, помещаем в EAX соответствующий стеку бит из v9502.
@@Def_st:
MOV EAX, DWORD PTR DS:[$890ADC]
SUB EBX, 21
MOV CL, BL
SHR EAX, CL
ADD EBX, 21
//Оставляем в EAX 0 или помещаем в него адрес конструкций стека (в зависимости от начального значкния EAX, т. е. бита v-переменной).
@@All_st:
AND EAX, 1
JZ @@Exit
//EAX = $699420 + 1352*EBX + 21708.
LEA ECX, DS:0[EBX*8]
SUB ECX, EBX
LEA EDX, [ECX+ECX*2]
MOV ECX, DWORD PTR DS:[$699420]
LEA EAX, [EBX+EDX*8]
LEA EAX, [ECX+EAX*8+21708]
@@Exit:
PUSH $44009A
END;
//Модификация условия выхода из цикла (в функции $440030).
//42 прохода (для каждого стека) вместо стандартных 8 (для каждой возможной позиции около атакующего).
PROCEDURE Damage_target; ASSEMBLER; {$FRAME-}
ASM
PUSH EAX
//Если 32-й бит в v9501 = 1, модифицируем.
MOV EAX, DWORD PTR DS:[$890AD8]
SHR EAX, 31
TEST EAX, EAX
JNZ @@Modify
//Восстановление изначального кода: сравнивание номера текущего атакованного с 8.
CMP EBX, 8
JMP @@Exit
//Модифицирование кода: сравнивание номера текущего атакованного с 42.
@@Modify:
CMP EBX, 42
@@Exit:
POP EAX
MOV [EBP-12], EBX//Восстанавливаем испорченную команду.
PUSH $44019A
END;
BEGIN
HookCode(POINTER($440052), @Damage_ability, C_HOOKTYPE_JUMP);
HookCode(POINTER($440064), @Damage_position, C_HOOKTYPE_JUMP);
HookCode(POINTER($440095), @Damage_stack, C_HOOKTYPE_JUMP);
HookCode(POINTER($440194), @Damage_target, C_HOOKTYPE_JUMP);
END.
{!INFO
MODULENAME = 'Damage'
VERSION = '1.0'
AUTHOR = 'Sav'
}
USES Win, Utils, SysUtils, VPUtils;
CONST
(* HookCode constants *)
C_HOOKTYPE_JUMP = FALSE;
C_HOOKTYPE_CALL = TRUE;
C_OPCODE_JUMP = $E9;
C_OPCODE_CALL = $E8;
C_UNIHOOK_SIZE = 5;
TYPE
THookRec = RECORD
Opcode: BYTE;
Ofs: INTEGER;
END;
VAR
Temp: INTEGER;
PROCEDURE WriteAtCode(P: POINTER; Buf: POINTER; Count: INTEGER);
BEGIN
Win.VirtualProtect(P, Count, PAGE_READWRITE, @Temp);
Win.CopyMemory(P, Buf, Count);
Win.VirtualProtect(P, Count, Temp, NIL);
END;
PROCEDURE HookCode(P: POINTER; NewAddr: POINTER; UseCall: BOOLEAN);
VAR
HookRec: THookRec;
BEGIN
IF UseCall THEN BEGIN
HookRec.Opcode:=C_OPCODE_CALL;
END // .if
ELSE BEGIN
HookRec.Opcode:=C_OPCODE_JUMP;
END; // .else
HookRec.Ofs:=INTEGER(NewAddr)-INTEGER(P)-C_UNIHOOK_SIZE;
WriteAtCode(P, @HookRec, 5);
END;
//Модификация удара с флагом бить всех вокруг.
//Вместо вычисления 8-ми атакованных позиций, проверяется каждый из 42 стеков на необходимость атаки по нему (записанную в v-переменных).
//$890AD8 - v9501 - номера целевых стеков атакующего (побитно), $890ADC - v9502 - номера целевых стеков защищающегося (побитно).
//Модификация условия выполнения прохода цикла (в функции $440030).
//Запись 0 (что значит - все позиции вокруг могут быть атакованы).
//Без модификации значение - начение - смежные позиций побитно от правой нижней против часовой стрелки (0 - доступна).
PROCEDURE Damage_ability; ASSEMBLER; {$FRAME-}
ASM
//Если 32-й бит в v9501 = 1, модифицируем.
PUSH EAX
MOV EAX, DWORD PTR DS:[$890AD8]
SHR EAX, 31
TEST EAX, EAX
POP EAX
JNZ @@Modify
//Восстановление изначального кода: получение всех позиций рядом, на которых кто-то стоит побитно (Запись в ECX).
MOV ECX, [EBP+8]
JMP @@Exit
//Модифицирование кода: запись 0 (будто на всех клетках рядом кто-то стоит) в возвращаемый результат.
@@Modify:
MOV ECX, 0
@@Exit:
TEST ECX, EAX//Восстанавливаем испорченную команду.
PUSH $440057
END;
//Модификация вызова функции, вычисляющей позицию текущего атакованного (в функции $440030).
//Автоматическое возвращение 1 (легальной позиции) (в EAX), т. к. позиция может быть вычислена неправильно, если будет больше 8 атакованных.
PROCEDURE Damage_position; ASSEMBLER; {$FRAME-}
ASM
//Если 32-й бит в v9501 = 1, модифицируем.
MOV EAX, DWORD PTR DS:[$890AD8]
SHR EAX, 31
TEST EAX, EAX
JNZ @@Modify
//Восстановление изначального кода: вызов функции, вычисляющей позицию текущего атакованного (Запись в EAX).
MOV EAX, $5242E0
CALL EAX//Внутри функции используется ESP - нельзя использовать PUSH перед ней.
JMP @@Exit
//Модифицирование кода: запись 1 (легальной позиции) в возвращаемый результат.
@@Modify:
MOV EAX, 1
@@Exit:
PUSH $440069
END;
//Модификация вызова функции вычисления адреса конструкций для атакованного на определённой позиции (в функции $440030).
//Вместо вычисления стека по позиции, смотрим в бите v-переменной, надо ли атаковать текущий проверяемый стек.
//Если надо, возвращаем (в EAX) $699420 + 1352*номер стека + 21708, иначе 0.
PROCEDURE Damage_stack; ASSEMBLER; {$FRAME-}
ASM
//Если 32-й бит в v9501 = 1, модифицируем.
MOV EAX, DWORD PTR DS:[$890AD8]
SHR EAX, 31
TEST EAX, EAX
JNZ @@Modify
//Восстановление изначального кода: вызов функции, вычисляющей адрес конструкций для атакованного на определённой позиции (запись в EAX).
MOV EAX, $4E7230
CALL EAX
JMP @@Exit
//Модифицирование кода: проверка, надо ли атаковать текущий стек и запись адреса его конструкций или 0 в EAX.
@@Modify:
//Проверяем, какой стороне принадлежит проверяемый стек.
CMP EBX, 21
JAE @@Def_st
//Если стек принадлежит атакующей стороне, помещаем в EAX соответствующий стеку бит из v9501.
MOV EAX, DWORD PTR DS:[$890AD8]
MOV CL, BL
SHR EAX, CL
JMP @@All_st
//Если стек принадлежит защищающейся стороне, помещаем в EAX соответствующий стеку бит из v9502.
@@Def_st:
MOV EAX, DWORD PTR DS:[$890ADC]
SUB EBX, 21
MOV CL, BL
SHR EAX, CL
ADD EBX, 21
//Оставляем в EAX 0 или помещаем в него адрес конструкций стека (в зависимости от начального значкния EAX, т. е. бита v-переменной).
@@All_st:
AND EAX, 1
JZ @@Exit
//EAX = $699420 + 1352*EBX + 21708.
LEA ECX, DS:0[EBX*8]
SUB ECX, EBX
LEA EDX, [ECX+ECX*2]
MOV ECX, DWORD PTR DS:[$699420]
LEA EAX, [EBX+EDX*8]
LEA EAX, [ECX+EAX*8+21708]
@@Exit:
PUSH $44009A
END;
//Модификация условия выхода из цикла (в функции $440030).
//42 прохода (для каждого стека) вместо стандартных 8 (для каждой возможной позиции около атакующего).
PROCEDURE Damage_target; ASSEMBLER; {$FRAME-}
ASM
PUSH EAX
//Если 32-й бит в v9501 = 1, модифицируем.
MOV EAX, DWORD PTR DS:[$890AD8]
SHR EAX, 31
TEST EAX, EAX
JNZ @@Modify
//Восстановление изначального кода: сравнивание номера текущего атакованного с 8.
CMP EBX, 8
JMP @@Exit
//Модифицирование кода: сравнивание номера текущего атакованного с 42.
@@Modify:
CMP EBX, 42
@@Exit:
POP EAX
MOV [EBP-12], EBX//Восстанавливаем испорченную команду.
PUSH $44019A
END;
BEGIN
HookCode(POINTER($440052), @Damage_ability, C_HOOKTYPE_JUMP);
HookCode(POINTER($440064), @Damage_position, C_HOOKTYPE_JUMP);
HookCode(POINTER($440095), @Damage_stack, C_HOOKTYPE_JUMP);
HookCode(POINTER($440194), @Damage_target, C_HOOKTYPE_JUMP);
END.
Ещё - отклонение, вызвавшее, скорее всего, эту ошибку - неправильная работа функции sub_5242E0, вызванной по адресу 0x00440064. Там идёт работа со стеком напрямую и извлекается неверное значение (по адресу 0x0052430F (MOV EBX, [EBP+arg_0]; arg_0 = 8])).
Но фишка-то в том, что всё это только при отладке. Просто в игре ничего такого нет.
P.S. Без включенного режима немодифицируемости (отключить - 32-й бит переменной v9501 = 1) ошибка будет в любом случае (правда, разная в игре и отладчике), но с этим я как-нибудь сам разберусь.