• Я зарабатываю 100 000 RUB / месяц на этом сайте!

    А знаешь как? Я всего-лишь публикую (создаю темы), а админ мне платит. Трачу деньги на мороженое, робуксы и сервера в Minecraft. А ещё на паль из Китая. 

    Хочешь так же? Пиши и узнавай условия: https://t.me/alex_redact
    Реклама: https://t.me/yougame_official

Гайд Nuitka Analysis. Разбираем Nuitka и крякаем софт!

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
25 Мар 2021
Сообщения
11
Реакции
2
Привет! Я еще давно занимался изучением Nuitka, и лишь совсем недавно задумался о написании статьи, что верно ведь тогда, год назад, у меня не было должного представления о нуитке и получилось бы, что я выложил чушь из своих догадок.

Что такое Nuitka?
" Nuitka is a Python compiler written in Python."
Nuitka - это "компилятор" для питона написанный на си, который не обходится без DLL питона и костылей. Думаю так будет объективней.:roflanEbalo: Обычно Нуитку используют вместо PyInstaller из-за его небезопасности, ведь в Nuitka нельзя обойтись без реверса и должной базы знаний. Не плюйтесь в экран, но в большинстве известных мне случаев, Нуитку используют для накрытия ратников, или тулок для маленьких доксеров.:roflanPominki:

Я думаю что есть люди заинтересованные в том чтобы крякнуть какую-нибудь прогу с нуиткой или хотя-бы научиться. Для них я и пишу статью.

В этой статье мы будем разбирать как тестовую программку, так и реальный уникализатор с лолза.

Начинаем! :CoolStoryBob:


Устанавливаем Nuitka: pip install nuitka

Пишем программку:
в предпросмотре тема питона тошнотная:FailFish:
nuitka_debug.py:
Expand Collapse Copy
### SOURCE CODE ###
import os

def exit(a,b,c):
    return os._exit(a+b+c)

def main():
    print("Hello, YOUGAME!")
    password = input("Enter a password: ")
    if password == "gaga": print("GREAT")
    else: print("WRONG")
    os.system("pause") #Ждем перед выходом из программы

    a,b,c = 1,2,3
    exit(a,b,c) # Смотрим на то, как будут вести себя простейшие операции

if __name__ ==  "__main__": main()

Компилируем тестовую программку:
В процессе компиляции использовалась Nuitka 2.7.4
nuitka --debug --trace-execution [nuitka_debug.py]
Нас интересуют только билд и pdb файл. Создаем папку и кидаем всё туда.

Запускаем IDA, открываем билд и загружаем pdb.

Нас интересуют две функции: loadConstantsBlob и modulecode__main__
Когда вы будете реверсить у вас не будет названий функций слева сбоку, поэтому я даю вам их отличительные признаки.

Чтобы найти loadConstantsBlob переходим к вызову FindResourceA, ведь эту функцию вызывает только loadConstantsBlob (насколько мне известно - всегда).
Чтобы найти modulecode__main__ переходим к PyMarshal_ReadObjectFromString, рядом находиться "call REG" который и ведет к мэйну ИЛИ
находим строку "__main__" и смотрим функцию, которая вызывает loadConstantsBlob с __main__ как аргумент

Что же делают эти функции?

loadConstantsBlob распаковывает ресурсы программы при том, что имеет структуру VM. Nuitka, подготавливаясь к компиляции, упаковывает ресурсы скрипта в файл __constants.bin. В билде ресурсы находятся в RCDATA. Ресурсы скрипта - это названия переменных, аргументы функций, типы данных, значения переменных, названия импортируемых модулей и прочее. В дальнейшем распакованные ресурсы будут упоминаться как константы

image.png

На пикче конечно ничего не ясно, но Nuitka таким образом пакует ресурсы. Взято при компиляции тестовой программки.
Зато видны строки, пароль, переменные, аргументы и значения аргументов.
Если интересно как это распаковывается, то рассмотрите
Пожалуйста, авторизуйтесь для просмотра ссылки.

modulecode__main__ занимается второй половиной - выполнением кода на основе констант.
Начинается main с использования констант для создания объектов кода. На примере программки это функции main, exit и <module>
code-objs.png

mod_consts это список констант

Стоит учесть что почти все переменные здесь - объекты структуры PyObject*, которая является базой для всех типов в CPython
Если вы хотите прочитать данные переменной то нужно сначала узнать к какому типу она относится как к объекту (ob_type)
В большинстве случаев можно прочитать PyObject* по оффсету 0x30

Далее создаётся исполняемый фрейм
CURRENT_FRAME = MAKE_COMPILED_FRAME(module_code_object, _module, ob_refcnt, 0);
В основном он будет использоваться для хранения внутри номера текущей строки, что поможет при отладке

После импорта пары модулей собираются функции на основе ранее полученных объектов кода

funcs.png

И вот, после полной инициализации программа приступает к выполнению кода.
_result = CALL_FUNCTION_NO_ARGS(tstate, main_func); запускает функцию main

Переходим к main (impl___main_____function__2_main)
Анализируем... понимаем, что фрейм создается для каждой функции, следовательно и следить за строками будет проще:seemsgood:
Уделяем внимание тому, что для вызова функций используется CALL_FUNCTION с различными вариациями, что может вызвать сложности 100% вызовет сложности при анализе ил восстановлении кода без pdb.

Перейдем к части с проверкой пароля
image.png

Все вроде интуитивно понятно, поэтому не буду заморачиваться
После длительной проверки данных ассертами наконец переходим к сравнению строк.:CoolCat:

image.png


Очень заметна функция COMPARE_EQ_CBOOL_UNICODE_UNICODE (return_value, operand1, operand2) которая и занимается сравнением == в питоне.

В моем случае я просто поменял jz на nop и крякми решен!

image.png


Отлично, почти и не напрягались, а уже ломанули.

Вторая часть


Во второй части мы попробуем ломануть уникализатор видео и фото - AVAMA

Кидаем аваму в DIE
die.png


Упаковщик: Nuitka[OneFile] говорит нам о том, что авама была скомпилирована с флагом --onefile
На гитхабе давно валяется
Пожалуйста, авторизуйтесь для просмотра ссылки.
, который нам поможет распаковать билд.

Кидаем экстрактор в одну папку с билдом и прописываем в командной строке nuitka-extractor [onefile_build].exe
Далее будем работать с экстрактом в папке extracted. В новых версиях при --onefile в эксракте будет не .exe а .dll поэтому вам придется написать лоадер для запуска.

Кидаем все файлы из оригинальной папки в _extracted ведь без этого софт не запустится.
files.png

Важный момент: При KeyboardInterrupt Нуитка стучит в консоль номер последней исполняемой строки т.е. пока программа ожидает ввод пользователя мы можем нажать ctrl+c и узнать где находится ввод.


Например:
line.png


Запускаем IDA и загружаем софт, сразу переходим к мэйну. Находим "__main__" и переходим на modulecode__main__. Так как кодовая база у тестового билда и крякми почти одинаковая, то можно расставить названия важных функций по местам.

Так как мы собираемся отталкиваться от номера строки, то нам нужно найти переменную содержащую номер, у меня их две - MAIN_FRAME и traceback_lineno

lines.png


Теперь находим нашу строку - 1248
1248.png


Я посмотрел, поставил бряки и нашел точку сравнения ключей - 1245 строка
1245-true-compare.png


PySequence_Conatins() выполняет команду "in" в питоне - поиск данных в списке.
В нашем случае это можно изобразить как:
compare_keys:
Expand Collapse Copy
if key not in keys:
    print("fuck")
    exit()

Патчим...
было:
Expand Collapse Copy
mov rdx,rbx
mov rcx,rax
call qword ptr ds:[<PySequence_Contains>]
cmp eax,FFFFFFFF
jne avama_unik.7FF6E8652CA3
mov rax,qword ptr ds:[rdi+60]
mov r15d,4DD
mov qword ptr ss:[rsp+68],rax
mov rax,qword ptr ds:[rdi+68]
mov qword ptr ss:[rsp+70],rax
mov rax,qword ptr ds:[rdi+70]
mov qword ptr ss:[rsp+60],rax
xor eax,eax
mov qword ptr ds:[rdi+60],rax
mov qword ptr ds:[rdi+68],rax
mov qword ptr ds:[rdi+70],rax
jmp avama_unik.7FF6E86549AD
test eax,eax                                       ###
sete cl                                            ###

стало:
Expand Collapse Copy
mov rdx,rbx
mov rcx,rax
call qword ptr ds:[<PySequence_Contains>]
cmp eax,FFFFFFFF
jne avama_unik.7FF6E8652CA3
mov rax,qword ptr ds:[rdi+60]
mov r15d,4DD
mov qword ptr ss:[rsp+68],rax
mov rax,qword ptr ds:[rdi+68]
mov qword ptr ss:[rsp+70],rax
mov rax,qword ptr ds:[rdi+70]
mov qword ptr ss:[rsp+60],rax
xor eax,eax
mov qword ptr ds:[rdi+60],rax
mov qword ptr ds:[rdi+68],rax
mov qword ptr ds:[rdi+70],rax
jmp avama_unik.7FF6E86549AD
mov cl,0
nop
nop
nop



result.png

Оууу дяяяя

Большое спасибо за прочтение, это была моя первая статья.
 
Рад видеть статью на такую тему, красавчик! Впервые встречаю такой материал.
 
респект за гайдик. Жду от тебя еще статьей + понятно пишешь (отдельное спасибо за выделение на фотках)
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
ахаххах помню писал свой лоадер на питоне, вначале использовал пайинсталер, а сам лоадер брал длл с гитхаба :roflanEbalo:
может кто нибудь помнит, GLoader. в ластовых версиях я вроде использовал нуитку, если хотите то можете попробовать реверснуть:roflanBuldiga:
 
Назад
Сверху Снизу