📜 ⬆️ ⬇️

Obfuscation of C ++ Strings in Visual Studio

Binary protection of their programs is often difficult and ungrateful, because if someone needs a product, they will break it anyway, no matter how hard you try. At the same time, the best protection should always be written, or at least tweaked, manually, and all sorts of packers / encoders / virtual machines also help, of course, but the more automatically the protection works, the easier it will then break, and if you use some a well-known packer, his crackers have already broken 10 times in other products, and they know what's in it. In addition, all more or less successful protection packages cost a lot of money.

Again, the more sophisticated the protection system is and the more manual configuration it requires, the more it affects the appearance of the code itself, all sorts of checks, encodings, checksums that, if done properly, need to be pushed to various places in code so cracker is harder to find. The code takes an unreadable form, it becomes more difficult to change it, it all takes even longer, and so on.

So the question is whether to find a compromise between the level of protection / time spent / readability of the code / cost, etc.
')
I want to share with you my own solution for obfuscation of lines in the program, which, although giving only minimal protection, is 1) free 2) easy 3) almost not spoiling the appearance of the code 4) new, which cracker is most likely in this particular configuration have not seen.

I just want to say that this solution will only briefly slow down a confident hacker with experience, but is very simple and takes only about 15 minutes in strength in the initial setup. It is designed to give minimal protection against noob-hackers rather and push you to a more protected type of thinking in coding, and give you a chance to develop this method yourself and implement other types of protection.

Obfuscation of lines - why it is necessary



In the usual compilation of programs in C / C ++, all the lines used in it are in the .exe file in plain text. This is bad because:
  1. If the program uses any passwords / keys, they will be immediately visible to the person who will simply open the file and view it.
  2. Seeing the names of protocols / functions / messages and other system strings, an attacker will be able to get information about which functions / libraries your program uses, and on which algorithms it is built on, thereby greatly simplifying the work of analyzing and hacking the program. One very good example that I just recently caught is - the pokerbotoscar.wordpress.com/2009/03/20/how-casinos-detect-pokerbots at the bottom of this page shows how he made a lot of conclusions about what the poker client does just in lines located in its binary.
  3. Having found certain lines in the program image, the attacker will be able to put a debugger breakpoint directly on them, and for example, instantly find a place in your program, from which, for example, the line “buy a license” is displayed. After that, it will easily detect your license check function (for example), and there it will be a technical matter. If there are no lines in the binary, the function searches will slow down a bit.
    You can easily see all the lines using the Sysinternals Process Explorer program, which immediately shows all the found character strings longer than three letters in any loaded program.


Requirements



My requirements were simple enough - obfuscation should be carried out automatically, minimally change my code, and the lines should not remain in a readable form, either in a binary or in a program image in memory.

Many solutions simply encode strings in a binary, and when a program starts, they go through all strings and decode them. This is counter-productive, because lines are read from the image as easily as from a file — even by Process Explorer. (Which in general is not even a cracker tool but just a substitute for taskmgr.)

I wrote the script for directly encoding strings in php, so you need both php itself and the path to php.exe in PATH.

So let's get down to business.
I used Visual Studio 2008, but everything will work in approximately the same way in other versions of Visual Studio. In other compilers it will be even easier, considering some oddities of VS, about which a bit later.

To begin with - obfuscation header, obfuscator.h
It will need to be connected to each file in which obfuscation will be used.
#ifndef _OBFUSCATOR_H #define _OBFUSCATOR_H #ifdef X #pragma message("MACRO X IS ALREADY DEFINED, EXPECT SERIOUS ERRORS") #endif #ifdef DO_OBFUSCATE_STRINGS __forceinline char *obDecodeStr(char *inst); #define X(s)obDecodeStr(OBPREPROCESSENCODEDSTR(s)) #else #define X(s)s #endif #endif 


As you can see, I use macro X (), in which every obfucated line will be wrapped. Since the name consists of only one letter (for minimal impact on the protected code), and there is a chance that the same macro is also announced elsewhere in the project (or in someone else's library), I added a message that in this case will appear in the build log window.

If DO_OBFUSCATE_STRINGS is defined, then the string is replaced with X (s) obDecodeStr (OBPREPROCESSENCODEDSTR (s))
“OBPREPROCESSENCODEDSTR” is just a token, which will then search for my script encoding strings. The name is specifically made long to exclude that this combination of letters will occur somewhere else in the project. So, the script itself:

 <?php date_default_timezone_set('UTC'); function parseArgs($argv){ array_shift($argv); $out = array(); foreach ($argv as $arg){ // --foo --bar=baz if (substr($arg,0,2) == '--'){ $eqPos = strpos($arg,'='); // --foo if ($eqPos === false){ $key = substr($arg,2); $value = isset($out[$key]) ? $out[$key] : true; $out[$key] = $value; } // --bar=baz else { $key = substr($arg,2,$eqPos-2); $value = substr($arg,$eqPos+1); $out[$key] = $value; } } // -k=value -abc else if (substr($arg,0,1) == '-'){ // -k=value if (substr($arg,2,1) == '='){ $key = substr($arg,1,1); $value = substr($arg,3); $out[$key] = $value; } // -abc else { $chars = str_split(substr($arg,1)); foreach ($chars as $char){ $key = $char; $value = isset($out[$key]) ? $out[$key] : true; $out[$key] = $value; } } } // plain-arg else { $value = $arg; $out[] = $value; } } return $out; } $args = parseArgs($argv); echo "Obfuscating strings in ".$args[1]."\r\n"; $f = fopen($args[0], 'rb'); $o = fopen($args[1], 'wb'); define('ENCODESTRTOKEN', 'OBPREPROCESSENCODEDSTR('); while ($line= fgets ($f)) { while (($esp = strpos($line, ENCODESTRTOKEN))!==false) { $sesp = $esp; $esp+=strlen(ENCODESTRTOKEN); while ($line[$esp]!='"') $esp++; $esp++; $sstart = $esp; $s = ''; while (true) { if ($line[$esp]=='"') break; if ($esp>=strlen($line)) break; if ($line[$esp]=='\\') { if ($line[$esp+1]=='\\') $s.='\\'; if ($line[$esp+1]=='r') $s.="\r"; if ($line[$esp+1]=='n') $s.="\n"; if ($line[$esp+1]=='t') $s.="\t"; $esp+=2; continue; } $s.=$line[$esp]; $esp++; } $enc = ""; $ch = 0; $chphase = 0; while ($ch<strlen($s)) { if ($chphase==0) $cod = ord($s[$ch]) & 15; else $cod = (ord($s[$ch]) & (255-15))/16; $cod = dechex(rand(1,15)*16 + $cod); $enc.="\\x$cod"; if ($chphase==0) $chphase = 1; else { $ch++; $chphase = 0;}; } echo "Obfuscating string \"$s\" to \"$enc\"\r\n"; $line = substr_replace($line, $enc, $sstart, $esp-$sstart); $line = substr_replace($line, "", $sesp, strlen(ENCODESTRTOKEN)-1); }; fputs($o, $line); }; ?> 


As you can see, the script reads the file name from the command line and searches for our tokens, “OBPREPROCESSENCODEDSTR”. Having found such a token, the string is "encoded."

The coding algorithm is certainly not the most robust, but you can easily modify it for yourself if you think it will add more protection. Here it is presented simply as a working example.

Put this file in the root directory of your project and name it obfuscate-i.php

Finally, the file containing the decoding function
 #include "obfuscator.h" typedef char odecoded[4095]; odecoded obbuf[4]; unsigned short lastbuf = 0; __forceinline char *obDecodeStr(char *inst) { lastbuf++; if (lastbuf>3) lastbuf = 0; unsigned int i = 0; unsigned int db = 0; bool phase = true; unsigned short schar = 0; while (inst[i]!=(char)0) { if (phase) { schar = 0; schar+=(((unsigned short)inst[i]) & 0x0F); } else { schar+=(((unsigned short)inst[i]) & 0x0F) * 16; obbuf[lastbuf][db] = (char)schar; db++; } phase = !phase; i++; } obbuf[lastbuf][db] = (char)0; return obbuf[lastbuf]; } 


What is interesting about this function is that it does not decode the string in the place where it is located, but decodes it into a special buffer, the address of which already returns. This leads to the fact that all strings will never be in the decoded state in the program image. Only 4 of them will be decoded, those that were decoded last.

__forceinline is used as an attempt to prevent an attacker from simply putting a breakpoint in the obDecodeStr function itself and magically getting all of our lines.

Why are buffers 4 and not 1?


Imagine calling MessageBox (0, X (“Some value”), X (“some another value”), MB_OK)
If it were not for obfuscation, then just the addresses of the lines would get into the MessageBox function and everything would be fine. But when obfuscation is activated, this call turns into a MessageBox (0, obDecodeStr (“Some value”), obDecodeStr (“some another value”), MB_OK), and both obDecodeStr calls are executed * before * directly executed by MessageBox. And if only one buffer was used, the second call to obDecodeStr would simply rewrite the original string, and the same function would be included in both arguments of the function: MessageBox (0, “some another value”, “some another value”, MB_OK).

Therefore, 4 buffers instead of 1. If you use functions that accept more than 4 obfuscated char * parameters at once, then you will have to increase the number of buffers.

Project configuration



So, how to configure this all automatically?
(I write all the options in English, I think you will understand even if you have a Russian studio)

  1. First of all, copy your release configuration project 2 times. The first copy is called Release-obfuscated-prestep, the second Release-obfuscated. (prestep is needed because VisualStudio cannot save preprocessed files, pass them through an external tool and then compile in one step).
  2. In the Release-obfuscated-prestep configuration, select all .cpp files except obfuscator.cpp and go to Properties. There, under C ++ / Preprocessor / Generate Preprocessed file, select Without Line Numbers / EP / P
    This will lead to the fact that instead of compiling, all .cpp files will be saved with the .i extension in the form processed by the preprocessor. That is, all the macros in them have already been deployed, including our X () macro.
  3. Build this configuration (Release-obfuscated-prestep). The linker will swear at the lack of object files and the build will not finish, but it doesn't matter, we only need the generated .i files.
  4. Find all these new .i files and add them to the project. If the studio asks whether to create a new rule for this extension, you can safely answer NO.
  5. Now select all these files and go to Properties. For all configurations except Release-obfuscated, select Excluded from build → YES for these files.
  6. In the Release-obfuscated configuration, set Excluded from build → NO for these files, and finally, for the same files, in the Custom Build Step settings, in Command Line, enter
    php obfuscate-i.php $(InputPath) src-obfuscated\$(InputName).ob.cpp
    And in the Outputs field, enter
    src-obfuscated\$(InputName).ob.cpp
    You may need to create a src-obfuscated directory into which the processed files will be saved.
  7. Now select all these .i files, right click and select Compile. Visual Studio should call our script, and you should see the work process in the log window, and the files * .ob.cpp should appear
  8. Add these new files to the project, and set Exluded from build to NO for the Release-obfuscated configuration, and to YES for all other configurations.
  9. Select all of your original .cpp files except for obfuscator.cpp, and in the Release-obfuscated configuration, set Excluded From Build to YES
  10. Finally, in the Release-obfuscated-prestep and Release-obfuscated configurations, go to the Properties of the project itself and in C / C ++ / Preprocessor / Preperocessor Definitions add the DO_OBFUSCATE_STRINGS symbol to activate obfuscation.


Now it is enough to wrap each line in X (), connect the obfuscation header and your lines will be protected.

In normal development, use the old Release / Debug configurations, which should remain unchanged, and when the time comes to release a binary, first build the Release-obfuscated-prestep configuration (which does not build to the end), and finally Release-obfuscated, which will generate the “protected” binary

Source: https://habr.com/ru/post/122807/


All Articles