Перейти к содержанию

Пишем D3DX9 хак на NASM для Divinity Dragon Commander


Рекомендуемые сообщения

Некоторые особенности:

1 PE32 DLL 4.5 кб
2 Функция поиска паттерна
3 Паттерны текст вида
4 Функция обмена патч байтами
5 Если версия не совпадает - MessageBoxW и FreeLibrary
6 Клавиши работают только в активном окне игры
7 Меню и клавиши используют поток игры
8 Рисовка на базе D3DXCreateFontW->DrawTextW
9 100% unicode

 

NASM: https://www.nasm.us -> nasm-2.xx.xx-win64.zip -> nasm.exe
Linker: http://www.godevtool.com/Golink.zip -> golink.exe
Редактор: https://notepad-plus-plus.org

 

Приступим...
Создадим txt файл и переименуем в "dragon commander hack.asm"

Открываем файл в notepad++

 

Добавим EntryPoint, структуры, импорт апи

Скрытый текст
global DllMain

struc RECT
.left resd 1
.top resd 1
.right resd 1
.bottom resd 1
endstruc

;kernel32.dll
extern DisableThreadLibraryCalls
extern CreateThread
extern CloseHandle
extern GetModuleHandleW
extern VirtualProtect
extern FreeLibrary

;user32.dll
extern GetFocus
extern GetAsyncKeyState
extern GetClientRect
extern MessageBoxW

;d3dx9_43.dll
extern D3DXCreateFontW

 

 

Создадим секцию кода

Скрытый текст
section .code

 

Добавим функцию DllMain, resd 1 - для пропуска адреса возврата а ret 3*4 значит что у функции 3 параметра.

Скрытый текст
DllMain:
struc .stack
resd 1
.hInstDLL resd 1
.Reason resd 1
.Reserved resd 1
endstruc
cmp dword[esp+.Reason],1
jnz .DLL_PROCESS_ATTACH
push dword[esp+.hInstDLL]
call [DisableThreadLibraryCalls]
push 0
push 0
push dword[esp+.hInstDLL+2*4]
push Init
push 0x1000
push 0
call [CreateThread]
push eax
call [CloseHandle]
mov al,1
.DLL_PROCESS_ATTACH:
ret 3*4

 

 

Добавляем функцию инициализации, получаем базу/размер модулей из PE хидера, поиск патч опций, DX9Binding.dll там будем ставить хуки для менюхи.

Скрытый текст
Init:
struc .stack
resd 3
.hInstDLL resd 1
endstruc
push esi
push edi

push 0
call [GetModuleHandleW]
mov esi,eax
mov edi,[esi+60] ;IMAGE_DOS_HEADER.e_lfanew
mov edi,[esi+edi+80] ;IMAGE_NT_HEADERS32.OptionalHeader+IMAGE_OPTIONAL_HEADER32.SizeOfImage

push pAddGold1
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aAddGold1],eax
sub [bAddGold1+1],eax

push pAddGold2
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aAddGold2],eax
sub [bAddGold2+1],eax

push pAddResearchPoints
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aAddResearchPoints],eax
sub [bAddResearchPoints+1],eax

push pInstantCallDragon
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aInstantCallDragon],eax

push pDragonHealth1
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aDragonHealth1],eax

push pDragonHealth2
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aDragonHealth2],eax

push pDragonEnergy
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aDragonEnergy],eax

push pWeaponNoOverheat
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aWeaponNoOverheat],eax

push pWeaponRapidFire
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aWeaponRapidFire],eax

push pSkillNoCoolDown
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aSkillNoCoolDown],eax

push DX9BindingDLL
call [GetModuleHandleW]
test eax,eax
jz .NotFound
mov esi,eax
mov edi,[esi+60] ;IMAGE_DOS_HEADER.e_lfanew
mov edi,[esi+edi+80] ;IMAGE_NT_HEADERS32.OptionalHeader+IMAGE_OPTIONAL_HEADER32.SizeOfImage

push pD3DEndFrame
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aD3DEndFrame],eax
sub [bD3DEndFrame+1],eax

push pD3DresetDevice
push edi
push esi
call FindPattern
test eax,eax
jz .NotFound
mov [aD3DresetDevice],eax
sub [bD3DresetDevice+1],eax

push bD3DEndFrame.size
push bD3DEndFrame
push dword[aD3DEndFrame]
call MemSwap
push bD3DresetDevice.size
push bD3DresetDevice
push dword[aD3DresetDevice]
call MemSwap

pop edi
pop esi
ret 1*4
.NotFound:
push 0x10+0x10000 ;MB_ICONERROR+MB_SETFOREGROUND
push MsgTitle
push MsgVersionUnsupported
push 0
call [MessageBoxW]
pop edi
pop esi
jmp [FreeLibrary]

 

 

 Добавляем функцию поиска. Она не особо сложная, быстрая и может пропускать мусорные байты. Принимает паттерны текст вида.

Скрытый текст
FindPattern:
struc .stack
resd 4
.Buffer resd 1
.Size resd 1
.Pattern resd 1
endstruc
push ebx
push esi
push edi
mov eax,[esp+.Buffer]
mov ecx,[esp+.Size]
add ecx,eax
mov edx,[esp+.Pattern]
.Next:
lea esi,[eax-1]
lea edi,[edx-2]
.Check:
add edi,2
mov bx,[edi]
test bl,bl
jz .Found
add esi,1
cmp ecx,esi
jz .NotFound
cmp bx,'??'
jz .Check
sub bx,'00'
cmp bh,10
jb .Skip1
sub bh,7
.Skip1:
cmp bl,10
jb .Skip2
sub bl,7
.Skip2:
shl bl,4
add bl,bh
cmp [esi],bl
jz .Check
add eax,1
jmp .Next
.NotFound:
xor eax,eax
.Found:
pop edi
pop esi
pop ebx
ret 3*4

 

 

и функцию обмена байтами, позволяет не копировать оригинал код для откл опций.

Скрытый текст
MemSwap:
struc .stack
.OldProtect resd 1
.stacksize equ $-$$
resd 2
.Address resd 1
.Patch resd 1
.Size resd 1
endstruc
push ebx
sub esp,.stacksize
lea eax,[esp+.OldProtect]
push eax
push 0x40 ;PAGE_EXECUTE_READWRITE
push dword[esp+.Size+2*4]
push dword[esp+.Address+3*4]
call [VirtualProtect]
mov eax,[esp+.Address]
mov ecx,[esp+.Patch]
mov edx,[esp+.Size]
.Next:
sub edx,1
mov bh,[eax+edx]
mov bl,[ecx+edx]
mov [eax+edx],bl
mov [ecx+edx],bh
jnz .Next
lea eax,[esp+.OldProtect]
push eax
push dword[esp+.OldProtect+1*4]
push dword[esp+.Size+2*4]
push dword[esp+.Address+3*4]
call [VirtualProtect]
add esp,.stacksize
pop ebx
ret 3*4

 

 

Теперь создадим функцию меню.

Скрытый текст
MenuHook:
struc .stack
.D3D_Device resd 1
.Rect resb RECT_size
alignb 4
endstruc
push ebp
push esi
push edi
sub esp,.stack_size

 

 

Место где установили перехват - достает D3D_Device, нам он нужен для D3DXCreateFontW, сохраним его, а потом вернем игре.

Скрытый текст
mov eax,[eax+0x138] ;device
mov [esp+.D3D_Device],eax

 

Добавим код для проверки активности окна. Также код используется для получения hGameWnd, следовательно, после инжекта, меню нарисуется только после клика в окно игры.

Скрытый текст
call [GetFocus]
cmp dword[hGameWnd],0
jnz .FirstClick
mov [hGameWnd],eax
jmp .SkipDraw
.FirstClick:
cmp [hGameWnd],eax
jnz .SkipKeys

 

 

Добавим кнопку скрыть/показать меню. cmp + setz = 0/1 aka откл/вкл

Скрытый текст
push 0x2D ;VK_INSERT
call [GetAsyncKeyState]
cmp [kMenu+1],ah
jz .Menu
mov [kMenu+1],ah
test ah,ah
jz .Menu
cmp byte[kMenu],0
setz [kMenu]
.Menu:
cmp byte[kMenu],0
jnz .SkipDraw

 

 

Добавляем все опции...

Скрытый текст
push 0x61 ;VK_NUMPAD1
call [GetAsyncKeyState]
cmp [kAddGold+1],ah
jz .AddGold
mov [kAddGold+1],ah
test ah,ah
jz .AddGold
cmp byte[kAddGold],0
setz [kAddGold]
push bAddGold1.size
push bAddGold1
push dword[aAddGold1]
call MemSwap
push bAddGold2.size
push bAddGold2
push dword[aAddGold2]
call MemSwap
.AddGold:

push 0x62 ;VK_NUMPAD2
call [GetAsyncKeyState]
cmp [kAddResearchPoints+1],ah
jz .AddResearchPoints
mov [kAddResearchPoints+1],ah
test ah,ah
jz .AddResearchPoints
cmp byte[kAddResearchPoints],0
setz [kAddResearchPoints]
push bAddResearchPoints.size
push bAddResearchPoints
push dword[aAddResearchPoints]
call MemSwap
.AddResearchPoints:

push 0x63 ;VK_NUMPAD3
call [GetAsyncKeyState]
cmp [kInstantCallDragon+1],ah
jz .InstantCallDragon
mov [kInstantCallDragon+1],ah
test ah,ah
jz .InstantCallDragon
cmp byte[kInstantCallDragon],0
setz [kInstantCallDragon]
push bInstantCallDragon.size
push bInstantCallDragon
push dword[aInstantCallDragon]
call MemSwap
.InstantCallDragon:

push 0x64 ;VK_NUMPAD4
call [GetAsyncKeyState]
cmp [kDragonHealth+1],ah
jz .DragonHealth
mov [kDragonHealth+1],ah
test ah,ah
jz .DragonHealth
cmp byte[kDragonHealth],0
setz [kDragonHealth]
push bDragonHealth1.size
push bDragonHealth1
push dword[aDragonHealth1]
call MemSwap
push bDragonHealth2.size
push bDragonHealth2
push dword[aDragonHealth2]
call MemSwap
.DragonHealth:

push 0x65 ;VK_NUMPAD5
call [GetAsyncKeyState]
cmp [kDragonEnergy+1],ah
jz .DragonEnergy
mov [kDragonEnergy+1],ah
test ah,ah
jz .DragonEnergy
cmp byte[kDragonEnergy],0
setz [kDragonEnergy]
push bDragonEnergy.size
push bDragonEnergy
push dword[aDragonEnergy]
call MemSwap
.DragonEnergy:

push 0x66 ;VK_NUMPAD6
call [GetAsyncKeyState]
cmp [kWeaponNoOverheat+1],ah
jz .WeaponNoOverheat
mov [kWeaponNoOverheat+1],ah
test ah,ah
jz .WeaponNoOverheat
cmp byte[kWeaponNoOverheat],0
setz [kWeaponNoOverheat]
push bWeaponNoOverheat.size
push bWeaponNoOverheat
push dword[aWeaponNoOverheat]
call MemSwap
.WeaponNoOverheat:

push 0x67 ;VK_NUMPAD7
call [GetAsyncKeyState]
cmp [kWeaponRapidFire+1],ah
jz .WeaponRapidFire
mov [kWeaponRapidFire+1],ah
test ah,ah
jz .WeaponRapidFire
cmp byte[kWeaponRapidFire],0
setz [kWeaponRapidFire]
push bWeaponRapidFire.size
push bWeaponRapidFire
push dword[aWeaponRapidFire]
call MemSwap
.WeaponRapidFire:

push 0x68 ;VK_NUMPAD8
call [GetAsyncKeyState]
cmp [kSkillNoCoolDown+1],ah
jz .SkillNoCoolDown
mov [kSkillNoCoolDown+1],ah
test ah,ah
jz .SkillNoCoolDown
cmp byte[kSkillNoCoolDown],0
setz [kSkillNoCoolDown]
push bSkillNoCoolDown.size
push bSkillNoCoolDown
push dword[aSkillNoCoolDown]
call MemSwap
.SkillNoCoolDown:

 

 

После опций, добавим код создания шрифта и получения размера видимой области окна, также передвинем меню в правый угол а для копирования Rect структуры - используем SSE2.

Скрытый текст
.SkipKeys:
cmp byte[kMenu],0
jnz .SkipDraw
cmp dword[hFont],0
jnz .SkipCreateFont
push hFont
push Font
push 0
push 5 ;CLEARTYPE_QUALITY
push 0
push 0
push 0
push 0
push 700 ;FW_BOLD
push 0
push 15
push dword[esp+.D3D_Device+11*4]
call [D3DXCreateFontW]
push Rect
push dword[hGameWnd]
call [GetClientRect]
add dword[Rect+RECT.top],3
mov eax,[Rect+RECT.right]
sub eax,160
mov [Rect+RECT.left],eax
sub dword[Rect+RECT.right],6
.SkipCreateFont:
movdqu xmm0,[Rect]
movdqu [esp+.Rect],xmm0
lea ebp,[esp+.Rect]
mov esi,[hFont]
mov edi,[esi] ;d3dx9core.h

 

 

Для вывода текста опций - используем DrawTextW

Скрытый текст
push 0xFF1C9ADB ;blue
push 0x2 ;DT_RIGHT
push ebp
push Menu.size
push Menu
push 0
push esi
call [edi+60] ;DrawTextW
add [esp+.Rect+RECT.top],eax

mov eax,0xFF1C9ADB ;blue
mov ecx,0xFFFF0000 ;red
cmp [kAddGold],cl
cmovnz eax,ecx
push eax
push 0
push ebp
push AddGold.size
push AddGold
push 0
push esi
call [edi+60] ;DrawTextW
add [esp+.Rect+RECT.top],eax

mov eax,0xFF1C9ADB ;blue
mov ecx,0xFFFF0000 ;red
cmp [kAddResearchPoints],cl
cmovnz eax,ecx
push eax
push 0
push ebp
push AddResearchPoints.size
push AddResearchPoints
push 0
push esi
call [edi+60] ;DrawTextW
add [esp+.Rect+RECT.top],eax

mov eax,0xFF1C9ADB ;blue
mov ecx,0xFFFF0000 ;red
cmp [kInstantCallDragon],cl
cmovnz eax,ecx
push eax
push 0
push ebp
push InstantCallDragon.size
push InstantCallDragon
push 0
push esi
call [edi+60] ;DrawTextW
add [esp+.Rect+RECT.top],eax

mov eax,0xFF1C9ADB ;blue
mov ecx,0xFFFF0000 ;red
cmp [kDragonHealth],cl
cmovnz eax,ecx
push eax
push 0
push ebp
push DragonHealth.size
push DragonHealth
push 0
push esi
call [edi+60] ;DrawTextW
add [esp+.Rect+RECT.top],eax

mov eax,0xFF1C9ADB ;blue
mov ecx,0xFFFF0000 ;red
cmp [kDragonEnergy],cl
cmovnz eax,ecx
push eax
push 0
push ebp
push DragonEnergy.size
push DragonEnergy
push 0
push esi
call [edi+60] ;DrawTextW
add [esp+.Rect+RECT.top],eax

mov eax,0xFF1C9ADB ;blue
mov ecx,0xFFFF0000 ;red
cmp [kWeaponNoOverheat],cl
cmovnz eax,ecx
push eax
push 0
push ebp
push WeaponNoOverheat.size
push WeaponNoOverheat
push 0
push esi
call [edi+60] ;DrawTextW
add [esp+.Rect+RECT.top],eax

mov eax,0xFF1C9ADB ;blue
mov ecx,0xFFFF0000 ;red
cmp [kWeaponRapidFire],cl
cmovnz eax,ecx
push eax
push 0
push ebp
push WeaponRapidFire.size
push WeaponRapidFire
push 0
push esi
call [edi+60] ;DrawTextW
add [esp+.Rect+RECT.top],eax

mov eax,0xFF1C9ADB ;blue
mov ecx,0xFFFF0000 ;red
cmp [kSkillNoCoolDown],cl
cmovnz eax,ecx
push eax
push 0
push ebp
push SkillNoCoolDown.size
push SkillNoCoolDown
push 0
push esi
call [edi+60] ;DrawTextW

add eax,8
add [esp+.Rect+RECT.top],eax
push 0xFF1C9ADB ;blue
push 0x2 ;DT_RIGHT
push ebp
push About.size
push About
push 0
push esi
call [edi+60] ;DrawTextW

 

 

Возвращаем игре D3D_Device, убираем стек буфер, восстанавливаем важные регистры и ставим ret для возврата на код игры

Скрытый текст
.SkipDraw:
mov eax,[esp+.D3D_Device]
add esp,.stack_size
pop edi
pop esi
pop ebp
ret

 

 

Добавляем функцию освобождения ресурсов которые были созданы D3DXCreateFontW, если их не освободить - игра просто вылелит в момент изменения размера окна. Обнуление hFont нужно чтобы D3DXCreateFontW мог создать новый шрифт и для получения нового размера окна.

Скрытый текст
D3DresetDeviceHook:
push eax
push ecx
push edx
cmp dword[hFont],0
jz .Skip
mov eax,[hFont]
mov ecx,[eax] ;d3dx9core.h
push eax
call [ecx+8] ;ReleaseFont
mov dword[hFont],0
.Skip:
pop edx
pop ecx
pop eax
mov edi,ecx ;orig
mov [esp+0x10+4],ebx ;orig
ret

 

 

и остальные функции для обработки перехвата. там также находится "оригинал код" который был заменен на call

Скрытый текст
AddGoldHook1:
mov [esp+0x20+4],eax ;orig
mov dword[eax+0x3C],1000
mov eax,[eax+0x3C] ;orig
ret

AddGoldHook2:
mov dword[ecx+edx*4+0x480],100000
mov ecx,[ecx+edx*4+0x480] ;orig
ret

AddResearchPointsHook:
mov dword[eax+0x44],1000
mov edx,[eax+0x44]
pop ecx ;ret
push edx ;orig
push ecx
lea ecx,[esp+0x5C+4] ;orig
ret

 

 

Создадим секцию данных для: паттернов, патчей, строк

Скрытый текст
section .data
pAddGold1 db '894424??8B403C50',0
bAddGold1 db 0xE8
dd AddGoldHook1-5
db 0x90,0x90
.size equ $-bAddGold1

pAddGold2 db '8B8C??????????8944????894C????39',0
bAddGold2 db 0xE8
dd AddGoldHook2-5
db 0x90,0x90
.size equ $-bAddGold2

pAddResearchPoints db '528D4C24??FF15????????6A06',0
bAddResearchPoints db 0xE8
dd AddResearchPointsHook-5
.size equ $-bAddResearchPoints

pInstantCallDragon db '2BC8898E????????79',0
bInstantCallDragon db 0x29,0xC9
.size equ $-bInstantCallDragon

pDragonHealth1 db '7D??8B4424348B0FD9',0
bDragonHealth1 db 0xEB
.size equ $-bDragonHealth1
pDragonHealth2 db '7D06D805????????D835????????D95C????D944????D84D',0
bDragonHealth2 db 0x74
.size equ $-bDragonHealth2

pDragonEnergy db '7A06D947??D95F??8B',0
bDragonEnergy db 0x70
.size equ $-bDragonEnergy

pWeaponNoOverheat db 'D987????????D887????????D99F????????D987',0
bWeaponNoOverheat db 0xD9,0xEE,0x90,0x90,0x90,0x90
.size equ $-bWeaponNoOverheat

pWeaponRapidFire db 'E8????????3BF07C162B',0
bWeaponRapidFire db 0xB8,0x20,0x4E,0x00,0x00
.size equ $-bWeaponRapidFire

pSkillNoCoolDown db '2BC8890E7906C7',0
bSkillNoCoolDown db 0x29,0xC9
.size equ $-bSkillNoCoolDown

pD3DEndFrame db '8B80????????8B088B??????????50FFD28B',0
bD3DEndFrame db 0xE8
dd MenuHook-5
db 0x90
.size equ $-bD3DEndFrame

pD3DresetDevice db '8BF9895C????39',0
bD3DresetDevice db 0xE8
dd D3DresetDeviceHook-5
db 0x90
.size equ $-bD3DresetDevice

align 4,db 0
Menu dw __utf16__'Insert menu',0
.size equ ($-Menu-2)/2
align 4,db 0
AddGold dw __utf16__'Num1 add gold',0
.size equ ($-AddGold-2)/2
align 4,db 0
AddResearchPoints dw __utf16__'Num2 add research points',0
.size equ ($-AddResearchPoints-2)/2
align 4,db 0
InstantCallDragon dw __utf16__'Num3 instant call dragon',0
.size equ ($-InstantCallDragon-2)/2
align 4,db 0
DragonHealth dw __utf16__'Num4 dragon health',0
.size equ ($-DragonHealth-2)/2
align 4,db 0
DragonEnergy dw __utf16__'Num5 dragon energy',0
.size equ ($-DragonEnergy-2)/2
align 4,db 0
WeaponNoOverheat dw __utf16__'Num6 weapon no overheat',0
.size equ ($-WeaponNoOverheat-2)/2
align 4,db 0
WeaponRapidFire dw __utf16__'Num7 weapon rapid fire',0
.size equ ($-WeaponRapidFire-2)/2
align 4,db 0
SkillNoCoolDown dw __utf16__'Num8 skill no cooldown',0
.size equ ($-SkillNoCoolDown-2)/2
align 4,db 0
About dw __utf16__'by gmz',0
.size equ ($-About-2)/2

align 4,db 0
DX9BindingDLL dw __utf16__'DX9Binding.dll',0
align 4,db 0
Font dw __utf16__'Arial',0
align 4,db 0
MsgTitle dw __utf16__'',0
align 4,db 0
MsgVersionUnsupported dw __utf16__'Version unsupported!',0

 

 

Создадим секцию неинициализ. данных, которые не занимают место в длке

Скрытый текст
section .bss
hGameWnd resd 1
hFont resd 1
Rect resb RECT_size
aAddGold1 resd 1
aAddGold2 resd 1
aAddResearchPoints resd 1
aInstantCallDragon resd 1
aDragonHealth1 resd 1
aDragonHealth2 resd 1
aDragonEnergy resd 1
aWeaponNoOverheat resd 1
aWeaponRapidFire resd 1
aSkillNoCoolDown resd 1
aD3DEndFrame resd 1
aD3DresetDevice resd 1
kMenu resb 2
kAddGold resb 2
kAddResearchPoints resb 2
kInstantCallDragon resb 2
kDragonHealth resb 2
kDragonEnergy resb 2
kWeaponNoOverheat resb 2
kWeaponRapidFire resb 2
kSkillNoCoolDown resb 2

 

 

Сборка:

Скрытый текст
@echo off

..\nasm.exe -f win32 "dragon commander hack.asm"
..\golink.exe /ni /nw /osversion 00060001 /version 00000000 /subsystemversion 00060001 /stacksize 0x10000 /stackinit 0x1000 /heapsize 0x10000 /heapinit 0x1000 /largeaddressaware /nxcompat /dynamicbase /base 0x10000000 /dll /entry DllMain "dragon commander hack.obj" kernel32.dll user32.dll d3dx9_43.dll

if exist *.obj del *.obj
pause

 

 

ps справка по опциям линкера на оф сайте.

  • Плюс 6
Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.