[Bug 40623] DOOM (2016) crashes on launch due to Denuvo copy protection

wine-bugs at winehq.org wine-bugs at winehq.org
Sat May 21 23:32:16 CDT 2016


https://bugs.winehq.org/show_bug.cgi?id=40623

--- Comment #20 from roman at hargrave.info <roman at hargrave.info> ---
(In reply to roman at hargrave.info from comment #19)
> (In reply to Michael Müller from comment #18)
> > Created attachment 54540 [details]
> > Hack to prevent crash
> > 
> > I debugged the crash some days ago. The problem with the Denuvo protection
> > is that it contains a lot of intentional broken code that is called as soon
> > as the system notices that something is going wrong. It is therefore
> > possible that the crash happens on purpose and that the currently crashing
> > code path should not be reached at all if everything works fine.
> > Nevertheless, I analyzed the crash and attached a hack to to work around it.
> > 
> > The following assembler code leads to the crash. I added some annotations to
> > explain the behavior.
> > 
> > ----
> > 
> > mov rax,qword ptr gs:[60]      ; TEB->PEB
> > mov qword ptr ss:[rsp+C80],rbx
> > mov qword ptr ss:[rsp+C68],rbp
> > 
> > mov rcx,qword ptr ds:[rax+18]  ; PEB->LdrData 
> > 
> > mov qword ptr ss:[rsp+C60],rsi
> > mov qword ptr ss:[rsp+C58],rdi
> > mov r9,qword ptr ds:[rcx+20]   ; LdrData->InMemoryOrderModuleList (list
> > entry)
> > 
> > mov qword ptr ss:[rsp+C50],r12
> > xor edi,edi
> > sub r9,10 ; -> LDR_MODULE
> > 
> > mov qword ptr ss:[rsp+C48],r14
> > mov qword ptr ss:[rsp+C40],r15
> > cmp qword ptr ds:[r9+30],rdi ; LDR_MODULE->BaseAddress
> > ; The comparison is a bit misleading, as the BaseAddress
> > ; can never be zero. The idea is that if we reach the end
> > ; of the list, we get a pointer to LdrData->InMemoryOrderModuleList
> > ; again. The offset of LDR_MODULE->BaseAddress now matches the
> > ; PEB_LDR_DATA->EntryInProgress offset. This value should be zero
> > ; and wine never changes it anyway. The iteration therefore stops
> > ; when we reach this entry again.
> > 
> > ; Reached end of list
> > je doomx64.159E4907E ; --> Crash
> > 
> > ; BeginHash:
> > mov r10,qword ptr ds:[r9+60] ; LDR_MODULE->BaseDllName.Buffer
> > mov edx,edi ; edx will contain the hash, initialize with 0
> > mov r8,rdi
> > movzx ecx,word ptr ds:[r10] ; Check if BaseDllName is empty
> > test cx,cx
> > je doomx64.159E49075 ; --> NextEntry
> > 
> > ; ConverToLowerCase:
> > lea eax,dword ptr ds:[rcx-41] ; - 'A'
> > cmp ax,19
> > ja doomx64.159E49055 ; --> HashChar
> > add cx,20 ; tolower
> > 
> > ; HashChar:
> > inc r8
> > movzx eax,cx
> > movzx ecx,word ptr ds:[r10+r8*2] ; ecx -> next char
> > 
> > ; The actual "hashing" of the name
> > add edx,eax ; edx += char
> > add edx,edx ; edx *= 2
> > 
> > ; Reached end of string?
> > test cx,cx
> > jne doomx64.159E49048 ; no --> ConverToLowerCase
> > cmp edx,C8A08         ; check hash against 0xC8A08
> > je doomx64.159E49178  ; --> found
> > 
> > ; NextEntry:
> > mov r9,qword ptr ds:[r9] ; InLoadOrderModuleList.Flink
> > cmp qword ptr ds:[r9+30],rdi ; LDR_MODULE->BaseAddress
> > ; Reached end of list?
> > jne doomx64.159E49036 ; no --> BeginHash
> > 
> > ; Crash:
> > ; This function is meant to crash. It calls rsi (=rdi),
> > ; which is already set to zero when the whole code path is
> > ; reached. This is most probably just broken code to confuse us.
> > mov rsi,rdi
> > lea rax,qword ptr ss:[rsp+C98]
> > lea rdx,qword ptr ds:[159DDCE20]
> > mov r9d,20219
> > xor r8d,r8d
> > mov rcx,FFFFFFFF80000001
> > mov qword ptr ss:[rsp+20],rax
> > call rsi ; rsi = rdi = 0
> > test eax,eax
> > 
> > ----
> > 
> > The whole algorithm just searches for a specific dll using a hash. The
> > enumeration matches the following C code:
> > 
> > ----
> > 
> > TEB *teb = (void*) NtCurrentTeb();
> > PEB *peb = teb->Peb;
> > 
> > PEB_LDR_DATA *ldr_data = peb->LdrData;
> > LIST_ENTRY *entry = ldr_data->InMemoryOrderModuleList.Flink;
> > LDR_MODULE *mod = (void*) ((char*) entry - 0x10);
> > 
> > while (mod->BaseAddress)
> > {
> > 	/* hash mod->BaseDllName.Buffer */
> > 	mod = (void*)mod->InLoadOrderModuleList.Flink;
> > }
> > 
> > ----
> > 
> > The assembler code searches for the hash 0xC8A08 which corresponds to
> > advapi32.dll. The dll is loaded directly by doom through the PE import
> > section. So why does it crash? Well, if you look at the enumeration you will
> > see that it starts the search using the InMemoryOrderModuleList list but
> > continues using the InLoadOrderModuleList list. The search only succeeds if
> > the first entry in InMemoryOrderModuleList (i.e. the dll loaded at the
> > lowest address) is loaded before advapi32. Otherwise the code intentionally
> > crashes.
> > 
> > I currently don't have a windows machine for testing but it would be great
> > if someone could attach a debugger on windows and take a look at the loaded
> > libraries and their addresses. This way we can check if the whole code is
> > garbage to confuse people or if the necessary conditions are met on windows.
> > 
> > I wrote a hack to pin advapi32.dll as first entry of the
> > InMemoryOrderModuleList. This prevents the crash and Doom uses advapi32 to
> > read some registry keys. It also loads the dbdata license file. When this is
> > done nothing interesting happens any more. I guess that the license check
> > fails, but I haven't debugged it any further since I don't have enough time
> > at the moment. It would also make sense to check the theory on windows first
> > to prevent wasting time on some intentionally broken code.
> > 
> > If someone wants to debug this any further, the attached hack should help. I
> > wrote it against Staging (to use WINEDEBUG=+DOOMx64.exe:relay for generating
> > relay logs without debugging Steam), but it should also apply on plain wine.
> 
> Thanks for the input Michael. If it's truly the case that WINE is simply not
> similar enough to a Windows environment for Denuvo to be "OK", and not the
> case that WINE is not implemented behaviour required for the basic
> functioning of Denuvo in itself, that would mean that a simple set of
> patched could potentially make this work. Unfortunately I can't do the
> suggested testing, but I would like to note that debugging Denuvo (or
> running it in a VM) may lead to unexpected results.

I would also like to note that I contacted Denuvo asking for a sample
application with which to test WINE.

I was completely ignored.

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list