Here is some addresses thanks to: -:TKK:-WaSsUp
Original posted from: https://www.mpgh.net/forum/142-call-d...-duty-4-a.html
From the previous tutorial, the following call is responsible for printing the name-tags:
Code:
Code:
0042F6C9 |. E8 A2FEFFFF CALL iw3mp.0042F570
Which contains a function that looks like:
Code:
Code:
0042F570 /$ 51 PUSH ECX
0042F571 |. 56 PUSH ESI
0042F572 |. E8 29EDFFFF CALL iw3mp.0042E2A0
0042F577 |. 83C4 04 ADD ESP,4
0042F57A |. 56 PUSH ESI
0042F57B |. E8 50EBFFFF CALL iw3mp.0042E0D0
0042F580 |. 83C4 04 ADD ESP,4
0042F583 |. 59 POP ECX
0042F584 \. C3 RETN
And, also, from before, we know that the second function call contains a nice loop which we can only assume is responsible for building the name-tags amoung all the entities. It only makes sense that best place to look is at the bottom of this loop, as the perfect place to finally draw the names is once everything else is taken care of. Scrolling down, the last call in this loop is:
Code:
Code:
0042E257 |> 51 |PUSH ECX
0042E258 |. D95C24 1C |FSTP DWORD PTR SS:[ESP+1C]
0042E25C |. D94424 1C |FLD DWORD PTR SS:[ESP+1C]
0042E260 |. 8B4C24 30 |MOV ECX,DWORD PTR SS:[ESP+30]
0042E264 |. D91C24 |FSTP DWORD PTR SS:[ESP]
0042E267 |. 56 |PUSH ESI
0042E268 |. 51 |PUSH ECX
0042E269 |. E8 02F5FFFF |CALL iw3mp.0042D770
But just for grins, it should make sense that this is called in the former of the two functions from before, so navigate back, and upon entering it and scrolling down, we see our guess is correct:
Code:
Code:
0042E3CC . 51 PUSH ECX
0042E3CD . 8B4C24 18 MOV ECX,DWORD PTR SS:[ESP+18]
0042E3D1 . D91C24 FSTP DWORD PTR SS:[ESP]
0042E3D4 . 56 PUSH ESI
0042E3D5 . 51 PUSH ECX
0042E3D6 . E8 95F3FFFF CALL iw3mp.0042D770
Enter the call to 42D770h so we can begin to piece this out - scrolling down, this gem should immediately catch your eye:
Code:
Code:
0042D845 |. 50 PUSH EAX ; /Arg3
0042D846 |. 68 607D6C00 PUSH iw3mp.006C7D60 ; |Arg2 = 006C7D60 ASCII \"Unable to get name for client num: %i
\"
0042D84B |. 6A 0E PUSH 0E ; |Arg1 = 0000000E
0042D84D |. E8 2EF40C00 CALL iw3mp.004FCC80 ; \iw3mp.004FCC80
Well, we are definitely in some function responsible for printing client names, look around the function and you should stumble upon a call that looks like:
Code:
Code:
0042D9FE |. 6A 00 PUSH 0
0042DA00 |. 6A 00 PUSH 0
0042DA02 |. 6A 00 PUSH 0
0042DA04 |. 6A 00 PUSH 0
0042DA06 |. D95C24 54 FSTP DWORD PTR SS:[ESP+54]
0042DA0A |. D94424 28 FLD DWORD PTR SS:[ESP+28]
0042DA0E |. 6A 00 PUSH 0
0042DA10 |. 6A 00 PUSH 0
0042DA12 |. 8BCF MOV ECX,EDI
0042DA14 |. 51 PUSH ECX ; colour
0042DA15 |. 6A 03 PUSH 3
0042DA17 |. 8D5424 68 LEA EDX,DWORD PTR SS:[ESP+68]
0042DA1B |. 52 PUSH EDX
0042DA1C |. 83EC 10 SUB ESP,10
0042DA1F |. D95424 0C FST DWORD PTR SS:[ESP+C]
0042DA23 |. 8D8424 8C000000 LEA EAX,DWORD PTR SS:[ESP+8C]
0042DA2A |. D95C24 08 FSTP DWORD PTR SS:[ESP+8] ; scale
0042DA2E |. D94424 54 FLD DWORD PTR SS:[ESP+54]
0042DA32 |. D95C24 04 FSTP DWORD PTR SS:[ESP+4] ; y
0042DA36 |. D94424 48 FLD DWORD PTR SS:[ESP+48]
0042DA3A |. D91C24 FSTP DWORD PTR SS:[ESP] ; x
0042DA3D |. 53 PUSH EBX ; font
0042DA3E |. 6A 20 PUSH 20
0042DA40 |. 50 PUSH EAX ; text
0042DA41 |. E8 7A2C0400 CALL iw3mp.004706C0 ; print names
From our work with drawing static text (DoxCoding Forums View topic - Drawing Text Using Engine Functions[CoD4][mAsm]), we know that Call of Duty 4's text functions seem to like to transfer x, y, and the scale in esp, esp + 4, and esp + 8, respectively. So just to ensure that we are correct, let us place a breakpoint on the "sub esp, 10h" at 42DA1Ch, and step through this. Run into view of a name-tag, and you should see that as Call of Duty 4 adjusts for distance, esp + 8 varies between 0.4 and 0.8, and that esp + 4 and esp hold values that certainly could be x, and y. The rest of the parametres pushed are documented, and can be found and understood by simply stepping through the series of pushes.
It bares mentioning here two facts - the first being that this is not an actual text-out function; enter the call, and you will see this is simply a wrapper for another text-out function:
Code:
Code:
004706C0 /$ 8B4424 40 MOV EAX,DWORD PTR SS:[ESP+40]
004706C4 |. D9EE FLDZ
004706C6 |. 8B4C24 3C MOV ECX,DWORD PTR SS:[ESP+3C]
004706CA |. 8B5424 38 MOV EDX,DWORD PTR SS:[ESP+38]
004706CE |. 50 PUSH EAX
004706CF |. 8B4424 38 MOV EAX,DWORD PTR SS:[ESP+38]
004706D3 |. 51 PUSH ECX
004706D4 |. 8B4C24 38 MOV ECX,DWORD PTR SS:[ESP+38]
004706D8 |. 52 PUSH EDX
004706D9 |. 8B5424 38 MOV EDX,DWORD PTR SS:[ESP+38]
004706DD |. 50 PUSH EAX
004706DE |. 8B4424 38 MOV EAX,DWORD PTR SS:[ESP+38]
004706E2 |. 51 PUSH ECX
004706E3 |. 8B4C24 38 MOV ECX,DWORD PTR SS:[ESP+38]
004706E7 |. 52 PUSH EDX
004706E8 |. 8B5424 38 MOV EDX,DWORD PTR SS:[ESP+38]
004706EC |. 50 PUSH EAX
004706ED |. 8B4424 28 MOV EAX,DWORD PTR SS:[ESP+28]
004706F1 |. 51 PUSH ECX
004706F2 |. 8B4C24 28 MOV ECX,DWORD PTR SS:[ESP+28]
004706F6 |. 52 PUSH EDX
004706F7 |. 8B5424 28 MOV EDX,DWORD PTR SS:[ESP+28]
004706FB |. 83EC 14 SUB ESP,14
004706FE |. D95C24 10 FSTP DWORD PTR SS:[ESP+10]
00470702 |. D94424 54 FLD DWORD PTR SS:[ESP+54]
00470706 |. D95C24 0C FSTP DWORD PTR SS:[ESP+C]
0047070A |. D94424 50 FLD DWORD PTR SS:[ESP+50]
0047070E |. D95C24 08 FSTP DWORD PTR SS:[ESP+8]
00470712 |. D94424 4C FLD DWORD PTR SS:[ESP+4C]
00470716 |. D95C24 04 FSTP DWORD PTR SS:[ESP+4]
0047071A |. D94424 48 FLD DWORD PTR SS:[ESP+48]
0047071E |. D91C24 FSTP DWORD PTR SS:[ESP]
00470721 |. 50 PUSH EAX
00470722 |. 51 PUSH ECX
00470723 |. 52 PUSH EDX
00470724 |. E8 07661800 CALL iw3mp.005F6D30
00470729 |. 83C4 44 ADD ESP,44
0047072C \. C3 RETN
The second is that Call of Duty 4 uses several text-out functions, and each display text differently. The text-out function I had found before, and used both in my tutorial, and the coffee series of releases, takes a maximum x and y of around 600. This is important when you realise that this function takes a maximum x and y of around 2000; bottom line, the two functions are not interchangeable.
But, with all that, we can now move onto coding this.
*Some of the base stuff I literally copied and pasted from my previous tutorials since the same ideas apply. This is my apology for being a lazy bum.*
So here I'm probably going to lose a lot of you, because we are going to be coding this in assembly. I'll give you the chance to hit the back button on your browser...
...for those of you still around, let's start.
* I personally use RadASM, and although I would strongly suggest it, you are free to write the code in whatever you want. I'm using MASM to assemble and link though, so it is likely that some of the code will not work on other ASM packages, although, it shouldn't be too hard to edit. *
First, let's get the shell set up (this is a .DLL for any that didn't guess):
Code:
Code:
.386
.model flat, stdcall
option casemap: none
.code
_main:
end _main
Now, before the code section, but under the option, we need to throw in some includes:
Code:
Code:
include \masm32\macros\macros.asm
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
ow we need some data to dump all the data we found during our struggles - create a .data section under your includes, but above the .code section, and put:
Code:
Code:
.data
staticY real4 100.0f
jmpback_draw_tags dd 42da40h
@ori_text_out dd 4706c0h
hold dd 0
Okay, now on to the actual .DLL, in main:
Code:
Code:
push ebp
mov ebp, esp
mov eax, dword ptr ds:[ ebp + 0ch ]
cmp eax,1
jnz @NOT_DLL_ATTACH
push eax
;our code will go here
pop eax
@NOT_DLL_ATTACH:
leave
retn 0ch
* I did give you the chance to leave. If you're going to code in assembly, you better be prepared to write in assembly, not this semi-H.L.A. shit mASM now supports. *
Okay, basic rundown of this code; since this is a .DLL, one of our main concerns is about keeping everything nice and balanced. First we push the base pointer on the stack, the move the stack pointer into the base pointer,which basically tells the running code that the current base of the program is the base of our .DLL, which Mr. Computer needs to know so he (there are no girls on the internet, therefore, computers are always guys) can figure out where to return upon calls, jumps, calculations, etc. The push saves the base pointer on the stack, so that when we leave our function and return, the base is set again to CoD4's base pointer. After that we move the data in the base pointer + Ch (12d), which holds the reason for our .DLL being called.
* ebp = base
ebp + 4 = __stdcall DllMain
ebp + 8 = hModule
ebp + 12 = ul_reason_for_call
ebp + 16 = lpReserved *
If the reason is DLL_PROCESS_ATTACH (or 1h), then we want to execute our code, otherwise, we want to leave our .DLL and return execution flow to CoD4's (making sure to balance the stack - three parametres = retn CH).
* eax needs to hold the reason for the call at the return of our .DLL, so we preserve it by push'ing it before our function calls, and pop'ing it right before we return. *
* I apologise in advance for using invoke, the code was getting a tad unruly. *
With our base set up, we know that we are going to need to modify some memory for our hook, so we should allocate some place for the old protection type:
Code:
Code:
invoke VirtualAlloc, 0, 4, 1000h, 40h
mov ebx, eax
First, let's remove the limitations on seeing enemy name-tags, as well as seeing name-tags through walls - both discussed in the tutorial at the beginning of this article:
Code:
Code:
invoke VirtualProtect, 42dea4h, 6, 40h, ebx
mov byte ptr ds:[ 42dea4h ], 0e9h
mov dword ptr ds:[ 42dea5h ], 0000020ah
mov byte ptr ds:[ 42dea9h ], 90h
invoke VirtualProtect, 42dea4h, 6, dword ptr ds:[ ebx ], ebx
invoke VirtualProtect, 42e1ach, 6, 40h, ebx
xor ecx,ecx
@@:
mov byte ptr ds:[ 42e1ach + ecx ], 90h
inc ecx
cmp ecx, 6
jl @b
invoke VirtualProtect, 42e1ach, 6, dword ptr ds:[ ebx ], ebx
Next, we are going to hook the text-out function right above the actual call - at 42DA3Ah to be exact:
Code:
Code:
invoke VirtualProtect, 42da3ah, 6, 40h, ebx ;unprotect the memory
mov byte ptr ds:[ 42da3ah ], 0e9h ;create a patch, e9h being a jump
lea ecx, @draw_ESP ;move the address of our function into ecx
sub ecx, 42da3fh ;subtract the caller + 5 from callee
mov dword ptr ds:[ 42da3bh ], ecx ;move the result into 42da3ah + 1
mov byte ptr ds:[ 42da3fh ], 90h ;nop out the final byte to balance out the six bytes
invoke VirtualProtect, 42da3ah, 6, dword ptr ds:[ ebx ], ebx ;reprotect the memory
Getting close, but before we can right our ESP function, we have to copy over the draw_text function over from CoD4; under the retn 0ch:
Code:
Code:
; edx = x
; ebx = y
; ecx = text
@draw_text:
push 0
push 0
push 0
push 0
push 0
push 0
push 0
push 3
push 0
fld1
sub esp, 10h
fst dword ptr ss:[ esp + 0ch ]
fstp dword ptr ss:[ esp + 8 ]
fld dword ptr ds:[ ebx ]
fstp dword ptr ss:[ esp + 4 ]
fld dword ptr ds:[ edx ]
fstp dword ptr ss:[ esp ]
push 0f8d6ech
push 20
push ecx
call dword ptr cs:[ @ori_text_out ]
add esp, 40h
retn
I realise I left out the colour, but this is primarily due to laziness; besides, it is not to hard to implement this yourself.
Time to move onto the draw_ESP function:
Code:
To make the math easy as possible, I tried to maintain the stack as much as possible from the jump to my own call - essentially, I ordered the reconstruction of the code I removed so that it was possible to only account for my modifications:
Code:
Code:
fstp dword ptr ss:[ esp ]
mov hold, eax
;code to go here
mov eax, hold
push ebx
push 20h
jmp jmpback_draw_tags
* The reason for the hold is that I was experiencing some problems with the initial name in eax (probably as a result of the call to my own text-out function) being over-written. *
Next to save edx, ebx, and ecx:
Code:
Code:
push edx
push ebx
push ecx
;and here
pop ecx
pop ebx
pop edx
All that is left now is a call to our function:
Code:
Code:
fld dword ptr ss:[ esp + 0ch ] ;+0ch to account for the three pushes
fstp dword ptr ds:[ edx ]
lea ebx, staticY
lea ecx, CTXT( \"X\" )
call @draw_text
The final code for easy C+P:
Code:
Code:
.386
.model flat, stdcall
option casemap: none
include \masm32\macros\macros.asm
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
staticY real4 100.0f
jmpback_draw_tags dd 42da40h
@ori_text_out dd 4706c0h
hold dd 0
.code
_main:
push ebp
mov ebp, esp
mov eax, dword ptr ds:[ ebp + 0ch ]
cmp eax,1
jnz @NOT_DLL_ATTACH
push eax
invoke VirtualAlloc, 0, 4, 1000h, 40h
mov ebx, eax
invoke VirtualProtect, 42da3ah, 6, 40h, ebx
mov byte ptr ds:[ 42da3ah ], 0e9h
lea ecx, @draw_ESP
sub ecx, 42da3fh
mov dword ptr ds:[ 42da3bh ], ecx
mov byte ptr ds:[ 42da3fh ], 90h
invoke VirtualProtect, 42da3ah, 6, dword ptr ds:[ ebx ], ebx
invoke VirtualProtect, 42dea4h, 6, 40h, ebx
mov byte ptr ds:[ 42dea4h ], 0e9h
mov dword ptr ds:[ 42dea5h ], 0000020ah
mov byte ptr ds:[ 42dea9h ], 90h
invoke VirtualProtect, 42dea4h, 6, dword ptr ds:[ ebx ], ebx
invoke VirtualProtect, 42e1ach, 6, 40h, ebx
xor ecx,ecx
@@:
mov byte ptr ds:[ 42e1ach + ecx ], 90h
inc ecx
cmp ecx, 6
jl @b
invoke VirtualProtect, 42e1ach, 6, dword ptr ds:[ ebx ], ebx
invoke VirtualFree, ebx, 4, 4000h
pop eax
@NOT_DLL_ATTACH:
leave
retn 0ch
; edx = x
; ebx = y
; ecx = text
@draw_text:
push 0
push 0
push 0
push 0
push 0
push 0
push 0
push 3
push 0
fld1 ;push 1.00 on the stack
sub esp, 10h
fst dword ptr ss:[ esp + 0ch ]
fstp dword ptr ss:[ esp + 8 ]
fld dword ptr ds:[ ebx ]
fstp dword ptr ss:[ esp + 4 ]
fld dword ptr ds:[ edx ]
fstp dword ptr ss:[ esp ]
push 0f8d6ech
push 20
push ecx
call dword ptr cs:[ @ori_text_out ]
add esp, 40h
retn
@draw_ESP:
fstp dword ptr ss:[ esp ]
mov hold, eax
push edx
push ebx
push ecx
fld dword ptr ss:[ esp + 0ch ]
fstp dword ptr ds:[ edx ]
lea ebx, staticY
lea ecx, CTXT( \"X\" )
call @draw_text
pop ecx
pop ebx
pop edx
mov eax, hold
push ebx
push 20h
jmp jmpback_draw_tags
end _main
Obviously from this code, this will only print above the X location of the player, however, this is quite easy to change if you have a basic understanding of the code we reversed. This is also not being realised to its ability - for example, you can grab the colour off the stack, and distinguish between friend and enemy. To get a true sense of use though, it might be useful to reverse the function, and figure out which calls are responsible for transforming the 3D coordinate the function starts with and implement them in a more traditional`WorldToScreen' function; in this way it is possible to write a less limited ESP. But I will leave that up to you, until next time: