Nfs most wanted 2005 ошибка debug error

Такая проблема:

Погоня с полицией, 4/5 уровень, 35+ минут погони (чаще всего ошибка начинает вылетать, когда погоня набирает время более получаса) и тут выскакивает окно:

Microsoft Visual C++ Debug Library

Debug Error!
Program: D:GamesNeed For Speed Most Wantedspeed.exe
R6025
-pure virtual function call
(press Retry to debug application)

Эта гадость не даёт мне устроить нормальную заворушку с копами уже целую неделю. Помогите!

Игра — лицензия, патч 1.3.
ОС — Win7 SP1 Starter Edition x32


Go to needforspeed


Debug Error in NFS Most Wanted 2005

So, I´m about to race Razor, and the only thing left to do are some milestones missions. But when I am about 8 minutes into the pursuit, this happens:

Microsoft Visual C++ Debug Library

Debug Error Program …Electronic ArtsNeed For Speed Most Wantednfs.exe

R6025 — pure virtual function call

(press retry to debug the application)

Searching on the internet, apparently, some people has this problem too, but, no one has a solution.

Anyway, I really want to beat the game, so, I appreciate all the help.

Archived post. New comments cannot be posted and votes cannot be cast.

Реверс-инжиниринг, Assembler


Рекомендация: подборка платных и бесплатных курсов Python — https://katalog-kursov.ru/

Привет, Хабр! С вами снова спидраннинг коммьюнити NFS. И мы снова чиним старенькую игрушку — NFS Most Wanted. Я уже рассказывал о починке багов в своих предыдущих статьях, а сегодня хотел был пойти с вами немного глубже в дебри дизассемблирования. Заинтересовавшихся прошу под кат.

Предыстория

Когда-то давно,

когда EA издавала хорошие NFS

, вышла одна из известнейших гоночных игр — Most Wanted. Увы, написана она была не так хорошо, как продавалась, и периодически падала. Конечно, обычный человек на это обращает мало внимания — ну вылетела разок за прохождение, ничего страшного. А вот нам это создает огромные проблемы: сколько потенциальных рекордов было убито случайными падениями без внятных симптомов. Все закончилось тем, что KuruHS лично попросил меня разобраться в ситуации. Отказаться я не смог.

Что имеем

IDA — для дизассемблирования
Cheat Engine — для редактирования памяти и инструкций
Visual Studio — для отладки (Trace Points, оказались весьма удобной вещью)

У нас есть куча дампов. Приличная куча, гигабайт на 10. С них мы и начнем — проанализируем, на каких инструкциях падает игра. А падает она довольно рандомно, хотя некоторые закономерности прослеживаются. За время решения проблем мы нашли несколько потенциально опасных мест, которые иногда крашат игру. Например:

в функции вычисления хэша строки. Видимо, разработчики не ожидали получить null-pointer в этом месте, поэтому не добавили проверку на него. Из-за этого в редких случаях игра падала. Фикс довольно банальный — прыгнуть в первый пустой кусок экзешника, да сделать test edi, edi. Потом jz retun и jmp откуда прыгали изначально.

Другой похожий случай нашёлся в процедуре по адресу
0х0057D105 mov edx, [ecx] ; я так и не смог понять, что конкретно она делает

Разработчики снова не ожидали получить там null pointer, поэтому игра падала. Фикс абсолютно идентичен предыдущему.

Наиболее распространённая причина падения оказалась в функции AllocateMemory. Попытки ее дизассемблирования повергли в ужас всех, кто работал над проблемой падений игры. Внимания удостоен уже тот факт, что в игре как минимум 5 разных подсистем управления памятью. Во что я ввязался…

Ладно, нет времени ныть, надо реверсить. Несколько вечеров за разборами этого мусора принесли свои плоды: код, хоть все еще и не читаемый, стал более понятен. Судя по всему, эта подсистема работает по стандартной схеме: грабастаем некоторое количество памяти сразу, разбивая на блоки, храним их в двусвязном списке; по требованию выдаём свободные участки, а если таковых нету — пытаемся взять у системы еще. Ах, 2005-ый, когда операции с памятью были достаточно дорогими, чтобы ей разбрасываться как попало…

Некоторые места в этой функции вызывают у меня приступы головной боли, потому что мой мозг напрочь отказывается даже пытаться их обработать. Но одно мне ясно точно — где-то среди всех этих связных списков, состоящих из связных списков, кроется неправильный указатель, из-за которого все и падает. Единственное решение, пришедшее в голову — отключить проверку «use_best_fit» чтобы подсистема выдавала первый попавшийся свободный блок, а не искала тот, который она считает наиболее подходящим.

Конечно, проблемы целиком это не решило, но как минимум игра стала действительно стабильнее — за неделю тестирования в этом конкретном месте она упала всего несколько раз (при учете, что KuruHS проводит в игре по 10 часов в день), что я считаю довольно неплохим результатом.

Pure virtual function call.

Та самая ошибка, что проиллюстрирована в шапке. Люди, знакомые с С++ сразу поймут, в чем проблема. Однако без исходного кода все становится значительно сложнее. Ситуацию усложняет CRT, который, аки партизан, упорно не хочет генерировать дампы, если вылавливает этот тип ошибки.

Purecall означает, что код попытался вызвать «чисто виртуальную функцию» (виртуальную функцию класса, не имеющую реализации). Без сомнений, этого сделать у него не получается, поэтому единственное, на что он решается — сообщить об этом пользователю и завершиться с кодом 0. В итоге вроде бы все и хорошо с кодом, а на деле все плохо.

Спасибо Microsoft за замечательную функцию — _set_purecall_handler, которая позволяет заменить обработчик purecall’ов. Ищем в экзешнике упоминания/ссылки, находим саму функцию. Теперь осталось написать свой обработчик и не забыть установить его как хэндлер. Для этого нам нужно найти достаточно большой кусок неиспользуемого кода в самом экзешнике, который мы сможем перезаписать на наш код. Недолгий поиск показал, что это будет функция _CxxThrowException (ссылок на нее не было найдено). Беспощадно записываем все ее тело nop’ами и начинаем творить поверх нее:

Вот так будет выглядеть псевдокод новых процедур:

new_handler:
	xor	eax, eax		; return *(0);
	mov	eax, [eax]		; моментально валит игру
	ret
set_handler:
	push 	new_handler
	call	_set_purecall_handler	; _set_purecall_handler(new_handler);
	add	esp, 4			; cdecl, восстанавливаем стек
	ret	

Компилируем (в моем случае руками вбиваем в Cheat Engine) и вставляем в код:

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

Единственная проблема заключается в том, что ошибка эта довольно редкая, а часами бесцельно играть не хочется. Я все же решил немного потестировать сам, и был приятно удивлен — игра упала буквально через 10 минут геймплея, причем упала на только что написанном мною участке. Перемещаемся по стеку вызовов немного выше:

0043E005  call        dword ptr [edx+80h] 

Ничего не могу сказать, кроме как: «да, это вызов виртуальной функции». Первая же мысль — а что, если без него? Выпиливаем его nop’ами, тестируем — вроде живем. Игра работает как надо. Побочных эффектов нет. Собираем патч, отсылаем на тестирование. Через день прилетает дамп, где та же процедура падает несколькими байтами ниже. Выпиливаю и ее — игра начинает падать. Все ведет к тому, что нужно думать над более серьезным решением. Но в голову ничего не лезет, поэтому откладывается на неопределенный срок.

За ночь я успел все обдумать, и пришёл к выводу. Вы скажете, что С++ не умеет в рантайме определять тип объекта? А я скажу, что может. И очень просто — по адресу виртуальной таблицы в памяти. Изучив дампы, я пришел к выводу, что периодически в процедуру прилетает неправильный класс (vtbl @ 0x00890970), а значит мы можем отловить эту ситуацию:


	cmp	edx, 00890970h
	jnz	good_class
	xor	eax, eax
	jmp	return
good_class:
	call	dword ptr[edx+80h]
	jmp	continue

Но есть одна загвоздка: это занимает достаточно много места, и это необходимо встроить в процедуру. Найти достаточно места не получится, все что есть — пара пустых кусков в несколько байт перед функцией. Спасибо уже и на том, что их много и они близко. Поэтому пишем спагетти и прыгаем из одного места в другое чуть ли не после каждой инструкции:

Лирика

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

Патчим и запускаем. И получаем всё ту же проблему: этот крах настолько редкий, что за почти 4 часа тестирования этот кусок кода был запущен всего пару раз, и все разы на вход был получен правильный класс.

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

Быстрый осмотр показал, что игра может упасть, если один из аргументов не равен нулю. Сама процедура вызывается всего в двух местах, причем в одном из случаев вызывается с тем самым аргументом, выставленным в 0. Значит смотрим другую функцию.

убираем по максимуму «лишние» проверки и пытаемся насильно вызвать эту функцию. Запускаем тестирвоание и наконец-то получаем неправильный класс на вход. Ждём, пока отладчик студии допечатает весь текст, игра отвисает и… продолжает работать. Ура!


Скриншот мыльный, ибо запись со стрима

Заключение

Решение найдено — игра больше не падает, даже если на вход подали что-то не то. Это заметно на скриншоте выше — часть заграждения отсутствует, потому что игра попыталась поставить туда что-то не то. Что именно — загадка, покрытая мраком, но я уверен, что рано или поздно мы узнаем и это.

В целом, ситуация действительно заметно улучшилась — KuruHS смог полноценно провести в игре порядка 20 часов без единого падения, что раньше было бы просто невозможно.

Весь фикс я решил оформить в виде asi скрипта, по принципу Widescreen патчей от ThirteenAG. Почитать исходники и скачать скрипты можно на гитхабе.

Спасибо за внимание!

Widjaja

Joined
Jun 12, 2007
Messages
4,815
(0.82/day)
Location

Wangas, New Zealand

System Name Darth Obsidious
Processor Intel i5 2500K
Motherboard ASUS P8Z68-V/Gen3
Cooling Cooler Master Hyper 212+ in Push Pull
Memory 2X4GB Corsair Vengeance DDR3 1600
Video Card(s) ASUS R9 270x TOP
Storage 128GB Samsung 830 SSD, 1TB WD Black, 2TB WD Green
Display(s) LG IPS234V-PN
Case Corsair Obsidian 650D
Audio Device(s) Infrasonic Quartet
Power Supply Corsair HX650w
Software Windows 7 64bit and Windows XP Home
Benchmark Scores 2cm mark on bench with a razor blade.


  • #1

Just about to complete NFSMW and the it CTD and this popped up.

Never seen this before.

BTW I also in add/remove I have two Microsoft Visual C++ 2005 Redistibutable listed.
I’m only supposed to have one listed right?

And would this cause the issue with Need for Speed Most Wanted?

You are using an out of date browser. It may not display this or other websites correctly.
You should upgrade or use an alternative browser.

  • #1

The last couple of days, Need for Speed: Most Wanted has been crashing on me. When it does, I get the error «Pure Virtual Function Call.» It seems to have started only after I completed the full 100% in Career mode. What in the hell is causing this?? It is very frustrating. I have the latest Nvidia drivers and the 1.3 update for the game. Any help would be appreciated.

  • #2

I guess nobody else has this problem with the game??

Guest

Guest


  • #3

yeah, i’ve got this problem too
when i play in challenges — long pursuit (more than 7 minutes) when i’m in pursuit in about 8,5 minutes i see that:

Microsoft Visual C++ Debug Library
Debug Error
Program …Electronic ArtsNeed For Speed Most Wantednfs.exe

R6025
— pure virutal function call

(press retry to debug the application)

—————-
when i press retry the application exits

i have about 25-30% progress in game, career and challenge series

does anybody know why it happens

thanks for all help



Apr 6, 2011


4,839


0


23,460


  • #5

are they all full licensed versions of the game patched to the latest patch by EA?

Similar threads

  • Advertising
  • Cookies Policies
  • Privacy
  • Term & Conditions

  • Nfs most wanted 0xc000007b ошибка запуска
  • Nfs heat фикс ошибки the ea account
  • Nfs heat ошибка видеокарты
  • Nfs heat ошибка language
  • Nfs heat ошибка dr1002