> For the complete documentation index, see [llms.txt](https://steve-s.gitbook.io/0xtriboulet/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://steve-s.gitbook.io/0xtriboulet/archive/notice/ttps/ttps-jmpnocall.md).

# TTPs: JmpNoCall

A proof of concept demonstration of custom payload and implant implementations that results in clean call stack execution of malicious code

### Part One: Introduction

Over the past couple of weeks, there has been some interesting work regarding call stack tracing evasion by @NinjaParanoid. His technique used some cool APIs and callback functions to achieve clean call stacks and reduce detectability.

The problem is that most implant implementations execute code out of a RX sections of memory. This functionality can be detected by EDR when API calls or syscalls return to RX sections of memory.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FjFQFbLGv4ULE8wCFkpWM%2Fimage.png?alt=media&amp;token=69bdba14-2bd6-4385-b661-e3d364c35e9d" alt=""><figcaption><p><a href="https://0xdarkvortex.dev/proxying-dll-loads-for-hiding-etwti-stack-tracing/">https://0xdarkvortex.dev/proxying-dll-loads-for-hiding-etwti-stack-tracing/</a></p></figcaption></figure>

That got me interested in the topic, but I wanted a more customized solution. A significantly advanced threat actor is likely using custom payloads to execute tailored actions specific to their campaign, so we're going to utilize a different technique to achieve clean call stacks.

The technique I developed uses assembly ramps to jmp to our functions, without using the "call" instruction. We're going to do this by using a combination of inline assembly, an assembly onRamp, and a custom payload.

Note: for demonstration purposes, the allocated section of memory we'll use in this writeup uses RWX permissions, but the final code available on my GitHub implements this technique with RX permissions

### Part Two: Getting Started

So to start, we have to develop a way to get the address we want to return to at run time. We develop the following code and run in in x64dbg to validate that we are capturing the correct address:

```cpp
#include <stdio.h>
#include <Windows.h>

// x86_64-w64-mingw32-g++.exe implant.cpp -o implant.exe -masm=intel
// implant_backup_1.cpp
/* Reference
asm ( "assembly code"
           : output operands                  optional
           : input operands                   optional
           : list of clobbered registers      optional
);
*/

extern "C" void onRamp(PVOID exec_mem, PVOID ret_addr);

int main(void){
	printf("Implant running...\n");
	
	void * ret_addr = NULL;
	asm("lea %0, [rip+ReturnHere];"
	: "=r" (ret_addr) 								// ret_addr <- rip+ReturnHere
	:												// no inputs
	: 												// no predefined clobbers
	);
	
	printf("Return address: %p\n",ret_addr);		// get return address
	
	asm("ReturnHere:;");							//ret_addr
	printf("Exiting implant...\n");
}

```

And x64dbg shows us that our technique works!

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FHIeyVToeU8MqNvdEqLs3%2Fimage.png?alt=media&amp;token=c63ed56a-eb0f-470e-95e1-1dd74abee1b9" alt=""><figcaption></figcaption></figure>

### Part Three: Executing a payload

We can build a rudimentary assembly onRamp to call our payload

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FgEe3K5qvNqwpcV6nzCs5%2Fimage.png?alt=media&amp;token=73df2d99-23d6-4fc3-bc33-a03fa00cec9b" alt=""><figcaption></figcaption></figure>

We can implement an implant that uses this ramp and a standard msfvenom calc payload like so:

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FmkJzocxxCLsvZioST6Gd%2Fimage.png?alt=media&amp;token=35cb8577-8a56-4ffd-ba49-c1da05738962" alt=""><figcaption><p>implant_backup_2.cpp</p></figcaption></figure>

But even though we can achieve payload execution, we are not able to recover cleanly

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FYDz5KPJPM4pz5tTIzgWD%2Fimage.png?alt=media&amp;token=67ed7c04-8d66-4bdd-931b-c77616487219" alt=""><figcaption></figcaption></figure>

This is because the msfvenom payload we're using does not clean up the stack and return properly. There's another issue with this payload. The msfvenom payload uses several "call" opcodes that are going to be problematic for our call stack sanitization, no matter how clever we are with our onRamp.

Luckily for us, there is a robust calc payload implementation developed by @0xboku that we can use and customize with nasm so that we can achieve clean call stack execution.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FGy7ew6nKbBfProqzT02G%2Fimage.png?alt=media&amp;token=bf1eef41-9e8b-4ef1-a2f6-6edd405dd96f" alt=""><figcaption><p><a href="https://github.com/boku7/x64win-DynamicNoNull-WinExec-PopCalc-Shellcode/blob/main/win-x64-DynamicKernelWinExecCalc.asm">https://github.com/boku7/x64win-DynamicNoNull-WinExec-PopCalc-Shellcode/blob/main/win-x64-DynamicKernelWinExecCalc.asm</a></p></figcaption></figure>

### Part Four: Building the payload

Now that we have a robust method of payload execution, we can build custom on/off ramps to achieve clean call stack code execution, and customize our payload to leverage the ramps

This custom payload only has two "call" instructions, so we should be able to quickly patch those to achieve code execution without valid call stack traces! We also see that in its existing implementation, we execute our payload

If we take a look at the x64 convention, we can see that the several registers are listed as nonvolatile, which means we can expect any function call we make with to preserve the value(s) stored in those registers

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FyDpCfIDbF53E7yR9A8AS%2Fimage.png?alt=media&amp;token=5edf34cf-9407-442a-b530-33f6039ddb0e" alt=""><figcaption></figcaption></figure>

Now that we know that, we also know that our payload does not use the r13 and r15 registers at any point, which seems like the perfect places to store our return values.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2Fyl0DD81TIPg70zvPgr1b%2Fimage.png?alt=media&amp;token=335db47d-43f3-4340-9c03-8854d051530a" alt=""><figcaption></figcaption></figure>

Once we've built the on ramp, we need to make a couple of modifications to our payload in order to retain its functionality.

For the time being, we'll use an interrupt offRamp.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FCvPAWSYZKHYYRMuzsw5z%2Fimage.png?alt=media&amp;token=97505af8-9736-40d4-bae2-9f559cf76638" alt=""><figcaption></figcaption></figure>

The first is replacing the "call r14" instruction with a push+jmp instruction

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FWCAXs31njvZSqckQEnnv%2Fimage.png?alt=media&amp;token=72712382-a9ec-4e90-9c6f-86064d7ebab7" alt=""><figcaption></figcaption></figure>

The second change is to patch up the other call instruction, all the changes are visible in the screenshot below. It's important to remember that you'll have to change some prologues in order to keep the stack organized after removing the call instructions.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FZZsF8jm6rjrup80sxYi1%2Fimage.png?alt=media&amp;token=dc6e89da-122f-4a91-85c2-9b3b6d133843" alt=""><figcaption></figcaption></figure>

And if we run this code, we see that it works!

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FlYGgjYfvjwbmZdE4A6gL%2Fimage.png?alt=media&amp;token=048cfd01-d05e-4a1d-8d52-a0c8444ca3c0" alt=""><figcaption></figcaption></figure>

### Part Five: Cleaning up

Now, our offRamp function needs to clean up the stack. Currently the stack looks like this:

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FGwbS39zhhiBrtzPlhURC%2Fimage.png?alt=media&amp;token=a305466f-84e9-4863-8d58-a39405e9ad84" alt=""><figcaption></figcaption></figure>

But we know that the we can pop everything off the stack until rsp = r13, so lets implement that in our offRamp function

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2Fb5BVqQk3IriQLk3lrS5k%2Fimage.png?alt=media&amp;token=e3cf5cfe-833f-49c2-aaa4-c377b9296328" alt=""><figcaption></figcaption></figure>

Now when we land on the interrupt, our implant is ready to return to main()

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FVPuKe9dkhp38VaIr2cfP%2Fimage.png?alt=media&amp;token=c1190656-4414-4145-843b-4b47b60f2073" alt=""><figcaption></figcaption></figure>

Our final version of offRamp looks like this:

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FnXgv9aiENjulLcFZxZON%2Fimage.png?alt=media&amp;token=1340f2c6-b34e-4023-a882-ed99328c72fb" alt=""><figcaption></figcaption></figure>

Based on our source code, we know that if we succeeded, we should see the "Exiting implant…" message in our console. And we have a working program!

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FFmDBuBf2lZJvpxIuyXcc%2Fimage.png?alt=media&amp;token=9d2d6655-740c-4c5c-bc88-bf46ad408a63" alt=""><figcaption></figcaption></figure>

Scrutinizing our stack throughout program execution, we can see that x64dbg correctly sees that we made this call from our payload, x 0x…2850 but we're returning to a regular .text section of memory!

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FVBdYe3rggUqJoaAteRPJ%2Fimage.png?alt=media&amp;token=c4cadb66-8a69-4958-9d63-812599036d86" alt=""><figcaption></figcaption></figure>

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FPl8QOGxvMZShkpuH6qBd%2Fimage.png?alt=media&amp;token=2b5b9d2e-95c6-4d47-b098-6a89ca6b9370" alt=""><figcaption><p>The return "To" address is within the range of our .text section</p></figcaption></figure>

### Part Six: Conclusion

This methodology can be a little clunky, but it provides a lot of non-standard functionality that may not be immediately obvious. The onRamp() function takes in a return\_address variable that could have been pulled from the stack because we properly call onRamp(). That technique is certainly viable, but by deliberately passing in our desired return\_address as a function argument we can actually return anywhere that we want and thereby obfuscate control flow analysis of our program.

The technique is not perfect, and it  requires custom payloads that work in concert with the onRamp/offRamp functions in order to function properly. This technique will probably never gain significant mainstream attention because of those limitations. However, it's still a cool technique and it's something very possible to implement using the methods above.

### References

{% embed url="<https://0xdarkvortex.dev/hiding-in-plainsight/>" %}

{% embed url="<https://github.com/boku7/x64win-DynamicNoNull-WinExec-PopCalc-Shellcode/blob/main/win-x64-DynamicKernelWinExecCalc.asm>" %}

{% embed url="<https://github.com/0xTriboulet/Red_Team_Code_Snippets/tree/main/Cpp/clean_stacks>" %}
