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

Чудеса на виражах VirtualProtectEx


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

Буду краток. Имеется игра, хочется с ней сделать N. С определенным модулем. Получаю base address модуля, получаю его же размер. Делаю OpenProcess с правами на все (sic!), дальше хочу в свой процесс скопировать весь модуль целиком. По шагам:

0. VirtualAlloc, *размер_модуля*, COMMIT, RWE
1. VirtualProtectEx, pID, *базовый_адрес_модуля*, *размер_модуля*, RO
2. RPM, pID, *базовый_адрес_модуля*, *адрес из п.0*, *размер_модуля*

Падаю уже на п. 0 - ERROR_INVALID_ADDRESS. Отладчиком смотрел - адрес валиден. RPM падает с ERROR_PARTIAL_COPY. Если копировать не весь модуль, а только кусочек (проверял на 64 байтах) - все в порядке. Размер модуля верен, pID тоже. На другом процессе этот же код молча отрабатывает. Вопрос - что еще придумать? :D

 

Взываю к @gmz и @Xipho

 

Вот еще пара скриншотов:

0.png.6d53579881247bf3c982d67055c68b79.p

1.thumb.png.a20c70c18711bdf96e358e2c4cbb

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

6 минут назад, keng сказал:

0. VirtualAlloc, *размер_модуля*, COMMIT, RWE

VirtualAllocEx  со ссылкой на свой модуль не пробовал?

 

2 минуты назад, Garik66 сказал:

:D Все дороги ведут к gmz. Я тоже часто обращаюсь либо к нему, либо к MasterGH

Вот щас обидел, да.

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

4 minutes ago, Xipho said:

VirtualAllocEx  со ссылкой на свой модуль не пробовал?

 

Вот щас обидел, да.

Не-не-не, я в *свой* процесс копирую модуль извне. Или это очередная фича, а не баг?

 

PS: Пришло в голову, что головной exe-модуль нельзя просто так весь ставить в RO, попробовал RWE - теперь VirtualProtectEx отдает ERROR_NO_ACCESS. Само собой, все запускается от админа и бла-бла-бла.

PPS: ERROR_NOACCESS - мой косяк, неправильно dwOldProtect передавал. Все еще ERROR_INVALID_ADDRESS.

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

2 минуты назад, Xipho сказал:

VirtualAllocEx  со ссылкой на свой модуль не пробовал?

 

Вот щас обидел, да.

offtopic.gif:D И конечно к Xipho - но это уже по умолчанию, как само собой разумеющееся.

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

7 минут назад, keng сказал:

PS: Пришло в голову, что головной exe-модуль нельзя просто так весь ставить в RO, попробовал RWE - теперь VirtualProtectEx отдает ERROR_NO_ACCESS. Само собой, все запускается от админа и бла-бла-бла.

PPS: ERROR_NOACCESS - мой косяк, неправильно dwOldProtect передавал. Все еще ERROR_INVALID_ADDRESS.

Завтра бы в обед, если будет время, обсудить кусок кода. Интересный момент получается.

 

6 минут назад, Garik66 сказал:

offtopic.gif:D И конечно к Xipho - но это уже по умолчанию, как само собой разумеющееся.

Да-да-да... ))

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

1 minute ago, Xipho said:

Завтра бы в обед, если будет время, обсудить кусок кода. Интересный момент получается.

 

Да-да-да... ))

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

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

Проблема скорее не в VirualProtectEx, а в том что ты в рабочем модуле попадаешь на зарезервированный кусок памяти ( MEM_RESERVE  и только) на что ReadProcessMemory дает пинка под зад, но ведь ее нельзя за это осуждать, как бы ты повел на ее месте?

 

Советую скопировать модуль по частям, то есть заголовки, секции и прочая лабуда 

Более простой и костыльный вариант это проверять каждую страницу памяти подопытного при помощи VirtualQueryEx и все что является MEM_COMMIT - копировать

 

Я так полагаю ты дампер пишешь, тогда не буду предлагать вариант с CreateFile.

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

3 hours ago, Dino said:

Проблема скорее не в VirualProtectEx, а в том что ты в рабочем модуле попадаешь на зарезервированный кусок памяти ( MEM_RESERVE  и только) на что ReadProcessMemory дает пинка под зад, но ведь ее нельзя за это осуждать, как бы ты повел на ее месте?

 

Советую скопировать модуль по частям, то есть заголовки, секции и прочая лабуда 

Более простой и костыльный вариант это проверять каждую страницу памяти подопытного при помощи VirtualQueryEx и все что является MEM_COMMIT - копировать

 

Я так полагаю ты дампер пишешь, тогда не буду предлагать вариант с CreateFile.

Вариант, конечно, да и смещения будет просто пересчитать, но меня больше интересует, нафига так сделали. Т.е. лично мне впервые такое в игре попадается. Ночью еще подумалось, что это могут быть господа репакеры, но не понимаю смысла в подобном "антидампе" в игре.

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

пхах получается между хидером и 1 секцией есть дыра в 0x1000? значит там пакер какой то?

а размер модуля из SizeOfImage?

покажи еще скрин секций из cffexplorer

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

54 minutes ago, gmz said:

пхах получается между хидером и 1 секцией есть дыра в 0x1000? значит там пакер какой то?

а размер модуля из SizeOfImage?

покажи еще скрин секций из cffexplorer

Размер - 0х1AA000, точнее посмотрю уже дома - на маке сложновато. :D

Пакер, кстати, тоже надо бы проверить - я все больше грешу на репакеров.

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

а размер модуля из SizeOfImage?
покажи еще скрин секций из cffexplorer

с Sizeofimage все в порядке он учитывает эту зарезервированную область

Изменено пользователем Dino
Ссылка на комментарий
Поделиться на другие сайты

Если предположить что загрузчик для каждой области резервирует память отдельно, то это все объясняется тем что адреса должны быть кратны 64кб, именно поэтому дистанция между заголовками и секциями 0xf000. Но это не точно

Изменено пользователем Dino
Ссылка на комментарий
Поделиться на другие сайты

3 часа назад, Dino сказал:

сли предположить что загрузчик для каждой области резервирует память отдельно, то это все объясняется тем что адреса должны быть кратны 64кб, именно поэтому дистанция между заголовками и секциями 0xf000. Но это не точно

в норм PE 4к выравнивание

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

48 минуты назад, gmz сказал:

в норм PE 4к выравнивание

Адрес при резервировании должен быть выровнен на 64к, а 4к это размер страницы при ее выделении

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

Используя тайную технику костылирования, я на скорую руку набросал вот это:

Spoiler

proc GetBadPages uses ebx edx ecx edi,lpModule,lpOpt
locals
    pID dd ?
    pHandle dd ?
    pMbi MEMORY_BASIC_INFORMATION ?
    rgnSize dd ?
    endAddr dd ?
endl
    mov     eax, [lpModule]
    mov     eax, [eax + MODULEENTRY32.th32ProcessID] ; eax = Process ID
    mov     [pID], eax
    invoke  OpenProcess, PROCESS_QUERY_INFORMATION, 0, [pID]
    .if     eax = 0
        jmp     .ERROR
    .endif
    mov     [pHandle], eax ; Save opened process handle
    ;
    mov     ebx, [lpModule]
    mov     ecx, [ebx + MODULEENTRY32.modBaseAddr] ; ecx = Module Base Address
    mov     [endAddr], ecx
    mov     ebx, [ebx + MODULEENTRY32.modBaseSize] ; ebx = Module Base Size, in bytes
    add     [endAddr], ebx ; endAddr = Module Base Address + Module Base Size
.LOOP:
    push    ecx ; Saving MBA
    lea     edx, [pMbi] ; edx = address of MEMORY_BASIC_INFORMATION struct
    invoke  VirtualQueryEx, [pHandle], ecx, edx, sizeof.MEMORY_BASIC_INFORMATION ; Trying to get info about the 1-st region
    .if     eax = 0
        jmp     .ERROR
    .endif
    lea     edx, [pMbi]
    mov     eax, [edx + MEMORY_BASIC_INFORMATION.RegionSize] ; Getting region size, in bytes
    .if     eax = 0 ; If it's zero - quit, we've got no access for some reason
        jmp     .ERROR
    .endif
    mov     [rgnSize], eax ; Saving current region size
    mov     eax, [edx + MEMORY_BASIC_INFORMATION.State] ; Getting region state
    .if     eax = MEM_RESERVE ; If it's MEM_RESERVE - BAM! Need to save dat
        mov     eax, [lpOpt]
        lea     eax, [eax + OPTION.ModuleBadPageList]
        .if dword [eax] = 0 ; The module's bad page list is empty
            pop     ecx ; Restoring current region address
            mov     [eax], ecx ; Saving it into buffer
            mov     ebx, [rgnSize]
            mov     [eax + 4], ebx ; Saving current region size next to it's address
            push    ecx ; Saving ecx back to stack
        .else ; The module's bad page list is NOT empty
            pop     ecx
            mov     ebx,ecx ; Saving current region address to ebx
            push    ecx
            mov     ecx,OPTION_BAD_PAGES_BUF_SZ
@@:
            .if     dword [eax] = 0 ; If we've got an empty space
                mov     [eax], ebx
                mov     ebx, [rgnSize]
                mov     [eax + 4], ebx
                jmp     @f ; Save addr and size and exit
            .else ; Keep searching
                sub     ecx, 8 ; 8 - 2 DWORDS, for addr and size
                .if     ecx = 0 ; Reached the limit?
                    jmp     .ERROR
                .endif
                add     eax, 8 ; Moving buffer 8 bytes forward
                jmp     @b ; Back to trying
            .endif
        .endif
    .endif
@@:
    pop     ecx
    add     ecx, [rgnSize] ; Increasing Module Base Address by current region size
    .if     ecx >= [endAddr] ; Check if we have reached the limit
        jmp     .EXIT
    .endif
    jmp     .LOOP
    ; exit
    xor     eax, eax ; Setting eax to zero - all fine
.ERROR:
    dec     eax
    jmp     .RET
.EXIT:
    .if     [pHandle] <> 0 ; Have we opened the handle?
        invoke  CloseHandle, [pHandle] ; Trying to close
        .if     eax = 0
            dec     eax
        .endif
    .endif
.RET:
    ret
endp

 

Кто хочет - читайте код и комментарии, я это в основном для себя оставил. Завтра, возможно, доделаю адекватное чтение, учитывая найденный мусор. :D

 

PS: Надо будет поиграться с rep lodsd.

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

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

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

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