PDA

Просмотр полной версии : Актуально. Закрытие диалогового окна с НПС


krysun
31.01.2012, 19:58
Нужен инжект, или функция на закрытие диалогового окна с НПС.
Посылка ESC не подходит, т.к. размораживает клиент.

krukovis
31.01.2012, 21:59
Нужен инжект, или функция на закрытие диалогового окна с НПС.
А ты можешь подсказать какая правильная структура окон?

krysun
31.01.2012, 22:19
Все что нашел по окнам (на ПвЛаб).
Если тут что-то неправильно, пробуйте эти.

Глобальный список
BA+1c+18+8+AC - список высокоуровневых окон (4б)
BA+1c+18+8+8С - список вспомогательных окон (4б)
Внутри каждого окна по смещению +1C8 лежит указатель на список контролов данного окна.
Структура списка (одинакова):
+4 - указатель на следующий элемент списка или 0, если элемент последний
+8 - PTR контрола
Структура контролов различна для каждого конкретного типа контрола (метки, кнопки, флажки и т.п.). Но есть и много общего, что совпадает для всех контролов:
+18 - класс контрола (указатель на ASCII-Z строку)
+84 - x-координата (4б, int, может быть отрицательна)
+88 - y-координата (4б, int, может быть отрицательна)
+8C - width (ширина контрола, 4б, int)
+90 - height (высота контрола, 4б, int)
Координаты контролов задаются относительно родительского окна (и могут быть отрицательными). Путём прямой модификации структуры контролов можно менять их состояние. Например, для меток (классы, начинающиеся с "Txt_"), по +B8 лежит указатель на текст метки. Для флажков (классы "Chk_") по +121 лежит 1-байтовое поле "чекнутости". Если там 1, флажок установлен.

инжект наиболее важной функции, которая играет для гуйщика ту же роль, что пакетная функция - для пакетчика.
809A00 - SendGUICommand.
esi=ecx = PTR целевого окна
1 арг. - указатель на символьную строку с командой
7FD4C0 - инжект установки активного окна
ecx=BA+1c+18+8,
1 арг. - PTR окна, которое должно стать активным

krukovis
01.02.2012, 21:38
Нужен инжект, или функция на закрытие диалогового окна с НПС.
Тут [Ссылки могут видеть только зарегистрированные и активированные пользователи]

gurin
01.04.2012, 00:28
Нельзя ли привести пример читающий список всех caption (если он есть) control всех окон?

а то что-то ка не мудрю - все фигня выходит


procedure ReadWinControls(hProc: THandle);
const
base_addr = $B27A04;
HiWin: array [1..5] of dword = (base_addr, $1c, $18, $8, $AC);
LoWin: array [1..5] of dword = (base_addr, $1c, $18, $8, $8C);
var
wins_addr, win_addr, ctrls_addr, ctrl_addr, ctrl_class_addr: dword;
ctrl_class: string;
begin
// список высокоуровневых окон
wins_addr := ChainRead32(hProc, @LoWin, High(LoWin));
repeat
if wins_addr <> 0 then
begin
// PTR структуры окна
win_addr := ReadInt32(hProc, ReadInt32(hProc, wins_addr) + $8);
// указатель на список контролов данного окна
ctrls_addr := ReadInt32(hProc, ReadInt32(hProc, win_addr) + $1C8);
if ctrls_addr <> 0 then
repeat
// PTR контрола
ctrl_addr := ReadInt32(hProc, ReadInt32(hProc, ctrls_addr) + $8);
// класс контрола (указатель на ASCII-Z строку)
ctrl_class_addr := ReadInt32(hProc, ReadInt32(hProc, ctrl_addr) + $18);
ctrl_class := ReadString(hProc, ctrl_class_addr);
// указатель на слудующий контрол данного окна
ctrls_addr := ReadInt32(hProc, ReadInt32(hProc, ctrls_addr) + $4);
until ctrls_addr = 0;
//указатель на следующий элемент списка или 0, если элемент последний
wins_addr := ReadInt32(hProc, ReadInt32(hProc, wins_addr) + $0);
end;
until wins_addr = 0;
end;

krukovis
01.04.2012, 13:20
Так можно получить указатели на все окна высокого или низкого уровня.
'Перечисления
Public Enum winLvlStruct As Integer
structHightLevel = 1
structLowLevel = 2
End Enum
'Получает все окна в заданном списке Высокого уровня или Низкого
Public Function getWinList(winStructLevel As Integer) As ArrayList
Dim WinStruct As Integer
'Лист для сохранения найденных окон
Dim winList As New ArrayList

If winStructLevel = 1 Then
'Обяъвляем адрес начала структуры окон высокого уровня
WinStruct = MemoryManager.ChainReadInt32(adrBaseAddress, &H1C, &H18, &H8, &HAC)
ElseIf winStructLevel = 2 Then
'Обяъвляем адрес начала структуры окон низкого уровня
WinStruct = MemoryManager.ChainReadInt32(adrBaseAddress, &H1C, &H18, &H8, &H8C)
Else
Return Nothing
End If

'Указатель на текущую структуру окна
Dim currentWinPtr As Integer = MemoryManager.ReadInt32(WinStruct + &H8)
'Указатель на структуру следующего окна или 0, если это последнее окно
Dim nextWinPtr As Integer = MemoryManager.ReadInt32(WinStruct + &H0)

'Рассмотрим окна
'Выходим если наткнемся на ноль
Do Until nextWinPtr = 0
winList.Add(currentWinPtr)
'берем следующее окно
nextWinPtr = MemoryManager.ReadInt32(nextWinPtr + &H0)
'И смотрим в его структуру
currentWinPtr = MemoryManager.ReadInt32(nextWinPtr + &H8)
Loop
Return winList
End Function
А наименование окна можно прочитать так:
'Параметр имени окна (ASCII строка) по указателю
Public Function getWinNameOnPtr(winPtr As Int32) As String
Return MemoryManager.ChainReadString_ASCII(winPtr + &H4C, 32, 0)
End Function
Если в более привычной записи то это так: WinName = WinPtr + $4C +$0.

gurin
01.04.2012, 15:19
Получаю ту же фигню на китайском :( и окон 428 штук, мне кажется их там столько нету

procedure ReadWinAddrList(hProc: THandle; winList: TStrings);
const
HiWin: array [1..5] of dword = (base_addr, $1c, $18, $8, $AC);
LoWin: array [1..5] of dword = (base_addr, $1c, $18, $8, $8C);
var
WinStruct, currentWinPtr, nextWinPtr: dword;
WinType: Integer;
begin
for WinType := 1 to 2 do
begin
// Обяъвляем адрес начала структуры окон высокого уровня
if WinType = 1 then
WinStruct := ChainReadInt32(hProc, @HiWin, High(HiWin))
else
if WinType = 2 then
WinStruct := ChainReadInt32(hProc, @LoWin, High(LoWin));
// Указатель на текущую структуру окна
currentWinPtr := ReadInt32(hProc, WinStruct + $8);
// Указатель на структуру следующего окна или 0, если это последнее окно
nextWinPtr := ReadInt32(hProc, WinStruct + $0);
// Рассмотрим окна
// Выходим если наткнемся на ноль
while nextWinPtr <> 0 do
begin
winList.Add(IntToHex(currentWinPtr, 8));
// берем следующее окно
nextWinPtr := ReadInt32(hProc, nextWinPtr + $0);
// И смотрим в его структуру
currentWinPtr := ReadInt32(hProc, nextWinPtr + $8);
end;
end;
end;

function getWinNameOnPtr(hProc: THandle; winPtr: dword): String;
begin
Result := ReadString(hProc, ReadInt32(hProc, winPtr + $4C));
end;


и мне кажется что тут

nextWinPtr = MemoryManager.ReadInt32(nextWinPtr + &H0)
'И смотрим в его структуру
currentWinPtr = MemoryManager.ReadInt32(nextWinPtr + &H8)

ошибка
nextWinPtr и так содержит указатель на следующее окно, мы получается через одно скачем. Мне кажется строки надо переставить местами, я уж не говорю о том что если nextWinPtr станет = 0, то в currentWinPtr будет чепуха
это не критично, т.к. дальше нигде использоваться не будет, но все-же

Добавлено через 57 минут
простая функция, возвращающая заголовок 1-го окна


function getFirstHiWinName(hProc: THandle): String;
const
HiWin: array [1..7] of dword = ($B27A04, $1c, $18, $8, $AC, $8, $4C);
var
addr: dword;
begin
addr := ChainReadInt32(hProc, @HiWin, High(HiWin));
Result := ReadString(hProc, addr);
end;


возвращает ровно ту же фигню на китайском что и пара функций выше

vogel
01.04.2012, 18:29
Я закрываю диалог так :


procedure THostPlayer.CloseDialog();
var
aParams : TParams;
begin
aParams.Param1 := DlgCancelPush;
self._process.InjectFunc(@DialogOperationCall, @aParams, SizeOf(aParams));
end;


Функция инжекта выглядит так :


procedure DialogOperationCall(aPParams : PParams); stdcall;
var
address : Pointer;
param : Cardinal;
begin
address := Pointer(DialogOpAddr); param := aPParams^.Param1;
asm
pushad
push param
mov esi, dword ptr [PW_BASE_ADDRESS]
mov esi, dword ptr [esi + PW_DYNAMIC_BASE_OFFSET]
mov esi, dword ptr [esi + PW_UIMANAGER_OFFSET]
mov esi, dword ptr [esi + PW_UIMANAGER_GAMEUI_OFFSET]
mov esi, dword ptr [esi + PW_UIMANAGER_CURRENT_DIALOG]
mov ecx, esi
call address
popad
end;
address := nil;
end;


Вот необходимые константы для актуальной версии ру-оффа :

DlgCancelPush = $00ACB998;
DialogOpAddr = $00809A00;

PW_BASE_ADDRESS = $00b27a04;
PW_DYNAMIC_BASE_OFFSET = $001c; // [base] + 1c = CECGameRun
PW_UIMANAGER_OFFSET = $0018; // [base] + 1c + 18
PW_UIMANAGER_GAMEUI_OFFSET = $0008; // [base] + 1c + 18 + 08 = CECGameUIMan
PW_UIMANAGER_CURRENT_DIALOG = $0074; // [base] + 1c + 18 + 08 + 74



~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Помог ? Не будь жлобом - нажми "Спасибо" !

gurin
01.04.2012, 18:35
DlgCancelPush

а адреса DlgOkPush нет случайно?

krukovis
01.04.2012, 21:20
а адреса DlgOkPush нет случайно?
Поставить брейк-поинт сюда 00809A02 и нажми ОК, в ESI загрузится адрес команды.

Добавлено через 3 минуты
и мне кажется что тут
Код:

nextWinPtr = MemoryManager.ReadInt32(nextWinPtr + &H0)
'И смотрим в его структуру
currentWinPtr = MemoryManager.ReadInt32(nextWinPtr + &H8)

ошибка
Я тебе выложил готовый код из своей программы и код абсолютно рабочий. Может просто переведешь и будешь юзать, вместо того, чтобы искать ошибки?

gurin
01.04.2012, 23:23
я перевел и привел его выше в посте
он у меня не заработал, потому и начал искать где налажал, не нашел :(, но нашел то что написал. Сорри если не в тему.

спасибо за брейкпойнт (сейчас порою) - и за то что вообще даёшь информацию

gurin
02.04.2012, 01:24
Каждый раз там разные значения. Но даже если его взять за адрес, то если его подставить вместо BTN_CLOSE - ничего не происходит.

Перешерстил весь код клиента, нашел несколько ссылок на IDOK, наиболее вероятная мне показалась $AD4870, но... не пашет :(, как и все остальные, видимо в коте эта кнопка не IDOK

krukovis
02.04.2012, 08:42
Каждый раз там разные значения.
Функция работы с gui принимает ТЕКСТОВЫЕ команды. Т.е. клиент выделяет место в памяти в случайном месте и помещает туда ASCII строку, например для кнопки ОК у кота это слово "confirm", и загружает адрес этой команды в функцию. Для кнопки Отмена - текст будет "IDCANCAL". Для этой команды есть и постоянное значение в памяти - текст по адресу 0x00ACB998 (используется при закрытие на крестик). Название то же, только выделять место в памяти не нужно, т.к. это стандартная команда для всех окон, для нее сделали константу. Но для кнопки так же выделяется память и туда пишется команда.

gurin
02.04.2012, 13:11
СПАСИБО!!! Нашёл, адрес кнопки ОК - 00ACB6C8

tianddu
27.02.2014, 22:38
а живого закрытия диалога нет ни у кого?)

diagnost
05.02.2016, 18:03
Нашел старый клиент
CPU Disasm
Address Hex dump Command Comments
00809A00 53 PUSH EBX
00809A01 56 PUSH ESI
00809A02 8B7424 0C MOV ESI,DWORD PTR [ESP+0C]
00809A06 8BD9 MOV EBX,ECX
00809A08 57 PUSH EDI
00809A09 8BFE MOV EDI,ESI
00809A0B 83C9 FF OR ECX,FFFFFFFF
00809A0E 33C0 XOR EAX,EAX
00809A10 F2:AE REPNE SCAS BYTE PTR [EDI]
00809A12 F7D1 NOT ECX
00809A14 49 DEC ECX
00809A15 75 08 JNE SHORT elementclient.00809A1F
00809A17 5F POP EDI
00809A18 5E POP ESI
00809A19 B0 01 MOV AL,1
00809A1B 5B POP EBX
00809A1C C2 0400 RET 4
00809A1F 8B43 78 MOV EAX,DWORD PTR [EBX+78]
00809A22 BF 01000000 MOV EDI,1
00809A27 3BC7 CMP EAX,EDI
00809A29 75 76 JNE SHORT elementclient.00809AA1
00809A2B 55 PUSH EBP
00809A2C 8B2D E884A100 MOV EBP,DWORD PTR [<&MSVCRT._strcmpi>]
00809A32 68 98B9AC00 PUSH OFFSET elementclient.00ACB998 ; ASCII "IDCANCEL"
00809A37 56 PUSH ESI
00809A38 FFD5 CALL EBP
00809A3A 83C4 08 ADD ESP,8
00809A3D 85C0 TEST EAX,EAX
00809A3F 75 16 JNE SHORT elementclient.00809A57
00809A41 8B4B 34 MOV ECX,DWORD PTR [EBX+34]
00809A44 BF 02000000 MOV EDI,2
00809A49 53 PUSH EBX
00809A4A 57 PUSH EDI
00809A4B 8B01 MOV EAX,DWORD PTR [ECX]
00809A4D FF50 18 CALL DWORD PTR [EAX+18]
00809A50 5D POP EBP
00809A51 5F POP EDI
00809A52 5E POP ESI
00809A53 5B POP EBX
00809A54 C2 0400 RET 4


Нашел в новом клиенте тоже место
CPU Disasm
Address Hex dump Command Comments
009B2FC0 /. 53 PUSH EBX
009B2FC1 |. 56 PUSH ESI
009B2FC2 |. 8B7424 0C MOV ESI,DWORD PTR [ARG.1]
009B2FC6 |. 8BD9 MOV EBX,ECX
009B2FC8 |. 57 PUSH EDI
009B2FC9 |. 8BFE MOV EDI,ESI
009B2FCB |. 83C9 FF OR ECX,FFFFFFFF
009B2FCE |. 33C0 XOR EAX,EAX
009B2FD0 |. F2:AE REPNE SCAS BYTE PTR [EDI]
009B2FD2 |. F7D1 NOT ECX
009B2FD4 |. 49 DEC ECX
009B2FD5 |. 75 08 JNZ SHORT elementclient.009B2FDF
009B2FD7 |. 5F POP EDI
009B2FD8 |. 5E POP ESI
009B2FD9 |. B0 01 MOV AL,1
009B2FDB |. 5B POP EBX
009B2FDC |. C2 0400 RET 4
009B2FDF |> 8B43 78 MOV EAX,DWORD PTR [EBX+78]
009B2FE2 |. BF 01000000 MOV EDI,1
009B2FE7 |. 3BC7 CMP EAX,EDI
009B2FE9 |. 75 76 JNE SHORT elementclient.009B3061
009B2FEB |. 55 PUSH EBP
009B2FEC |. 8B2D 84C3BF00 MOV EBP,DWORD PTR [<&MSVCRT._strcmpi>]
009B2FF2 |. 68 EC10CF00 PUSH OFFSET elementclient.00CF10EC ; /Arg2 = ASCII "IDCANCEL"
009B2FF7 |. 56 PUSH ESI ; |Arg1 => [ARG.1]
009B2FF8 |. FFD5 CALL EBP ; \msvcrt._strcmpi
009B2FFA |. 83C4 08 ADD ESP,8
009B2FFD |. 85C0 TEST EAX,EAX
009B2FFF |. 75 16 JNZ SHORT elementclient.009B3017
009B3001 |. 8B4B 34 MOV ECX,DWORD PTR [EBX+34]
009B3004 |. BF 02000000 MOV EDI,2
009B3009 |. 53 PUSH EBX
009B300A |. 57 PUSH EDI
009B300B |. 8B01 MOV EAX,DWORD PTR [ECX]
009B300D |. FF50 18 CALL DWORD PTR [EAX+18]
009B3010 |. 5D POP EBP
009B3011 |. 5F POP EDI
009B3012 |. 5E POP ESI
009B3013 |. 5B POP EBX
009B3014 |. C2 0400 RET 4


Получил готовый инжект закрытия любого окна
const
DlgCancelPush = $00CF10EC;//$00ACB998;
DialogOpAddr = $009B2FC0;//$00809A00;

PW_BASE_ADDRESS = $00D6F0AC;
PW_DYNAMIC_BASE_OFFSET = $001c; // [base] + 1c = CECGameRun
PW_UIMANAGER_OFFSET = $0010; // [base] + 1c + 18
PW_UIMANAGER_GAMEUI_OFFSET = $0008; // [base] + 1c + 18 + 08 = CECGameUIMan
PW_UIMANAGER_CURRENT_DIALOG = $0074; // [base] + 1c + 18 + 08 + 74

procedure DialogOperationCall(aPParams : PParams); stdcall;
var
address : Pointer;
param : Cardinal;
begin
//DialogOpAddr = $00809A00;
address := Pointer(DialogOpAddr);
param := aPParams^.Param1;
asm
pushad
push param
//PW_BASE_ADDRESS = $00b27a04;
mov esi, dword ptr [PW_BASE_ADDRESS]
//PW_DYNAMIC_BASE_OFFSET = $001c; // [base] + 1c = CECGameRun
mov esi, dword ptr [esi + PW_DYNAMIC_BASE_OFFSET]
//PW_UIMANAGER_OFFSET = $0018; // [base] + 1c + 18
mov esi, dword ptr [esi + PW_UIMANAGER_OFFSET]
//PW_UIMANAGER_GAMEUI_OFFSET = $0008; // [base] + 1c + 18 + 08 = CECGameUIMan
mov esi, dword ptr [esi + PW_UIMANAGER_GAMEUI_OFFSET]
// PW_UIMANAGER_CURRENT_DIALOG = $0074; // [base] + 1c + 18 + 08 + 74
mov esi, dword ptr [esi + PW_UIMANAGER_CURRENT_DIALOG]
mov ecx, esi
call address
popad
end;
address := nil;
end;

procedure CloseDialog();
var
aParams : TParams;
begin
aParams.Param1 := DlgCancelPush; //DlgCancelPush = $00ACB998; DlgOkPush =00ACB6C8 адрес кнопки ОК
InjectFunc(hProcess, @DialogOperationCall, @aParams, SizeOf(aParams));
end;

procedure TForm3.Button4Click(Sender: TObject);
begin
connect;
CloseDialog();
end;

Ну и до кучи Структура GUI

GA+10+08

Остальное на сколько понял осталось без изменения

Всем спасибо за проделанную работу.

N00bSa1b0t
05.02.2016, 18:37
Ради интереса спрошу - а кроме как чисто эстетической цели (ну, чтобы лишних окон не было), данный инжект несет практическую пользу?

diagnost
06.02.2016, 00:04
Зачем нужен этот инжект? Мне кажется здесь полный ответ ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) на этот вопрос. Тоже все перелопатил пока смог запустить этот инжект. Он имеет непосредственное отношение к GUI. Раньше его называли ButtonPress(это Call адрес), потом он получил название DialogOpAddr(Call адрес), а теперь просто GUI(Call адрес, еще его называют GUI_Command ) Я не хочу сказать что, они все одинаковы, но все они крутятся возле выше указанного участка кода. И как его искать до сих пор мне не понятно.
Осталось выяснить, как найти вот эти адреса
BA+0x1C+0x04+0x18+I+D
I (окна, длинна 0x850 байт)
0x2B8 Действия
0x2C0 Игроки и группы
0x2C4 Служба поддержки
0x314 Характеристики персонажа
0x32C Ремонт
0x36C Призыв духа
0x3E8 Помощь
0x40C Инвентарь
0x428 Диалог с нпс
0x438 Домашние животные
0x458 Окно алхимика
0x468 Панель 1-9
0x470 Горячие клавиши
0x4B0 Настройки
0x4C4 Умения
0x50C Системная панель
0x51C Задания
D (данные окна, длинна 0x218 байт)
0x98 WndX, dword
0x9C WndY, dword
0xA0 WndW, dword
0xA4 WndH, dword
0X90 WndO, byte (1-open, 0-close)

N00bSa1b0t
06.02.2016, 00:12
Зачем нужен этот инжект? Мне кажется здесь полный ответ на этот вопрос.
Нет, я понимаю, что он делает, но я не вижу смысла в закрытии окон.
Если бот работает автономно - какая разница, откыто у него окно разговора с нпц или нет?)

diagnost
06.02.2016, 00:30
Если окно не закрыть, перестают работать скиллы(в Пати), кроме этого после подключения инжекта автопути к боту, персы перестали следовать за ПЛ, тупо стоят, пока не закроешь окно. А также, если в таргете кто то есть при нажатии ESC идет команда сброс таргета, мне не всегда нужно делать сброс таргета, нужно просто закрыть окно(нажать на крестик). У ПЛ всегда кто то есть в таргете ESC просто не помогает.

N00bSa1b0t
06.02.2016, 00:41
А не проще было найти флаг блокировки интерфейса и его обнулять? :-)

diagnost
06.02.2016, 00:49
По другому менять фокус окна?(Хорошая идея, и почему об этом не подумал?) Для него также нужно искать адреса и инжектить, в начале топа есть такое, только примеров не видел.
Теперь по поводу оптимизации кода. ESI и EDI регистры используются немного по другому, они нам здесь не нужны. У нас остались свободными регистры EAX, EBX. Мы будем использовать EAX и ECX(который уже используется). После преобразования последняя строчка mov ecx, esi нам совсем не нужна. Приводим код к общему стандарту:

procedure BtnPressAs(aPParams:PParams); stdcall;
var
Btn_Call, BaseAddr: Pointer;
IdCancel: dword;
begin
BaseAddr := Pointer(aPParams^.Param1);
IdCancel := aPParams^.Param2;
Btn_Call := Pointer(aPParams^.Param3);
asm
pushad
mov eax, IdCancel
push eax
mov ecx, BaseAddr
mov ecx, dword ptr [ecx]
mov ecx, dword ptr [ecx+$1C]
mov ecx, dword ptr [ecx+$10]
mov ecx, dword ptr [ecx+$8]
mov ecx, dword ptr [ecx+$74]
call Btn_Call
popad
end;
end;

procedure WinClose();
var
aParams: TParams;
begin
aParams.Param1 := base_addr;
aParams.Param2 := IdCancel_addr;
aParams.Param3 := ButtonPress_addr;
if (aParams.Param1 <> 0) and (aParams.Param2 <> 0) and (aParams.Param3 <> 0) then
InjectFunc(@BtnPressAs, @aParams, SizeOf(aParams));
end;