X-Shar, не хотите потестировать вашу локальную модель qwen3, на СИ коде средней сложности. Просто любопытно, справится или нет. Если конечно, у вас есть на это время. А в коде нужно найти и устранить логическую ошибку. Сам код компилируется и работает, но некорректно (криво). Если что, код здесь: https://github.com/XXXRef/DisASM
Давай попробуем, а что нужно сделать, запустить на сервере просто ? А отличие от обычной qwen3, зачем самим собирать ? Я не против потестить.) --- Сообщение объединено, 24 июл 2026 в 15:02 --- Ну и у меня если-что графика с 24 гигов, больше нет возможностей, это учитывать надо. Если модель не влезет, то не получится... --- Сообщение объединено, 24 июл 2026 в 15:04 --- А-ты хочешь постестить код: https://github.com/XXXRef/DisASM Я думал ты хочешь свою модель потестить, сорян... Криво прочитал. Да, давай потестим...
GRAFik для сравнения спросил у трех нейронок. Вопрос у всех один: ### USER Во вложении код, расскажи что он делает, в нём логическая ошибка, как можно поправить ? Спросил у zer0kernel (По сути это почти дефолтный Qwen3 Coder, Kimi 2.6 и у меня еще ChatGPT 5.5 с высоким уровнем рассуждения). Вот результаты. Все нейронки указали на разные ошибки.) Лучше всего проанализировал GPT как по мне... Но там модель с рассуждением. Спойлер: Zer0Kernel ### USER Во вложении код, расскажи что он делает, в нём логическая ошибка, как можно поправить ? ### ASSISTANT Код, представленный в приложении, написан на языке C и, судя по структуре, предназначен для анализа или декодирования инструкций x86-ассемблера. Он включает в себя логику обработки параметров операндов, выделения памяти и взаимодействия с файлами. Ниже приведено пошаговое описание функциональности и указание на возможную логическую ошибку. --- ### Описание функциональности #### 1. **Меняет местами параметры** ```c if(cp->par[1]!='i'){ buf=cp->par[1]; cp->par[1]=cp->par[0]; cp->par[0]=buf; } ``` Этот фрагмент меняет местами два параметра (`cp->par[0]` и `cp->par[1]`), если второй параметр не равен `'i'`. Это может быть частью логики обработки операндов, где порядок параметров важен. #### 2. **Выделение памяти для массива параметров** ```c cp->parameters=(char**)malloc(sizeof(char*)*(cp->par_count)); ext_len=0; while(ext_len<cp->par_count){ cp->parameters[ext_len]=(char*)malloc(40); ext_len++; } ``` Здесь выделяется память для массива указателей на строки (`cp->parameters`) и для каждой строки выделяется 40 байт. Это может быть связано с хранением строковых представлений операндов. #### 3. **Обработка операндов** ```c switch(cp->par[counter]){ case 'r': // регистр ... case '1': // константа 1 cp->parameters[counter][0]='1'; cp->parameters[counter][1]=0; break; case 'a': // al, ax, eax ... ``` В этом фрагменте происходит обработка различных типов операндов, таких как регистры (`'r'`) и специальные значения (`'1'`, `'a'`). Например, если встречается `'a'`, то в зависимости от флага `w` записывается `al`, `ax` или `eax`. #### 4. **Функция `CharEnter()`** ```c char*CharEnter(){ char*res=NULL; int i=0; do{ i++; res=(char*)realloc(res,i); res[i-1]=(char)getchar(); }while(res[i-1]!='\n'); res[i-1]=0; return res; } ``` Функция читает строку с клавиатуры до символа новой строки (`'\n'`) и возвращает её. Она используется для ввода данных пользователем. #### 5. **Поиск секции кода** ```c IMAGE_SECTION_HEADER findCodeSection(FILE* hFile) { // TODO: реализация поиска секции кода } ``` Функция предназначена для поиска секции кода в PE-файле по значению `AddressOfEntryPoint`, но пока не реализована полностью. --- ### Возможная логическая ошибка #### Вопрос: Какая логическая ошибка может быть в коде? Одна из потенциальных проблем — **неправильное управление памятью**, особенно в части выделения и освобождения памяти. В следующем фрагменте: ```c free(cp->parameters[0]); cp->parameters[0]=temp_char1; break; ``` Если `temp_char1` — это указатель на строку, выделенную через `malloc`, и `cp->parameters[0]` ранее был выделен через `malloc`, то может возникнуть **двойное освобождение памяти**, если `temp_char1` указывает на ту же область, что и `cp->parameters[0]`. Также, если `temp_char1` — это строка, выделенная через `malloc`, но не инициализирована корректно, это может привести к **непредсказуемому поведению**. --- ### Как можно исправить 1. **Проверка на дублирование указателей**: Перед вызовом `free(cp->parameters[0])` необходимо убедиться, что `cp->parameters[0]` не совпадает с `temp_char1`. 2. **Инициализация `temp_char1`**: Убедиться, что `temp_char1` корректно инициализирована и не указывает на освобожденную память. 3. **Использование `strdup` вместо `malloc` + `strcpy`**: Если `temp_char1` — это строка, лучше использовать `strdup`, чтобы избежать ошибок при копировании. Пример исправления: ```c free(cp->parameters[0]); cp->parameters[0] = strdup(temp_char1); // безопасное копирование строки ``` --- ### Заключение Код представляет собой часть программы, которая анализирует x86-инструкции и обрабатывает их операнды. В нем есть потенциальная ошибка, связанная с неправильным управлением памятью, особенно при освобождении и переназначении указателей. Для исправления следует добавить проверки на дублирование указателей и корректную работу с динамически выделенной памятью. Если нужно, могу помочь с более подробным анализом конкретных участков кода. Спойлер: kimi ### USER Во вложении код, расскажи что он делает, в нём логическая ошибка, как можно поправить ? ### ASSISTANT Проанализировал код из `main.c` [1]. Это фрагмент **дизассемблера x86-инструкций**, который парсит опкоды и операнды из внешнего файла опкодов (`hFileOpcodes`) и формирует текстовое представление команды. ## Что делает код ### Основные этапы работы: 1. **Чтение мнемоники команды** — посимвольное чтение названия инструкции (mov, add, jmp и т.д.) до пробела 2. **Чтение спецификации операндов** — парсинг шаблона операндов из файла опкодов (например, `"rm r"` — register/memory и register) 3. **Обмен операндов при `d=0`** — если бит направления `d` сброшен и первый операнд не immediate, меняет местами операнды (это корректное поведение x86: `d=0` означает, что приёмник — src, источник — dst) 4. **Выделение памяти под параметры** — `malloc(40)` на каждый операнд 5. **Парсинг операндов по типам** — обработка регистров (`'r'`), control registers (`'c'` — cr0-cr7), и других типов ## Логическая ошибка: некорректный обмен операндов ### Проблема в этом блоке: ```c if((cp->sf.d==0) && (cp->par_count>=2)){ if(cp->par[1]!='i'){ // ← ПРОВЕРЯЕТ ТОЛЬКО par[1] buf=cp->par[1]; cp->par[1]=cp->par[0]; cp->par[0]=buf; } } ``` ### Почему это ошибка: | Сценарий | Поведение | Проблема | |---------|-----------|----------| | `par[0] == 'i'` (immediate первый) | Обмен **не** происходит | ✓ Корректно | | `par[1] == 'i'` (immediate второй) | Обмен **не** происходит | ✗ **ОШИБКА** — должен происходить, иначе `d=0` сломает семантику | | Оба не `'i'` | Обмен происходит | ✓ Корректно | **Суть**: В x86 при `d=0` направление операндов инвертировано. Если **любой** из операндов — immediate, обмен опасен или некорректен. Но текущая логика защищает только от `par[1]=='i'`, пропуская случай `par[0]=='i'`. При `par[0]=='i'` и `par[1]!='i'` код **всё равно обменивает**, получая immediate в позиции приёмника — что генерирует невалидную инструкцию (immediate нельзя записать в, например, `mov 5, eax` вместо `mov eax, 5`). ### Исправление: ```c if((cp->sf.d==0) && (cp->par_count>=2)){ // Не обменивать, если ЛЮБОЙ операнд — immediate if(cp->par[0]!='i' && cp->par[1]!='i'){ buf=cp->par[1]; cp->par[1]=cp->par[0]; cp->par[0]=buf; } } ``` Или, если семантика требует сохранить immediate на месте источника: ```c if((cp->sf.d==0) && (cp->par_count>=2)){ // Обмен только если immediate строго на второй позиции (корректно для d=0) if(cp->par[0]!='i'){ // первый не immediate — можно менять buf=cp->par[1]; cp->par[1]=cp->par[0]; cp->par[0]=buf; } // иначе: par[0]=='i' — уже корректно для d=0 (src=imm, dst=par[1]) } ``` ## Дополнительные замечания | Проблема | Место | Риск | |---------|-------|------| | Утечка памяти | `cp->par`, `cp->com_text`, `cp->parameters` — нет `free()` | При длительной работе | | Жёстко заданный размер | `malloc(40)` на операнд | Переполнение при длинных именах | | Нет проверки `realloc`/`malloc` на NULL | Везде | SEGFAULT при нехватке памяти | | `fread(&buf,1,1,...)` — `buf` как `char`, но используется как `int` в сравнениях | Весь код | Потенциальные проблемы с signed/unsigned | Код явно находится в стадии разработки (много `//printf("DEBUG")`), так что некоторые проблемы — ожидаемый технический долг. Спойлер: ChatGPT Во вложении код, расскажи что он делает, в нём логическая ошибка, как можно поправить ? ЧТО ДЕЛАЕТ ПРОГРАММА Это простой дизассемблер для Windows PE-файлов, рассчитанный преимущественно на 32-битные инструкции x86. Программа работает следующим образом: 1. Запрашивает путь к EXE-файлу или другому PE-файлу. 2. Читает из DOS-заголовка поле e_lfanew, которое находится по смещению 0x3C. 3. Переходит к PE-заголовку и получает: * количество секций; * размер Optional Header; * RVA точки входа AddressOfEntryPoint. 4. Просматривает таблицу секций и находит секцию, внутри которой расположена точка входа программы. 5. Переходит к полю PointerToRawData найденной секции, то есть к физическому смещению секции в файле. 6. Вызывает функцию GetCommand(), которая: * читает до 15 байт, поскольку 15 байт — максимальная длина инструкции x86; * разбирает префиксы LOCK, REP, REPNZ, 0x66, 0x67 и сегментные префиксы; * ищет инструкцию во внешней таблице res/opcodes.ttt; * разбирает opcode, ModR/M, SIB, регистры, смещения и непосредственные значения; * формирует текст инструкции и список операндов; * перемещает файловый указатель на фактическую длину разобранной инструкции. 7. Функция main() выводит полученные инструкции на экран. Например, результат работы может выглядеть так: push ebp mov ebp, esp sub esp, 20 При этом архив неполный. Для сборки отсутствуют следующие файлы: command_format.h res/opcodes.ttt Поэтому собрать и полностью проверить программу в текущем виде невозможно. ГЛАВНАЯ ЛОГИЧЕСКАЯ ОШИБКА Основная логическая ошибка находится в следующем фрагменте: int i = 1; while (...) { ... ``` while (i < command.par_count) { printf(", %s", command.parameters); i++; } ``` } Переменная i создаётся один раз перед основным циклом и не сбрасывается перед выводом каждой следующей инструкции. Предположим, первая инструкция имеет два операнда: mov eax, ebx При её выводе переменная i увеличится с 1 до 2. Следующая инструкция также имеет два операнда: add ecx, edx Но условие: i < command.par_count превратится в: 2 < 2 Это условие ложно. Поэтому программа выведет только: add ecx Второй операнд edx выведен не будет. КАК ИСПРАВИТЬ ОСНОВНУЮ ОШИБКУ Минимальное исправление — сбрасывать i перед выводом дополнительных операндов: while ((GetCommand(&command, hFile) == RET_SUCCESS) && (ftell(hFile) < (sSection.PointerToRawData + sSection.SizeOfRawData))) { ``` printf("%s", command.com_text); if (command.par_count != 0) { printf(" %s", command.parameters[0]); } i = 1; while (i < command.par_count) { printf(", %s", command.parameters); i++; } printf("\n"); ``` } Однако лучше вообще заменить внутренний цикл while на for: for (i = 1; i < command.par_count; i++) { printf(", %s", command.parameters); } В таком случае переменная i автоматически будет начинаться с 1 при обработке каждой инструкции. ОШИБКА В УСЛОВИИ ОСНОВНОГО ЦИКЛА В исходном коде используется примерно такое условие: while ((GetCommand(&command, hFile) == RET_SUCCESS) && (ftell(hFile) < sectionEnd)) Проблема заключается в порядке вычисления условий. Сначала вызывается GetCommand(), которая пытается прочитать очередную инструкцию, и только после этого проверяется, не вышел ли файловый указатель за пределы секции. В результате функция может прочитать данные за пределами текущей PE-секции. Безопаснее сначала проверять положение файлового указателя: long sectionEnd = (long)sSection.PointerToRawData + (long)sSection.SizeOfRawData; while (ftell(hFile) >= 0 && ftell(hFile) < sectionEnd) { int result = GetCommand(&command, hFile); ``` if (result != RET_SUCCESS) { break; } if (ftell(hFile) > sectionEnd) { break; } printf("%s", command.com_text); if (command.par_count > 0) { printf(" %s", command.parameters[0]); } for (i = 1; i < command.par_count; i++) { printf(", %s", command.parameters); } printf("\n"); ``` } Но это исправление только предотвращает вывод инструкции, которая пересекла границу секции. Для полного исправления функция GetCommand() должна получать количество оставшихся байт секции. Например: remaining = sectionEnd - ftell(hFile); После этого количество читаемых байт должно вычисляться так: size_t readSize = remaining < MAX_COMMAND_LEN ? (size_t)remaining : MAX_COMMAND_LEN; com_len1 = fread(mp, 1, readSize, hFile); ВОЗМОЖНАЯ ОШИБКА С ТОЧКОЙ ВХОДА Программа находит секцию, содержащую AddressOfEntryPoint, но затем начинает дизассемблирование с самого начала этой секции: fseek(hFile, sSection.PointerToRawData, SEEK_SET); Начало секции и точка входа программы — это не обязательно одно и то же место. Например: VirtualAddress секции: 0x1000 AddressOfEntryPoint: 0x1370 PointerToRawData: 0x0400 Физическое смещение точки входа в файле вычисляется так: entryFileOffset = sSection.PointerToRawData + (AddressOfEntryPoint - sSection.VirtualAddress); Для приведённого примера: 0x0400 + (0x1370 - 0x1000) = 0x0770 Следовательно, если требуется начинать дизассемблирование именно с точки входа программы, нужно перейти по смещению 0x0770, а не по смещению 0x0400. Исправленный переход может выглядеть так: long entryFileOffset = (long)sSection.PointerToRawData + ((long)AddressOfEntryPoint - (long)sSection.VirtualAddress); fseek(hFile, entryFileOffset, SEEK_SET); Если же задача программы заключается в дизассемблировании всей секции целиком, переход на PointerToRawData является нормальным. УТЕЧКА ПАМЯТИ MP В функции GetCommand() выделяется память: mp = (unsigned char *)malloc(MAX_COMMAND_LEN); Но при нормальном завершении функции она не освобождается: fclose(hFileOpcodes); return RET_SUCCESS; Необходимо добавить вызов free(): free(mp); fclose(hFileOpcodes); return RET_SUCCESS; Память также нужно освобождать во всех ветках функции, которые завершаются через return. ЗАПИСЬ ЗА ПРЕДЕЛЫ ВЫДЕЛЕННОЙ ПАМЯТИ В коде присутствует следующий фрагмент: temp_char1 = (char *)malloc(ext_len + 7); temp_char1[ext_len + 7] = 0; Если выделено ext_len + 7 байт, допустимые индексы находятся в диапазоне: 0 ... ext_len + 6 Индекс ext_len + 7 находится уже за пределами выделенного блока памяти. Правильный вариант: temp_char1 = malloc(ext_len + 7); temp_char1[ext_len + 6] = '\0'; Аналогичная ошибка присутствует в другом месте: temp_char1 = (char *)malloc(ext_len + 5); temp_char1[ext_len + 5] = 0; Правильный индекс: temp_char1[ext_len + 4] = '\0'; Однако если сразу после выделения памяти используется strcpy(), предварительно устанавливать завершающий нулевой символ обычно не требуется. Функция strcpy() сама копирует завершающий символ строки. НЕДОСТИЖИМЫЙ КОД В программе есть конструкция примерно следующего вида: case 7: return RET_ERROR; fclose(hFileOpcodes); free(mp); break; После выполнения return функция немедленно завершается. Следовательно, строки: fclose(hFileOpcodes); free(mp); break; никогда не выполняются. Правильный порядок: case 7: fclose(hFileOpcodes); free(mp); return RET_ERROR; НЕТ ПРОВЕРКИ ФОРМАТА PE-ФАЙЛА Программа практически не проверяет корректность загружаемого файла. Желательно проверить: * сигнатуру MZ в DOS-заголовке; * сигнатуру PE\0\0; * значение Machine; * поле Magic в Optional Header; * корректность числа секций; * корректность смещений; * соответствие размеров секций размеру файла; * успешность каждого вызова fread() и fseek(). Без таких проверок повреждённый или специально сформированный файл может привести к чтению неверных областей, аварийному завершению программы или повреждению памяти. ПРИМЕР ИСПРАВЛЕННОЙ ЛОГИКИ MAIN Основную часть main() можно переписать примерно так: long sectionStart; long sectionEnd; long entryFileOffset; int result; int i; sectionStart = (long)sSection.PointerToRawData; sectionEnd = sectionStart + (long)sSection.SizeOfRawData; entryFileOffset = sectionStart + ((long)AddressOfEntryPoint - (long)sSection.VirtualAddress); if (entryFileOffset < sectionStart || entryFileOffset >= sectionEnd) { printf("Ошибка: точка входа находится за пределами найденной секции.\n"); fclose(hFile); return RET_ERROR; } if (fseek(hFile, entryFileOffset, SEEK_SET) != 0) { printf("Ошибка перехода к точке входа.\n"); fclose(hFile); return RET_ERROR; } while (1) { long position = ftell(hFile); ``` if (position < 0 || position >= sectionEnd) { break; } result = GetCommand(&command, hFile); if (result != RET_SUCCESS) { break; } if (ftell(hFile) > sectionEnd) { printf("Ошибка: инструкция выходит за пределы секции.\n"); break; } printf("%s", command.com_text); if (command.par_count > 0) { printf(" %s", command.parameters[0]); } for (i = 1; i < command.par_count; i++) { printf(", %s", command.parameters); } printf("\n"); ``` } fclose(hFile); ИТОГ Главная логическая ошибка заключается в том, что переменная i, используемая для вывода операндов, не сбрасывается перед обработкой каждой следующей инструкции. Из-за этого после первой инструкции с несколькими операндами последующие инструкции могут выводиться не полностью. Основное исправление: for (i = 1; i < command.par_count; i++) { printf(", %s", command.parameters); } Кроме этого, в программе обнаружены и другие проблемы: * GetCommand() вызывается до проверки границы секции; * дизассемблирование начинается с начала секции, а не обязательно с точки входа; * присутствуют утечки памяти; * имеются записи за пределами выделенной памяти; * есть недостижимый код после return; * отсутствует полноценная проверка структуры PE-файла; * архив не содержит command_format.h и res/opcodes.ttt, поэтому полностью собрать программу в текущем виде нельзя. --- Сообщение объединено, 24 июл 2026 в 15:46 --- Main_fixed от ChatGPT, во вложении...
Частично справилась, но хуже всех.) Всё три модели указали на разные ошибки и я с этими ошибками согласен: 1)Zer0Kernel: Я согласен что лучше переписать код так: Код (Text): if (cp->parameters[0] != temp_char1) { free(cp->parameters[0]); cp->parameters[0] = temp_char1; } break; Также менее информативно проанализировала код, что он делает... Но не уверен что итоговая проблема в этом, но лучше попрвить, я не запускал код, незнаю что там за ошибка в итоге. 2)Kimi: Перестановка: Код (C): if ((cp->sf.d == 0) && (cp->par_count >= 2)) { if (cp->par[1] != 'i') { buf = cp->par[1]; cp->par[1] = cp->par[0]; cp->par[0] = buf; } } **Суть**: В x86 при `d=0` направление операндов инвертировано. Если **любой** из операндов — immediate, обмен опасен или некорректен. Но текущая логика защищает только от `par[1]=='i'`, пропуская случай `par[0]=='i'`. При `par[0]=='i'` и `par[1]!='i'` код **всё равно обменивает**, получая immediate в позиции приёмника — что генерирует невалидную инструкцию (immediate нельзя записать в, например, `mov 5, eax` вместо `mov eax, 5`). Вроде тоже соглашусь.) 3)GPT: Главная логическая ошибка заключается в том, что переменная i, используемая для вывода операндов, не сбрасывается перед обработкой каждой следующей инструкции. Тоже согласен... Код (C): int i = 1; while (...) { ... while (i < command.par_count) { printf(", %s", command.parameters[i]); i++; } } Короче тут комплекс проблем похоже...
X-Shar, рад тебя видеть живым и бодрым, заходи но новую дамагу, если будет желание встретить других знакомых старичков.
Привет! Рад видеть! Я уже там, но тор не очень удобен конечно... А Инде всё, не заходит больше никуда ?
Это нормальное его состояние...) Просто интересно чем сейчас занимается, всё визоры пилит, или как он там называл свои творения не помню уже...
Он увлекся биологией и креационизмом, заимствует теории своего рода опальных ученых, типа Шапиро, придумывает новые термины на их базе, постит сюда огромные пдфники, сгенерированные нейронками, которые никто не читает. В общем, некоторый новый виток эволюции Индия.