write a code that works as follows:
1. Create a data structure containing all of the assembly instructions that we want to execute.
2. Search the code section of ntdll.dll for each of the instructions above and store the addresses.
3. Add a custom exception handler in our program using RtlAddVectoredExceptionHandler.
4. Trigger a breakpoint using int 3.
5. The program has now entered our custom exception handler. Store the original thread context for later.
6. Set the EIP register to the first target instruction (within ntdll.dll) in our list.
7. If the current instruction is a 'call', set a hardware-breakpoint using the Dr0 debug register on the instruction directly after the call - we want to "step over" the call. Otherwise, set the single-step flag using EFlags |= 0x100 to break on the next instruction.
8. Update the values of any other registers that are necessary for the current instruction.
9. Continue execution using EXCEPTION_CONTINUE_EXECUTION. The next instruction will raise another exception, and we will continue from step #6 until all of the instructions have been run in sequence.
10. After all of the target instructions have been executed, restore the original thread context from step #5 to continue the original flow of the program.
1. Create a data structure containing all of the assembly instructions that we want to execute.
2. Search the code section of ntdll.dll for each of the instructions above and store the addresses.
3. Add a custom exception handler in our program using RtlAddVectoredExceptionHandler.
4. Trigger a breakpoint using int 3.
5. The program has now entered our custom exception handler. Store the original thread context for later.
6. Set the EIP register to the first target instruction (within ntdll.dll) in our list.
7. If the current instruction is a 'call', set a hardware-breakpoint using the Dr0 debug register on the instruction directly after the call - we want to "step over" the call. Otherwise, set the single-step flag using EFlags |= 0x100 to break on the next instruction.
8. Update the values of any other registers that are necessary for the current instruction.
9. Continue execution using EXCEPTION_CONTINUE_EXECUTION. The next instruction will raise another exception, and we will continue from step #6 until all of the instructions have been run in sequence.
10. After all of the target instructions have been executed, restore the original thread context from step #5 to continue the original flow of the program.