For whoever's interested, the
Mission Timer (for the Urgent ones) is stored on Double. Just search for "Value Between" (what you see on screen in seconds - 2) and (what you see on screen in seconds + 2) to make sure you don't miss out on the address.
What I mean by "in seconds".
Say timer says "04:09:xx" when you are about to complete the Urgent mission and you're at the Terminal (that's the counter is shown on-screen):
Then you do this: 4 minutes = 4 * 60 = 240 seconds; plus the seconds -> 240 + 9 = 249. So in CE search for a Double value between 247 and 251. Then let the timer count some more and repeat the logic. You will find your value
If you can't be arsed with it, here's the spot for the Time Elapsed value:
Code: Select all
ds.exe+2FA526E - C5FB1076 38 - vmovsd xmm6,[rsi+38] // RSI is the Timer pointer
ds.exe+2FA5273 - C5CB5AC6 - vcvtsd2ss xmm0,xmm6,xmm6
ds.exe+2FA5277 - C5F82F46 30 - vcomiss xmm0,xmm0,[rsi+30]
With the information above, now that we have a Timer pointer, we can do these:
Code: Select all
00000001420BD738 lea r8,qword ptr ds:[1438E0E38] "CountdownTimer_ExportedSetBlinkStartTime"
00000001420BD765 lea r8,qword ptr ds:[1438E0E00] "CountdownTimer_ExportedResetTimer"
00000001420BD792 lea r8,qword ptr ds:[1438E0D50] "CountdownTimer_ExportedSetVisible"
00000001420BD7BF lea r8,qword ptr ds:[1438E0CE0] "CountdownTimer_ExportedIsTimerRunning"
00000001420BD7EC lea r8,qword ptr ds:[1438E0C60] "CountdownTimer_ExportedGetTimeRemaining"
00000001420BD819 lea r8,qword ptr ds:[1438E0BA0] "CountdownTimer_ExportedGetTimeElapsed"
00000001420BD846 lea r8,qword ptr ds:[1438E0B58] "CountdownTimer_ExportedAdjustTimeElapsed"
00000001420BD873 lea r8,qword ptr ds:[1438E0AC0] "CountdownTimer_ExportedAdjustTimeRemaining"
00000001420BD8A0 lea r8,qword ptr ds:[1438E0A28] "CountdownTimer_ExportedPauseTimer"
00000001420BD8CD lea r8,qword ptr ds:[1438E09F0] "CountdownTimer_ExportedStartTimer"
..
000000014304A568 lea r8,qword ptr ds:[143A893A0] "DSCountTimerBase_ExportedGetHashCode"
000000014304A595 lea r8,qword ptr ds:[143A89248] "DSCountTimerBase_ExportedGetCurrentTimeSecondsF"
000000014304A5C2 lea r8,qword ptr ds:[143A89068] "DSCountTimerBase_ExportedSetCurrentTimeSecondsF"
000000014304A5EF lea r8,qword ptr ds:[143A88FD8] "DSCountTimerBase_ExportedPause"
000000014304A61C lea r8,qword ptr ds:[143A88F90] "DSCountTimerBase_ExportedReset"
000000014304A649 lea r8,qword ptr ds:[143A88EA8] "DSCountTimerBase_ExportedStart"
..
000000014304A676 lea r8,qword ptr ds:[143A88D90] "DSCountTimerBase_sExportedFindInstanceFromHashCode"
I've explained in my article
here how to use the above to get to the actual functions
I'll pick one: "00000001420BD765 lea r8,qword ptr ds:[1438E0E00] "CountdownTimer_ExportedResetTimer".
Now head to "00000001420CC930" in CE:
Code: Select all
ds.exe+20CC930 - 48 85 C9 - test rcx,rcx
ds.exe+20CC933 - 74 17 - je ds.exe+20CC94C
ds.exe+20CC935 - 48 83 79 40 00 - cmp qword ptr [rcx+40],00 { 0 }
ds.exe+20CC93A - 74 10 - je ds.exe+20CC94C
ds.exe+20CC93C - 48 8B 41 20 - mov rax,[rcx+20]
ds.exe+20CC940 - 48 85 C0 - test rax,rax
ds.exe+20CC943 - 74 07 - je ds.exe+20CC94C
ds.exe+20CC945 - C7 40 1C 00000000 - mov [rax+1C],00000000 { 0 }
ds.exe+20CC94C - C3 - ret
We know from earlier what our Timer pointer is; it's stored in RSI at the location I provided. Set a break there and "collect" it:
In my case RSI is 0x00000423C471E2E0. So.. this becomes our RCX for the function above, "CountdownTimer_ResetTimer" (at ds.exe+20CC930).
The first ASM line says "test rcx,rcx" followed by a JE. This checks if our pointer address is NULL. If 0, then the JE is taken to the RET instruction at ds.exe+20CC94C. Else, the rest of the code executes.
Now.. I went back and checked my RSI's memory space just to try and understand what the code in ResetTimer does. This is how the memory space looks like:
I've highlighted the Double value so you see where it is, at offset 0x38 in RSI. I'll now revert to "8 Byte hex" display so I can explain the rest:
Analyzing the function's code raised some concerns. While "test rcx,rcx + je" made sense, the next part didn't. I looked at "cmp qword ptr [rcx+40],00". This is usually an ASM instruction that checks if a POINTER is NULL. Looking at the pic above, you can see that 0x40 is not a pointer. It contains this value: 0000000100000006.
Then I fed RSI pointer to the GetName function I posted in my article and got this:
Nada. So, at this point you'd be saying to yourself "hmm, I don't think I can use this pointer with the ResetTimer function". Well.. wrong
I thought the same myself till I looked at the next lines of ASM:
Code: Select all
ds.exe+20CC93C - 48 8B 41 20 - mov rax,[rcx+20]
ds.exe+20CC940 - 48 85 C0 - test rax,rax
ds.exe+20CC943 - 74 07 - je ds.exe+20CC94C
ds.exe+20CC945 - C7 40 1C 00000000 - mov [rax+1C],00000000 { 0 }
Considering my timer value is at offset 0x38 and the code above expects to find a pointer at 0x20 from the start of the structure.. then something didn't fit. Also.. you see how the end of the code does "mov [rax+1C],00000000", which means it will 0 out the Timer value. Back to the logic.. a pointer is read here "mov rax,[rcx+20]". So I looked a bit down from the starting point of my structure, namely 0x10 and saw this:
I fed that to GetName and got this:
Then I also noticed an interesting FLOAT value down the line:
Converting that to Float display shows this:
If you divide 2700 (seconds) to 60 (seconds) you get..
45 minutes
And what was the Target Time in my first screenshot? That's right: 45:00:00.
Then I started poking at that 0x1C offset I saw in the function: "mov [rax+1C],00000000". Which is this value:
I set a breakpoint on it on access and found this code to access it:
Code: Select all
ds.exe+2FA54A3 - 0FB6 4A 18 - movzx ecx,byte ptr [rdx+18]
Fun fact is this piece of ASM is part of a function that's executing another function which adds Time Elapsed. So I thought "lemme make it 0". Nothing happened. Hmm.. "let me make it 2". And that, folks,
paused the timer:
There you have it, quick tutorial on how to tackle Timers. You basically can finish an Urgent Mission like this:
BR,
Sun