Subclassing без модуля, но с ассемблерной вставкой...

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Subclassing без модуля, но с ассемблерной вставкой...

Сообщение arthur2 » 04.06.2008 (Ср) 12:19

Нашёл очень удобный код:

Код: Выделить всё
Option Explicit

Public Event KeyDown(ByVal KeyCode As Long)

Private Const WM_KEYDOWN As Long = &H100
Private Const cNull As Long = &H0

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Const AsmMain As String = "558BEC83C4FC8D45FC50FF7514FF7510FF750CFF75086800000000B800000000FFD08B45FCC9C21000"

'    0  1  2  3  4  5  6  7    8  9  A  B  C  D  E  F
' ---------------------------------------------------
'|  55 8B EC 83 C4 FC 8D 45   FC 50 FF 75 14 FF 75 10
'|  FF 75 0C FF 75 08 68 00   00 00 00 B8 00 00 00 00
'|  FF D0 8B 45 FC C9 C2 10   00


Private OldCtl      As Long, OldCtl1     As Long
Private ASM_Ctl()   As Byte
Private ASM()   As Byte
Dim mHwnd As Long, mHwnd1 As Long

'адрес этой функции получается по номеру ProcNumber
Public Function winProc0(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  Select Case uMsg
  Case WM_KEYDOWN
    RaiseEvent KeyDown(wParam)
  End Select
   winProc0 = CallWindowProc(OldCtl, hWnd, uMsg, wParam, lParam)
End Function

'адрес этой функции получается по номеру ProcNumber
Public Function winProc1(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'  Select Case uMsg
'  Case WM_KEYDOWN
'    RaiseEvent KeyDown(wParam)
'  End Select
   winProc1 = CallWindowProc(OldCtl, hWnd, uMsg, wParam, lParam)
End Function

Public Function Hook(ByVal hWnd As Long)
  mHwnd = hWnd
  Call StartSubclass(hWnd, OldCtl, 0)
End Function


Private Sub StartSubclass(ByVal hWnd As Long, _
                          ByRef OldWndProc As Long, _
                          ByVal ProcNumber As Long)
  Dim lng As Long
  Dim tPtr As Long
 
  ASM = ASM_Ctl
  Call CopyMemory(tPtr, ByVal ObjPtr(Me), 4&)
  Call CopyMemory(lng, ByVal tPtr + &H1C + (4& * ProcNumber), 4&)
  Call CopyMemory(ASM(23), ObjPtr(Me), 4&)
  Call CopyMemory(ASM(28), lng, 4&)
  OldWndProc = SetWindowLong(hWnd, &HFFFC, VarPtr(ASM(0)))
 
' ProcNumber - номер функции в этом  классе, начиная сверху по порядку вниз...
End Sub

Public Sub Unhook()
  If OldCtl Then Call SetWindowLong(mHwnd, &HFFFC, OldCtl)
End Sub


Private Sub Class_Initialize()
  Dim lng As Long
  Dim tPtr As Long
  lng = Len(AsmMain) \ 2&
  ReDim ASM_Ctl(cNull To lng - vbNull)
  For lng = cNull To lng - vbNull
    ASM_Ctl(lng) = Val("&H" & Mid$(AsmMain, (lng) * 2& + vbNull, 2&))
  Next

End Sub

Private Sub Class_Terminate()
  Call Unhook
End Sub


Вот только очень хотелось было бы понять, что именно делает эта ассемберная вставка и можно ли вообще так делать? А то код очень удобный, а пользоваться им - боязно...
Артур
 
   

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 04.06.2008 (Ср) 13:43

Можно пользоваться, боятся нечего
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 04.06.2008 (Ср) 13:50

Вставка - оконная процедура, которая перенаправляет обработку оконных сообщений в Public Function winProc0 или winProc1. Главная цель этого метода - объединение всего кода контрола в одном ctl-модуле. Ну и защита IDE от обрушения при возникновении ошибки в оконной процедуре.

Пользоваться можно, если понимаешь, как это работает. Если не понимаешь - можешь обнаружишь проблемы, поиск причин которых будет не прост. А еще этот код, если не ошибаюсь, DEP-unsafe.
Лучший способ понять что-то самому — объяснить это другому.

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Сообщение Viper » 04.06.2008 (Ср) 14:28

Пользоваться можно. Только вот странно выглядит преобразование строки с ассемблерным кодом в массив байт через Mid. Можно было изначально массив байт проинициализировать. А то как то странно выглядит - ассемблерная вставка и преобразование строки через Mid. :)
Весь мир матрица, а мы в нем потоки байтов!

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 04.06.2008 (Ср) 14:45

ANDLL
В каком-то из твоих топиков как раз встречал, что при вызове такой вставки из массива байтов будут падения на новых машинах или на новых осях... Или я просто не так понял. Собственно, это-то меня и беспокоит.

Antonariy
Ага, та часть, которая снаружи, мне понятно. А если что в ассемблерной вставке не так - боюсь, в случае чего поиск для меня будет не то что "не прост", а "безнадёжен". Собственно, поэтому и интересуюсь.

Защита ide там не очень (я так понимаю, что во вставке нет обработки случая, когда функция класса недоступна, поэтому при отладке нельзя входить в класс - бейсик не падает, но виснет намертво). А вот то, что winproc-ов может быть сколько угодно и на любое окно можно прилепить по независимому субклассингу - очень удобно!

А что такое DEP-unsafe?
Артур
 
   

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 04.06.2008 (Ср) 14:52

Viper
Это понятно. Если решусь пользоваться - так и сделаю (в ресурс запихну). Но в оригинале так. Я пока и оставил в надежде, что может кто посмотрит, что за вставка такая?
Артур
 
   

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 04.06.2008 (Ср) 14:53

А что такое DEP-unsafe?

В каком-то из твоих топиков как раз встречал, что при вызове такой вставки из массива байтов будут падения на новых машинах или на новых осях...
- это и есть DEP-unsafe. :)
Исчерпывающе может объяснить Хакер, я вроде видел объяснения, но уже их не помню. Что-то насчет пометки, что область памяти, занимаемая массивом, является безопасной для исполнения.
Защита ide там не очень
Есть разные asm-шаблоны для сабклассинга, видимо этот из простейших.
А вот то, что winproc-ов может быть сколько угодно и на любое окно можно прилепить по независимому субклассингу - очень удобно!
Всё это можно сделать и обычным методом.
Лучший способ понять что-то самому — объяснить это другому.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 04.06.2008 (Ср) 15:50

Antonariy
что область памяти, занимаемая массивом, является безопасной для исполнения
Спасибо! вот теперь понятно (я принципа не мог понять - почему, собстенно на старых работает, а на новых нет. А это оказывается, безопасность... Типа страховки, чтобы что попало не выполнялось?)
То есть, нужно что-то вроде VirtualProtect приделывать к массиву? А как? В общем, кажется, получается не столько удобней, сколько даже сложнее. :( Да и проверит не на чем :)

Есть разные asm-шаблоны
А где бы на них посмотреть?

Всё это можно сделать и обычным методом.
Обычным методом - это нужно из модуля все сообщения по классам вручную разруливать. А тут - создал новый класс, а в нём уже своя winProc!
Артур
 
   

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 04.06.2008 (Ср) 16:23

Кстати да, конкретно приведенный код кривой(будет вылетать на новых компьютерах и осях).
Я как то лучше писал: http://forum.sources.ru/index.php?showtopic=196997
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 04.06.2008 (Ср) 19:50

ANDLL
Спасибо! Здорово.

Я попытался исправить то, что у меня. Сделал так:
Код: Выделить всё
Option Explicit

Public Event KeyDown(ByVal KeyCode As Long)

Private Const WM_KEYDOWN As Long = &H100
'Private Const cNull As Long = &H0
Private Const GWL_WNDPROC = -4

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Private Declare Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualProtect Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Private Declare Function VirtualFree Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Const MEM_DECOMMIT = &H4000
Private Const MEM_RELEASE = &H8000
Private Const MEM_COMMIT = &H1000
Private Const MEM_RESERVE = &H2000
Private Const MEM_RESET = &H80000
Private Const MEM_TOP_DOWN = &H100000
Private Const PAGE_READONLY = &H2
Private Const PAGE_READWRITE = &H4
Private Const PAGE_EXECUTE = &H10
Private Const PAGE_EXECUTE_READ = &H20
Private Const PAGE_EXECUTE_READWRITE = &H40
Private Const PAGE_GUARD = &H100
Private Const PAGE_NOACCESS = &H1
Private Const PAGE_NOCACHE = &H200

Const AsmMain As String = "55 8B EC 83 C4 FC 8D 45 FC 50 FF 75 14 FF 75 10 FF 75 0C FF 75 08 68 00 00 00 00 B8 00 00 00 00 FF D0 8B 45 FC C9 C2 10 00"

Private OldCtl      As Long
Private ASM_Ctl()   As Byte
Dim ASM()    As Byte

Dim mHwnd As Long
Dim asmProc As Long

Public Function zLVCallBack(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  Select Case uMsg
  Case WM_KEYDOWN
    RaiseEvent KeyDown(wParam)
  End Select
  zLVCallBack = CallWindowProc(OldCtl, hWnd, uMsg, wParam, lParam)
End Function

Public Function Hook(ByVal hWnd As Long)
  mHwnd = hWnd
  Call StartSubclass(hWnd, OldCtl, 0)
End Function

Public Sub Unhook()
  If OldCtl Then Call SetWindowLong(mHwnd, GWL_WNDPROC, OldCtl)
End Sub

Private Sub StartSubclass(ByVal hWnd As Long _
                          , ByRef OldWndProc As Long _
                          , ByVal ProcNumber As Long)
  Dim lng As Long
  Dim tPtr As Long

  ASM = ASM_Ctl
  Call CopyMemory(tPtr, ByVal ObjPtr(Me), 4&)
  Call CopyMemory(lng, ByVal tPtr + &H1C + (4& * ProcNumber), 4&)
  Call CopyMemory(ASM(23), ObjPtr(Me), 4&)
  Call CopyMemory(ASM(28), lng, 4&)
  lng = UBound(ASM) + 1
 
'  asmProc = VirtualAlloc(ByVal 0&, lng, MEM_COMMIT, PAGE_READWRITE)
  asmProc = VirtualAlloc(ByVal 0&, lng, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
  tPtr = VarPtr(ASM(0))
 
  Call CopyMemory(asmProc, tPtr, lng)
 
'  VirtualProtect asmProc, lng, PAGE_EXECUTE_READWRITE, 0&
 
  OldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, asmProc)
 
End Sub

Private Sub Class_Initialize()
  Dim lng As Long
  Dim tPtr As Long
 
  Dim s() As String
  s = Split(AsmMain, " ")
  lng = UBound(s)
 
  ReDim ASM_Ctl(lng)
 
  For lng = 0 To lng
    ASM_Ctl(lng) = Val("&H" & s(lng))
  Next

End Sub

Private Sub Class_Terminate()
Dim ln As Long
ln = UBound(ASM_Ctl) + 1
  Call Unhook
  VirtualFree asmProc, 0&, MEM_RELEASE
End Sub

Код вроде работает. Теперь он не DEP-unsafe? Или я не правильно понял :lol:

Но только теперь бейсик стал время от времени падать (не в режиме исполнения, а после того, как несколько раз запустил, в режиме редактирования). В основном, когда я что-то копирую в буфер или вставляю. Почему это может быть?
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 04.06.2008 (Ср) 23:47

Эх.. какую тему проспал.

Код: Выделить всё
Const AsmMain As String = "558BEC83C4FC8D45FC50FF7514FF7510FF750CFF75086800000000B800000000FFD08B45FCC9C21000"

кг/ам.


Во-первых, хранить хексдамп кода, а потом преобразовывать его в код, глупо, когда можно хранить код.

Во-вторых, дизасм этого кода показывает, что его А -- М (малоопытный :) ).

Предлагаю свой вариант:
31C050548D4804FF742418E0FA50E80000000058C21000

Короче в двое, и, соответственно, быстрее. (Вместо синего здесь стоит записать не lng, как в оригинале, а lng - VarPtr(asm(20)). Ну, 20 +/- 4 -- я могу ошибиться - считаю всё в уме.

И есть ещё один момент. Внутри WindowProc ссылка Me будет ссылаться на Nothing :) Если надо, чтобы Me был действительно Me:
31C050548D4804FF742418E0FA6800000000E80000000058C21000
Вместо красного записываем ObjPtr(Me), как и в примере.

Далее.
В конечном варианте туое "DADADA" заменилось ещё более тупым "DA DA DA".
Зачем здесь пробелы? Чтобы добавить к этому безобразию ещё и Split? Что-бы что? Ухудшить преобразование хексдамп-данные?

Далее.
Совершенно непонятно, с чего вдруг в VirtualFree вторыми аргументом идёт 0. Второй аргумент - это размер. И передавать туда надо размер закоммиченой области, а не 0.

Далее.
Я бы создавал приватную кучу с правом выполнения, и аллочил место под переходники там. А то Virtual-функции, это слишком низкоуровнево для такой задачи.

В основном, когда я что-то копирую в буфер или вставляю. Почему это может быть?

Код исключения в студию. OllyDbg в помощь.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 05.06.2008 (Чт) 9:46

Хакер
Во-первых, хранить хексдамп кода, а потом преобразовывать его в код, глупо, когда можно хранить код.
В конечном варианте туое "DADADA" заменилось ещё более тупым "DA DA DA"
это понятно и так. Если доковыряюсь-таки, то естественно, перенесу в ресурс: ASM()=LoadResData
Но здесь оставил так, чтобы было видно сам код асма (и в оригинале так). На сплит поменял, потому что For lng = cNull To lng - vbNull выглядело уж больно непролазно, а править было лень (всё равно выбрасывать).

Совершенно непонятно, с чего вдруг в VirtualFree вторыми аргументом идёт 0
По Эппельману - ноль, если задан флаг MEM_RELEASE. Впрочем, размер тоже ставить пробовал - всё равно после нескольких запусков падает.
Код исключения в студию.

Чаще всего, что программа обратилась к участку памяти, который не может быть рид. Иногда - бейсик выполнил недопустимую и можно отправить отчёт :lol:
Я бы создавал приватную кучу с правом выполнения
На какие функции поглядеть?

Смысл твоей правки асма, к сожалению, я всё равно понять не смогу, а вот как именно её вживить в пример?
Пытаюсь по твоему описанию:

Код: Выделить всё
'31 C0 50 54 8D 48 04 FF 74 24 18 E0 FA 50 E8 хх хх хх хх 58 C2 10 00
  Call CopyMemory(tPtr, ByVal ObjPtr(Me), 4&)
  Call CopyMemory(lng, ByVal tPtr + &H1C + (4& * ProcNumber), 4&)
 
'  Call CopyMemory(ASM(23), ObjPtr(Me), 4&)
'  Call CopyMemory(ASM(28), lng, 4&)

  Call CopyMemory(ASM(15), lng - VarPtr(ASM(20)), 4&)
(только ногами не бей)
15 это начало синего
А 20 - это откуда пересчитать?
Артур
 
   

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 05.06.2008 (Чт) 10:21

На какие функции поглядеть?
Не на какие, он пошутил. (то есть он конечно не пошутил но страдать этим все же не стоит)
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 05.06.2008 (Чт) 12:34

Чаще всего, что программа обратилась к участку памяти, который не может быть рид. Иногда - бейсик выполнил недопустимую и можно отправить отчёт
Вот это я и имел ввиду под проблемами, которые х. поймешь. В баню эти asm-шаблоны.
Лучший способ понять что-то самому — объяснить это другому.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 05.06.2008 (Чт) 15:51

В баню эти asm-шаблоны.
Да нет, дело-то не в асм-шаблонах. Они просто никак не могут вызывать такие баги.

Дело здесь, очевидно, в кривом управлении памятью.

На какие функции поглядеть?

HeapCreate( с флагом HEAP_CREATE_ENABLE_EXECUTE) + HeapDestroy
HeapAlloc + HeapFree

Я не знаю, почему возмущается andll, но по моему, выделять под каждые 23 байта кода целых 4096 байт памяти -- слишком дорогое удовольствие.

Смысл твоей правки асма, к сожалению, я всё равно понять не смогу, а вот как именно её вживить в пример?
Пытаюсь по твоему описанию:

Код: Выделить всё

'31 C0 50 54 8D 48 04 FF 74 24 18 E0 FA 50 E8 хх хх хх хх 58 C2 10 00
  Call CopyMemory(tPtr, ByVal ObjPtr(Me), 4&)
  Call CopyMemory(lng, ByVal tPtr + &H1C + (4& * ProcNumber), 4&)
 
'  Call CopyMemory(ASM(23), ObjPtr(Me), 4&)
'  Call CopyMemory(ASM(28), lng, 4&)

  Call CopyMemory(ASM(15), lng - VarPtr(ASM(20)), 4&)

(только ногами не бей)
15 это начало синего
А 20 - это откуда пересчитать?


Смысл моей правки заключается в следующих вещах:
В коде многократно используется push 0 да и вообще 0. При этом push 0 это 6800000000.

1) Я вижу, что в коде совершенно не используются регистры. На основнии этого, я вставляю в начало кода xor eax, eax (31C0), который очищает eax, а вместо 5-байтового push 0, ставлю везде push eax ( 58 )
2) Я вижу, что автор кода сохраняет в стеке ebp, и помещает в ebp значение esp, чтобы обращаться к стеку через ebp. Я не понимаю, кому и зачем это нужно. Код не настолько сложен, чтобы делать адресацию через ebp, поэтому адресация ebp вырезается к черту.
3) Методы класса отличаются от обычных процедур тем, что они неявно принимают в качестве первого аргумента указатель на me, а в качестве второго - указатель на место, куда надо положить возвращаемое значение. При этом действительно возвращаемое значение является hresult-ом, и используется для механизм обработки ошибок.
    Небольшое отступление.
    Кстати, если тебя устроит вместо
    zLVCallBack = CallWindowProc(...)
    писать
    Err.Raise CallWindowProc(...)
    то код можно вообще сократить до 58680000000050E900000000
Автор, для этой переменной-приёмника отводит место в стеке с помощью add esp, -4 (83C4FC) (это вместо напрашивающегося sub esp, 4 (83EC04), хех :) ).
Я для этих целей использую push eax ( 58 ). Это хоть и чуть медленнее, но, во-первых, короче, а во-вторых (самое главное) -- правильнее. Возврат в этом случае детерминирован -- он будет 0 если ты в функции забудешь вернуть значение. У автора будет возвращего случайное число.

Во-вторых, автор вычисляет адрес переменной-приёмника и кладёт этот адрес в стек так:
Код: Выделить всё
8D45 FC       LEA EAX, DWORD PTR SS:[EBP-4]
50            PUSH EAX

а я так:
Код: Выделить всё
54            PUSH ESP


В-третьих, автор берёт из переменной-приёмника значение и помещает его в EAX так:
Код: Выделить всё
8B45 FC       MOV EAX, DWORD PTR SS:[EBP-4]
а я так:
Код: Выделить всё
58            POP EAX


В-пятых, автор делает вызов через связку:
mov eax, 00000000
сall eax

а я просто:
call 00000000

С разницей только в том, что я записываю вместо красного не абсолютное, а относительное смещение.

Ну и т.д.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 05.06.2008 (Чт) 21:39

Antonariy
Да я с тобой согласен абсолютно. И вообще - не люблю использовать чего-нибудь, не до конца понятного. Но тут уж очень как-то волшебно - вот я и надеялся, что мне объяснят фокус и успокоят, что, мол там внутри всё нормально. Да и работал код на ура - и если бы не DEP-unsafe ... Короче, Абыдна

Хакер
Спасибо! читал, и даже возникала иллюзия, что чего-то чуть ли не почти вот-вот пойму... :oops: В любом случае, надеюсь, это окажется полезным тем, кто в этом понимает :lol:

Err.Raise CallWindowProc(...)
Эта штука должна возвращать значение из функции? Раз это работает, почему бы не попробовать.

Но всё-таки - как именно вживить твой код в пример? Красное должно замениться на ObjPtr(Me)? А синие на что?

Дело здесь, очевидно, в кривом управлении памятью
Ну да - скорее всего :oops: Я отматал пока всё к просто байтовому массиву - если у меня получится померить твою вставку с этим кодом (может всё же сделаешь исключение - ткнёшь носом, как надо?), тогда и начну разбираться, чего у меня там не так с памятью.

А кстати - возможно ли для той же цели вообще обойтись без асма? Можно ли получить адрес функции, которая находится не в модуле, а в классе? Ведь тогда можно было бы сделать то же самое без всяких вставок? или нет?
Артур
 
   

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 05.06.2008 (Чт) 21:55

Эта штука должна возвращать значение из функции?
Только отрицательные. А вот положительные вернуть вообще не получится.
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 05.06.2008 (Чт) 22:05

Но всё-таки - как именно вживить твой код в пример? Красное должно замениться на ObjPtr(Me)? А синие на что?

Вместо красного лонга надо записывать ObjPtr(Me).
Вместо синего лонга надо записывать смещение (абсолютное или относительное, в зависимости от автора кода) метода в класса.
Абсолютное смещение это адрес метода.
Относительное - [адрес метода] минус [адрес следующего байта после синего лонга].
Ну да - скорее всего Я отматал пока всё к просто байтовому массиву - если у меня получится померить твою вставку с этим кодом (может всё же сделаешь исключение - ткнёшь носом, как надо?), тогда и начну разбираться, чего у меня там не так с памятью.

Я не вижу связи. Можно оставить авторский (ужасный) вариант, и разобраться с выделением/освобождением памяти. Можно использовать мой, и не разбираться. Какая связь-то?

Я тебе предложил классный, на мой взгяд, вариант с кучей.
Тот вариант, который сейчас у тебя - с VirtualAlloc / VirtualFree -- отвратителен. Даже будучи поправленным, он останется отвратительным. Отвратительным по той причине, что на 20-40 байт выделяется страница. Т.е. реально используется от 10% до 5%. Потери памяти при этом - 90% и 95%.

Я понимаю, что памяти много. Но разве это повод так тупо её растрачивать? Для некоторых, конечно, возможность не заморачиваться намного ценнее возможности рационально использовать память. Для меня нет. Как будет для тебя -- решай.

Собственно, даже свой вариант я нахожу отстойным. Заметь, что для каждого экземпляра класса создаётся почти одинаковый переходник. Я бы воспользовался этим моментом. Однако в этом случае получилось бы медленнее, чем сейчас. Но, возможно, был бы выигрышь за счёт кеширования.

Вариант с кучами хорош тем, что память используется рационально. Если сделать размер переходника кратным размеру страницы (т.е. чтобы 4096 делилось на размер переходника), то будет вообще прекрасно. Образовавшиеся дырки от освободившихся переходников будут заняты новыми.

Ты, конечно, можешь, написать на основе VirtualAlloc/VirtualFree свой механизм куч, но вряд ли он будет лучше имеющегося.

А кстати - возможно ли для той же цели вообще обойтись без асма? Можно ли получить адрес функции, которая находится не в модуле, а в классе? Ведь тогда можно было бы сделать то же самое без всяких вставок? или нет?

Или нет. Без асма обойтись нельзя.
Функция в классе Class1:
Код: Выделить всё
Public Function WindowProc(byval hwnd&, byval msgid&, byval a&, byval b&) as long
      WindowProc = 5
End Function


будет на самом деле скомпилирована как:
Код: Выделить всё
Public Function WindowProc(ByVal Me As Class1, byval hwnd&, byval msgid&, byval a&, byval b&, byref RetVal&) as long
     RetVal = 5
     WindowProc = S_OK
End Function


Переходник и предназначен главным образом для того, чтобы функция в классе получила свои 6 параметров, вместо 4, которые передаются обычному WindowProc-у.

Переходник просто добавляет доп. два параметра в стек. Кстати, эти два параметра - один красный, один синий. Т.е. теперь, думаю, ясен смысл выделения цветом некоторых байтв в коде?

З.Ы. Вот мне пришло уведомление, пока я писал...
Что свидетельствует о том, что кто-то написал в эту тему. Так что если там написано то же самое, что написал здесь я, что-ж, не стирать же мне всё?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 05.06.2008 (Чт) 22:22

Начал долбиться своим любимым методом тыка:

Логично предположить, что в этом коде уже есть получение адреса функции, причём без всякого асма. И потом этот адрес записывается в асмовскую вставку. А асм работает уже с готовым адресом.

То есть, скорее всего, это где-то здесь:

Код: Выделить всё
  Call CopyMemory(tPtr, ByVal ObjPtr(Me), 4&)
  Call CopyMemory(lng, ByVal tPtr + &H1C + (4& * ProcNumber), 4&)

Я предположил, что теперь в lng как раз нужный адрес
Код: Выделить всё
   OldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, lng

Увы, мои рассуждения оказались неверны - бейсик умер. :(

[/list]
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 05.06.2008 (Чт) 22:26

Ты глупо ошибся. (Глупо ошибся, это когда ошибся нисколько не думая. Я понимаю, что метод тыка основан на ошибках, но надо же хоть чуть-чуть думать куда тыкать?) У тебя lng указывает (если ты ничего не менял) на начало асм-кода, а записывать адрес процедуры надо поверх синего, а синее отстаёт от начала асм-кода, т.е. от lng на <посчитай сам на сколько, а?> байт.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 05.06.2008 (Чт) 22:34

Хакер
Или нет. Без асма обойтись нельзя.

Ну вот, опять облом :lol:

Я не вижу связи. Можно оставить авторский (ужасный) вариант, и разобраться с выделением/освобождением памяти. Можно использовать мой, и не разбираться. Какая связь-то?
Ни какой. Собираюсь оставить твой, если получится. Но ведь разбираться всё-равно придётся?
Я тебе предложил классный, на мой взгяд, вариант с кучей.
Тот вариант, который сейчас у тебя - с VirtualAlloc / VirtualFree -- отвратителен
Я и буду делать с кучами - если вообще буду делать. Просто пока - временно - отматал к просто массивам, чтобы доразбираться в самой вставке. А то не очень удобно - всё время падает. :)
Артур
 
   

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 05.06.2008 (Чт) 22:40

Ты глупо ошибся. (Глупо ошибся, это когда ошибся нисколько не думая. Я понимаю, что метод тыка основан на ошибках, но надо же хоть чуть-чуть думать куда тыкать?) У тебя lng указывает (если ты ничего не менял) на начало асм-кода, а записывать адрес процедуры надо поверх синего, а синее отстаёт от начала асм-кода, т.е. от lng на <посчитай сам на сколько, а?> байт.
Нет, я просто попробовал вообще закомментировать весь асм и оставить только то, что процетировал. То есть, в lng там попадало tPtr плюс смещение до нужной процедуры. Но в свете того, что
будет на самом деле скомпилирована как
это уже значения не имеет :D
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 05.06.2008 (Чт) 22:46

А я бы советовал пользоваться тем методом, которым я пользовался в FNDLL1.

Плохо только то, что я уже сам плохо помню, каким методом я там пользовался, и у меня могло сложиться ошибочное впечатление, что именно тем, какой хочу предложить тебе. А смотреть лень.

Потому проиллюстрирую метод, который используется в FNDLL2 (разработку которого, надо сказать, я планировал закончить в марте, а после взяться за заказ А и закончить его к середине апреля, потом за заказ Б, а потом 1-го июня засесть за свой грандиозный проект. Сейчас 5-е июня, но FNDLL2 так и не закончен, как не закончен заказ А. Задница, да? :roll: ):

Код: Выделить всё
        Injection(c) = &H8D     'lea    ecx, [ebp+<RVA интерцептора>]
        Injection(c) = &H83
        e.AdjustorRVA = c(3)
        Injection(c) = &HC6
        Injection(c) = &H0
        Injection(c) = &HE8     ' mov   byte[eax], 0xE8
        Injection(c) = &H40     ' inc   eax
        Injection(c) = &HC7
        Injection(c(4)) = &H0   ' mov   dword[eax], <rel: к функции от интерцептора>
    Next e
    Injection(c) = &H6A     ' push  0x00000000
    Injection(c) = &H0
    Injection(c) = &H6A     ' push  0x00000005
    Injection(c) = &H5
    Injection(c) = OPC_PUSH_EBX
    Injection(c) = &HE8
    ioRel2UDEP_FromIcptr = c(3)
    'InterceptorFinal:
    Injection(c) = OPC_POP_EBX
    Injection(c) = &H83     ' sub   dword[esp], 5
    Injection(c) = &H2C
    Injection(c) = &H24


Вот здесь у меня c это Dim c as CCounter.
А CCounter - это класс, с дефолтным свойством, которое при каждом обращении увеличивается на единицу.

Но это у меня так, и оно так только потому что плинировалось использование сразу нескольких авто-счётчиков.

Если счётчик один, то c может быть функцией, со static переменной, которая при каждом вызове увеличивается и возвращается.

Ну, вобщем-то, это такая грубая попытка сымитировать / заменить чем-то недостяющего i++.

Как видишь, смещения интересующих меня мест запоминаются в нужные мне переменные (ioRel2UDEP_FromIcptr).

Потом, когда мне надо будет обратиться к такому-то месту, я просто прибавлю ioRel2UDEP_FromIcptr свой аналог твоего lng, и запишу в память по полученному адресу нужное число.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 05.06.2008 (Чт) 23:17

Вот пытаюсь (правильно?):
Код: Выделить всё
  Call CopyMemory(tPtr, ByVal ObjPtr(Me), 4&)
  Call CopyMemory(lng, ByVal tPtr + &H1C + (4& * ProcNumber), 4&)

'   0  1  2  3  4  5  6  7    8  9 10 11 12 13 14 15
'  -------------------------------------------------
'  31 C0 50 54 8D 48 04 FF   74 24 18 E0 FA 68 хх хх
'  хх хх E8 хх хх хх хх 58   C2 10 00

  Call CopyMemory(ASM(14), ObjPtr(Me), 4&)
 
'  Относительное - [адрес метода] минус [адрес следующего байта после синего лонга].
  Call CopyMemory(ASM(19), lng - VarPtr(ASM(23)), 4&)
 
  OldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, VarPtr(ASM(0)))

Увы - бейсик вмер
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 05.06.2008 (Чт) 23:22

Ээ.. Т.е. я правильн понял, что сначала первые 4 байта кода портятся числом, возвращённым функцией ObjPtr? И делают этот код полной кашей мусора?

Что здесь вообще делают две первых строчки кода?

Смещение высчитано неправильно. Адрес метода -- это не lng. lng это адрес начала кода.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 05.06.2008 (Чт) 23:27

Извини, у меня получается что читаю следующий пост, когда отправляю свой, поэтому и выходит такое приторможенное реагирование 8) (сижу на модеме - прям прошлый век, ей-богу)

Попробую разобраться и отвечу завтра, а то засыпаю уже - у меня уже 3:26 :)
Артур
 
   

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 05.06.2008 (Чт) 23:37

Первые две строки ничего не делают с асмом, они до асма. Они из оригинала. Их суть я недопонимаю, но, мне казалось, угадываю. Я так думал, что они должны получить адрес процедуры?
Код: Выделить всё

  Dim lng As Long
  Dim tPtr As Long
  Call CopyMemory(tPtr, ByVal ObjPtr(Me), 4&)
  Call CopyMemory(lng, ByVal tPtr + &H1C + (4& * ProcNumber), 4&)
Артур
 
   

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 06.06.2008 (Пт) 10:27

Хакер
я бы советовал пользоваться тем методом, которым я пользовался в FNDLL1

Ты несколько переоцениваешь мои познания в программировании: про самоувеличивающийся счётчик я понял (мало того, у меня самого есть подобный счётчик), и пользоваться им действительно удобно. Но асма я не знаю! Совсем не знаю, даже в общих чертах, так что как это использовать здесь, я не понял :)

Сейчас 5-е июня, но FNDLL2 так и не закончен, как не закончен заказ А. Задница, да? ):
Да уж. У меня, кстати, тоже бывает нечто подобное. Какой-нибудь простенький макрос для ворда (с которых у меня когда-то всё и началось) может трансформироваться в десять, потому что в процессе ковыряния одной идеи вдруг выколупывается другая, требующая немедленного воплощения в ущерб первой, и на уточнениях и забираниях в сторону, когда к финишу все же добираются хотя бы три из тех десяти отпочковавшихся, меня вдруг выносит на поиск подходящих иконок на кнопки, а процесс загоняния больших картинок в размер 16х16 оказывается настолько увлекательным, что это новое занятие тотчас тоже начинает дробиться и почковаться. 8)

У меня есть проект, которому я, по большому счёту, и обязан тем, что увлёкся программированием. Ему уже больше десяти лет. Тогда хотелось по-быстренькому наваять как получится простенькую обучалку для малолеток, но по-быстренькому не получилось, а процесс затянул и увлёк. Обучалка концептуально (не только программно) неимоверно усложнилась и расширилась. Пять лет назад мне казалось, что я уже вот-вот допишу. Три года назад, буквально за месяц до предполагаемого финиша, даже купил доменное имя под это дело. Но прошёл и месяц, и три года, проект ещё по меньшей мере трижды кардинально поменял несколько своих концепций, в нём доистребились остатки сумбура 8-7-летней давности и допонялись или полностью заменились куски кода, которые были мне не понятны. В конце того года на меня напал ступор и уже не в первый раз посетило сизифовское отчаяние. Проект в очередной раз забросился.

За те несколько месяцев, пока я на форуме, я разобрался в очень многих программерских вещах... но это вряд ли мне поможет. Скорее, наоборот - я уже вижу, как исправить и улучшить многие куски моей программы, уже предвкушаю новые открывающиеся возможности... А некоторые части уже начали меня раздражать своей неуклюжестью. Но их переделка - это опять «до основанья а затем». Надо бы остановить себя и закруглить-таки то, что уже есть и в том виде, в каком есть... Но для начала - надо бы вообще к проекту вернуться.

Уф-ф, выговорился - даже полегчало
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 06.06.2008 (Пт) 12:35

Во-первых.
Ы! (ы-факториал)

Ты несколько переоцениваешь мои познания в программировании: про самоувеличивающийся счётчик я понял (мало того, у меня самого есть подобный счётчик), и пользоваться им действительно удобно. Но асма я не знаю! Совсем не знаю, даже в общих чертах, так что как это использовать здесь, я не понял

Я, когда указал в сторону FNDLL2, хотел прорекламировать не кусочек асм-кода, а способ заполнения массива кодом.

И способ точно узнать, куда потом надо записать свои данные, чтобы ничего не упало.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 06.06.2008 (Пт) 14:19

Я, когда указал в сторону FNDLL2, хотел прорекламировать не кусочек асм-кода, а способ заполнения массива кодом.
Я всё равно не понял. Думаю, что тем, кто сам делает такие вставки, этот способ понятен и полезен. Но я-то такие вставки не делаю и не планирую делать :oops: Асм для меня - это вообще что-то мистическое и уму не растяжимое.

Вставка, которую ты предлажил, всего в 27 байт. Я специально и расписал табличкой, чтобы не просчитаться. Так что если я что-то не в те байты записал, то значит не просчитался, а понял неправильно.

Или я неправильно получил адрес метода. А тогда как получить правильно? И что тогда делают те две присловутые строки в оригинале?

Насчёт Ы - ты уже давно меня убедил в этом ы, и даже статью твою я уже читал. Но в оригинале я специально ничего не меняю - чтобы потом не отгдадывать, стало ли всё падать от того, что я "исправил" что-нибудь криво, или почему-нибудь другому. Я здесь-то не до конца понимаю, например, когда и зачем в копимемери нужно байвал, а когда байреф и чего куда там копируется 8)

Сначала нужно разобраться с принципиальной частью. Я и к байтовым массивам вернулся, и на гетМем пока не заменяю - именно поэтому. Как удасться вживить твою вставку, чтобы заработала, тогда и буду всё остальное пытаться переделать.

Только кажется, что ни чего из этой затеи у меня не выйдет.
Артур
 
   

След.

Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: SemrushBot и гости: 6

    TopList