Code obfuscation technique: shifted pointer

Honestly, I don't know its exact name, but I would call it "shifted pointer". This technique is quite common, at least in copy protection schemes.

In short: while writing a value into global memory you use an address, but by reading you use a sum of (other) addresses, or maybe a difference. The goal is to hide a real address from a reverse engineer who debugs the code or exploring it in IDA (or another disassembler).

This can be a nuisance.

#include <stdio.h>

// 64KiB, but it's OK
unsigned char secret_array[0x10000];

void check_lic_key()
{
	// pretend, lic_check has failed
	secret_array[0x6123]=1; // 1 mean failed
	// printf ("check failed\n");
	// exit(0); a cracker may patch here

	// or put there another value if check is succeeded
	// secret_array[0x6123]=0;
};

unsigned char get_byte_at_0x6000(unsigned char *a)
{
	return *(a+0x6000);
};

void check_again()
{
	if (get_byte_at_0x6000(secret_array+0x123)==1)
	{
		// do something mean (add watermark maybe) or report error:
		printf ("check failed\n");
	}
	else
	{
		// proceed further
	};
};

int main()
{
	// at start:
	check_lic_key();

	// do something

	// ... and while in some very critical part:
	check_again();
};

If compiled by non-optimizing MSVC 2015:

_check_lic_key  proc near
                push    ebp
                mov     ebp, esp
                mov     eax, 1
                imul    ecx, eax, 6123h
                mov     _secret_array[ecx], 1
                pop     ebp
                retn
_check_lic_key  endp


; char __cdecl get_byte_at_0x6000(char *a)
_get_byte_at_0x6000 proc near

a               = dword ptr  8

                push    ebp
                mov     ebp, esp
                mov     eax, [ebp+a]
                mov     al, [eax+6000h]
                pop     ebp
                retn
_get_byte_at_0x6000 endp

_check_again    proc near
                push    ebp
                mov     ebp, esp
                push    offset point_passed_to_get_byte_at_0x6000 ; a
                call    j__get_byte_at_0x6000
                add     esp, 4
                movzx   eax, al
                cmp     eax, 1
                jnz     short loc_406735
                push    offset _Format  ; "check failed\n"
                call    j__printf
                add     esp, 4

loc_406735:                             ; CODE XREF: _check_again+16
                pop     ebp
                retn
_check_again    endp

.data:0045F5C0 ; char secret_array[65536]
.data:0045F5C0 _secret_array   db 123h dup(?)          ; DATA XREF: _check_lic_key+E
.data:0045F6E3 ; char point_passed_to_get_byte_at_0x6000[65245]
.data:0045F6E3 point_passed_to_get_byte_at_0x6000 db 0FEDDh dup(?)

You see, IDA can only get two addresses: secret_array[] (start of the array) and point_passed_to_get_byte_at_0x6000.

How to deal with it: you can use hardware breakpoints on memory access operations (tracer has BPMx options) or symbolic execution engine or maybe you can write a plugin for IDA...

Surely, one array can be used for many values, not limited to boolean ones...

P.S. Optimizing MSVC 2015 is smart enough to optimize the get_byte_at_0x6000() function out.


→ [list of blog posts]

Please drop me email about any bug(s) and suggestion(s): dennis(@)yurichev.com.