Чем вам не мил GetMem?

Хакер дает советы, раскрывает секреты и делится своими мыслями по поводу программирования.

Модератор: Хакер

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

Чем вам не мил GetMem?

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

Warning! Страница разъехалась, и часть матерала уехала вправо. Если у вас разрешение экрана 1024x768, будьте внимательны. Рекомендуется прокрутиться максимально вправо и читать текст.

  • Всех программистов можно разделить на три категории:
    1. Программисты, которым не нужен прямой доступ к памяти.
    2. Программисты, которым часто требуется прямой доступ к памяти, и выбранный ими язык позволяет легко это осущеставлять.
    3. Программисты, которым часто требуется прямой доступ к памяти, но выбранный язык не позволяет им легко производить его.
  • Всех VB-программистов можно отнести к последней категории. Здесь надо уточнить, что "часто требуется" - это понятие относительное, и у каждого это "часто" своё.
  • Все случаи комирования -- это либо копирование блоков (причём иногда довольно больших), либо копирование значений каких-то переменных. В последнем случае, копируется обычно 4 байта, т.е. DWORD аля переменная типа Long (или String, ну или Single, но это редко).
    Относительно этого всех программистов можно разелить на три категории:
    1. Программисты, которые для копирования 4 байт используют GetMem4.
    2. Программисты, которые используют для копирования 4 байт памяти CopyMemory и не знают о GetMem4.
    3. Программисты, которые знают о GetMem4, но всё равно используют CopyMemory для копирования 4 байт.
  • С первыми все ОК, вторых достаточно направить в сторону описания функции GetMem4, а вот третьи...
  • Я не могу знать, почему третья категория делает так, но предполагаю, что причина в том, что люди не видят разницу, - считают, что это одно и то же.
  • Посему, давайте посмотрим, что делает GetMem4.
    На заметку
    Для тех, кто не в курсе: регистр - это такая ячейка в процессоре, способная хранить данные.


    (Обе функции принимают src и dst -- адреса откуда копировать и куда копировать. CopyMemory принимает ещё и length -- сколько копировать).
    Что делает GetMem4:
    1. В eax записывается src.
    2. В ecx записывается dst
    3. В eax записываются 4 байта по адресу, хранящемуся в eax.
    4. В память, по адресу, хранящемуся в ecx, записывается значение (те самые 4 байта), которые хранятся в eax.

    А теперь, что делает CopyMemory:
    1. Сохраняет значение esi в памяти
    2. Сохраняет значение edi в памяти
    3. В esi записывается src
    4. В edi записывается dst
    5. В ecx копируется length
    6. Число в esi сравнивается с числом в edi (т.е. по сути сравниваются src и dst).
    7. Если esi оказывается меньше и равен edi, то выполнение переходит на пункт 18.
    8. Число из ecx копируется в edx.
    9. К числу в edx применяется битовая and-маска 3. Таким образом в edx получается остаток от деления прежнего значения edx на 4.
    10. Запускается циклическое копирование блоков по 4 байта из esi в edi по ecx блоков. При этом, ecx на каждом шаге уменьшается на единицу. И по когда в ecx получается число 0, циклическое копирование заканчивается.
    11. В ecx записывается значение edx.
    12. Проверяется, а не равен ли случайно edx нулю. И если равен (т.е. если размер копируемого блока был кратен 4 и всё уже скопировалось), то выполнение переходит к пункту 15.
    13. Запускается циклическое копирование байтов из esi+ecx в edi + ecx. ecx при этом, опять же, уменьшается на единицу, и так происходит, пока ecx не станет равен нулю.
    14. Осуществляетя переход к пункту 15.
    15. Из памяти достаётся и записывается прежнее значение edi.
    16. Из памяти достаётся и записывается прежнее значение esi.
    17. Всё, CopyMemory на этом заканчивается.
    18. Хранящееся в ecx число (т.е. length) сранивается с 0. И если оно равно нулю, выполнение переход к пункту 15.
    19. В eax копируется edi (а там у нас dst).
    20. От eax отнимается esi (а там - src)
    21. Результат, получившийся в eax, сравнивается с ecx (а там - длина копируемого блока). Таким нехитрым образом проверяется, пересекаются ли копируемые области.
    22. И если ecx был меньше и равен результата в eax, то (то блоки пересекаются) выполнение передаётся на пункт 8.
    23. В противном случае (если блоки пересекаются), устанавливается флаг DF, который определяет, что циклическое копирование будет выполнено справа навлево.
    24. К esi прибавляется ecx.
    25. К edi прибавляется ecx.
    26. Осуществляется циклическое побайтовое копирование (Ахтунг! Именно побайтовое. Глючит либо меня, либо программистов MS, которые этот код писали. Что если мне нужно скопировать блок размером в мегабайт, и область [откуда] с областью [куда] пересекается всего-лишь одним байтом? Весь мегабайт будет копироваться побайтно?)
    27. Флаг DF сбрасывается.
    28. Выполнение переходит на пункт 15.
  • Чтобы чуть лучше показать вам разницу, позволю себе вставить картинку с блок-схемами обеих функций (картинка хоть и большая, весит всего 24 кб), сравните:
Изображение
  • А теперь скажите мне, те, кто используют вместо GetMem4 функцию CopyMemory. Вы хоть отдаёте себе отчёт, сколько лишних действий вы производите?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

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

Сообщение Viper » 06.06.2008 (Пт) 14:35

Есть еще одна категория проггеров: которые раньше не знали о GetMem4 (и прочих GetMemN, а также и о PutMemN). А теперь знают и стараются использовать именно эти функции там, где нужны действительно они. Но тут имеется ньюанс, что подчас имеется куча уже готовых кирпичиков с использованием CopyMemory, которые и используются. Понятно, что сии кирпичики надо переделывать, но ведь лень и все такое прочее .... :evil:
Весь мир матрица, а мы в нем потоки байтов!

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

Сообщение Viper » 10.06.2008 (Вт) 12:06

В продолжение темы. Помимо упомянутых GetMem1, GetMem2, GetMem4 и GetMem8, а также PutMem1, PutMem2, PutMem4 и PutMem8 существуют еще и GetMemStr, GetMemObj и GetMemVar (ну и PutMemStr, PutMemObj и PutMemVar), которые, понятное дело, делают тоже самое со строками, объектами и типом Variant.

Но и это еще не все. Существуют также загадочные:
SetMemObj, SetMemVar, SetMemEvent
GetMemEvent, PutMemEvent
GetMemNewObj, PutMemNewObj, SetMemNewObj.

Это то что за зверье?
Весь мир матрица, а мы в нем потоки байтов!

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 10.06.2008 (Вт) 13:13

SetMemObj, SetMemVar - эти ещё и IUnknown::AddRef вызывают.
GetMemEvent, PutMemEvent - что-то связано с ловлей событий, нам недоступно.
Изображение

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

Сообщение ANDLL » 10.06.2008 (Вт) 14:58

GetMemEvent, PutMemEvent - что-то связано с ловлей событий
Ничего подобного :)
Код: Выделить всё
.text:72795C8B GetMemObj:
.text:72795C8B                 mov     eax, [esp+4]
.text:72795C8F                 mov     ecx, [esp+8]
.text:72795C93                 mov     eax, [eax]
.text:72795C95                 test    eax, eax
.text:72795C97                 mov     [ecx], eax
.text:72795C99                 jz      short loc_72795CA1
.text:72795C9B                 mov     ecx, [eax]
.text:72795C9D                 push    eax
.text:72795C9E                 call    dword ptr [ecx+4]
.text:72795CA1
.text:72795CA1 loc_72795CA1:                           ; CODE XREF: .text:72795C99j
.text:72795CA1                 xor     eax, eax
.text:72795CA3                 retn    8
Код: Выделить всё
.text:72795D5F GetMemEvent:
.text:72795D5F                 mov     eax, [esp+0Ch]
.text:72795D63                 mov     ecx, [esp+10h]
.text:72795D67                 mov     eax, [eax]
.text:72795D69                 test    eax, eax
.text:72795D6B                 mov     [ecx], eax
.text:72795D6D                 jz      short loc_72795D75
.text:72795D6F                 mov     ecx, [eax]
.text:72795D71                 push    eax
.text:72795D72                 call    dword ptr [ecx+4]
.text:72795D75
.text:72795D75 loc_72795D75:                           ; CODE XREF: .text:72795D6Dj
.text:72795CA1                 xor     eax, eax
.text:72795CA3                 retn    10h
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

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

Сообщение Хакер » 10.06.2008 (Вт) 18:46

Вобщем-то цель статьи была не рассказать о недокументированных функциях (частично это сделал GSerg), а высказаться против использования CopyMemory для копирования DWORD-ов.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 10.06.2008 (Вт) 19:51

ANDLL
И что доказывет сей асмовый листинг? Как он может подтвердить/опровергнуть связь этих функций с IConnectionPoint и иже с ним?
Изображение

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

Сообщение ANDLL » 10.06.2008 (Вт) 21:12

Как он может подтвердить/опровергнуть связь этих функций с IConnectionPoint и иже с ним?
Эмм, а тебе что, не знаком асм? И ты не видишь что делает GetMemEvent?
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

BV
Thinker
Thinker
Аватара пользователя
 
Сообщения: 3979
Зарегистрирован: 12.09.2004 (Вс) 0:55
Откуда: Молдавия, г. Кишинёв

Сообщение BV » 10.06.2008 (Вт) 22:31

Чем вам не мил GetMem?


Тем, что его нет в CRT. memcpy рулит, да.
char *out = "|*0>78-,+<|"; size_t cc = char_traits<char>::length(out);
for (size_t i=0;i<cc;i++){cout<<static_cast<char>((out[i]^89));}cout<<endl;

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 11.06.2008 (Ср) 19:54

ANDLL
Я пока что вижу, что оно берёт из стека 2 адреса, копирует 4 байта с одного на другой и, если они не 0 вызывает IUnknown::AddRef. Как это связано с моим заявлением о связи с ловлей событий - ума не приложу.
Изображение

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

Сообщение Хакер » 11.06.2008 (Ср) 19:57

BV
Сишникам вход запрещён :) .

Хотя бы потому, что там (в сях) копирование дворда осуществимо средствами языка и проблема, описываемая в этой статье совершенно не актуальна для сишников.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

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

Сообщение ANDLL » 11.06.2008 (Ср) 20:18

Как это связано с моим заявлением о связи с ловлей событий - ума не приложу.
omg
Никак не связано.
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

Proxy
Профессор VB наук
Профессор VB наук
Аватара пользователя
 
Сообщения: 2855
Зарегистрирован: 31.08.2007 (Пт) 4:41

Re: Чем вам не мил GetMem?

Сообщение Proxy » 17.08.2008 (Вс) 21:29

Чем вам не мил GetMem?

Хз. Иногда полезен, когда требуется поместить, скажем в переменную типа long значения из известного адреса размещения переменной в другом приложении.
НО: обращение к памяти на низком уровне у третей, из вышеописанных групп программистов, чаще всего требуется всё же для работы с большими блоками памяти. Например для помещения в массив (тут не одна манипуляция на низком уровне) байтов адресного пространства целого процесса (сори за кривость изложения). Например дебаггер на одних только GetMemX сложно себе представить. Всему своё место.

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

Re: Чем вам не мил GetMem?

Сообщение Хакер » 17.08.2008 (Вс) 23:06

Ума не приложу, зачем ты выключаешь в своих сообщениях BBCode-ы. Чтобы я их потом включал? Или лень исправить настройку в профиле?

Proxy писал(а):
Чем вам не мил GetMem?

Хз. Иногда полезен, когда требуется поместить, скажем в переменную типа long значения из известного адреса размещения переменной в другом приложении.

Хе-хе. Извини, но это чушь. Переменную "из другого приложения" достать толко с помощью ReadProcessMemory.
НО: обращение к памяти на низком уровне у третей, из вышеописанных групп программистов, чаще всего требуется всё же для работы с большими блоками памяти.

Всё-же я видел на этом форуме тысячи подтверждений тому, что для 4-байтных переменных юзает всё что угодно, кроме GetMem,
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Proxy
Профессор VB наук
Профессор VB наук
Аватара пользователя
 
Сообщения: 2855
Зарегистрирован: 31.08.2007 (Пт) 4:41

Re: Чем вам не мил GetMem?

Сообщение Proxy » 18.08.2008 (Пн) 9:43

Сори. Разумеется напутал.

А что касается BB кодов: я не выключал(
Вот так сейчас:
BBCode всегда включён: да


Вернуться в МануAll

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

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

    TopList