Instructions are just bytes, find the instructions you need to call, convert them to bytes and write them to a place where they will be called by D3. I'm not very experienced and my methods may be easily detectable or not very optimal, but it works.
Here's basically how I did it in D3:
1. Get the address of the EndScene function.
2. Allocate some memory inside D3, let's call the address of this memory A.
3. Write the bytecode of these instructions to A
Code:
mov edi, edi
push ebp
mov ebp, esp
These are the first 5 bytes of the EndScene function, they are placed here because they will be replaced later and we don't want to screw up the normal execution of the program.
This stores all registers on the stack, don't really know if it's needed but it will make sure the game doesn't crash because of wrong register values
B is another location that we will write code to, I usually put it at something like A + 0x500 (I allocate 0x1000 bytes at A) because code doesn't take that much room.
Restores all register values
Jumps back to EndScene to let the program continue its execution, add 5 to the address because otherwise we will just jump back to A again (a jmp instruction is 5 bytes)
4. Write this code to BPlaceholder, this is were method calls will be placed later.
5. Write this code to EndScene
Everytime the program calls EndScene, the first instruction will make sure the code in A is executed. Right now, it does absolutely nothing out of the ordinary and the program can run just fine.
6. Now lets say we want to call a function: void C(bool b) with b=true and calling convention cdecl (wikipedia has some info on calling conventions).
Write this code to B
Code:
push 1
call C
add esp, 4
ret
The first line puts the argument to C on the stack, which is where the function expects it to be due to being a cdecl function. The next line just jumps to the function and executes it. The 3rd line 'cleans' the stack, this needs to be done because we pushed something on to it earlier (the value 1, 4 bytes). And lastly we return to A.
Right now, everytime to program calls EndScene, it will jump to A, then jump to B, execute C and go back to its normal execution flow. When you don't want it to call C anymore, just put a ret instruction on the first line of B. Obviously there are more things to keep in mind, getting the bytes of instructions for example, and more on calling conventions and stack management, but it's a start.