0

I need to make some simple character modifications on compile time, for example instead of:

char text[] = "test text";

I want to be able to write:

char text[] = _MODIFY("test text", 10);

And I want it to be preprocessed as:

char text[] = {116^10,101^10,115^10,116^10,32^10,116^10,101^10,120^10,116^10,0 };

where numbers are ASCII codes XORed with constant. How to define _MODIFY macro preprocessor?

16
  • Macros are basically a mechanism for text replacement. You cannot create loops or other logic with macros. Commented Feb 22, 2024 at 9:10
  • 1
    Do you really want to code the data absent a nul terminator? Why do you think char text[] = "test text"; isn't compiled with the character codes? The 't' is just a shortcut way of writing 116 anyway, but more portable. Commented Feb 22, 2024 at 9:17
  • What kind of "add some simple modification" do you want to do after you convert the string into numerical values? Commented Feb 22, 2024 at 9:21
  • @WeatherVane my mistake - zero as last byte should be present! Commented Feb 22, 2024 at 9:23
  • 1
    @Gerhardh exactly, obfuscate some strings using minimal effort. I will deobfuscate them in code. I don't want to use anything more fancy, because it will be run on very limited microcontroller device Commented Feb 22, 2024 at 9:26

3 Answers 3

1

The transformation you were originally describing is exactly, what the compiler does: char text[] = "test text"; is just a shorthand for char text[] = {116, 101, 115, 116, 32, 116, 101, 120, 116, 0 };.

I don't think, that the CPP is capable of doing that on the string definition, because it works string-based not token-based. So it has now clue, what it parameters you give to a macro, it just performs text expansion.

What you can do, is this:

#define LAZY_EVAL () // important space, stop evaluating macro until expansion

#define EXPAND(...) EXPAND_(EXPAND_(EXPAND_(EXPAND_(__VA_ARGS__))))
#define EXPAND_(...) __VA_ARGS__

#define OBFUSCATE_CHAR(C) ((C)^40)

#define OBFUSCATE_STRING(c, ...) OBFUSCATE_CHAR(c),        \
  __VA_OPT__( OBFUSCATE_RECURSIVE LAZY_EVAL (__VA_ARGS__))
#define OBFUSCATE_RECURSIVE() OBFUSCATE_STRING

#define OBFUSCATE(...) { __VA_OPT__(EXPAND(OBFUSCATE_STRING(__VA_ARGS__))) '\0'}

const char text[] = OBFUSCATE('T', 'e', 'x', 't');

This works around the limitations, that macro expansion isn't recursive by deferring evaluation of macro expansion until EXPAND.
Note, that this limits macro expansion to some depth. If you start seeing your macros again, higher the number of EXPAND_ calls.

See also: Recursive macros via __VA_OPT__

Alternatively, you might achieve it with your build system. Either you are modifying the source, or defining a macro with -D... .

Sign up to request clarification or add additional context in comments.

4 Comments

I updated the question to show what kind of modification I want
@PanJanek Do you want to "obfuscate" the string in the source code or in the executable or in some output?
yes I want to obfucate strings in the resulting binary.
@PanJanek Is that a feature, that you need, because strings need to be in a special format or are you trying to achieve "security"?
0

The C preprocessor simply lacks a feature for doing something like that. If you want that to be done then you have to write your own little preprocessing tool that needs to be executed in a prebuild step for generating the modified source files which are included in your project afterwards.

Comments

0

this is relatively simple to do once you agree to hard-code the length of the string. The hard requirements is that you have to hard-code the string length, because it is not possible to get it in preprocessor. Having the string length, you call the proper macro and expand the chain however you want.

I would first start with a boilerplate FOREACH macro that calls a function with an index and two arguments. As we know the length at preprocessor time, we can call the proper overload FOREACH_##strlen statically.

For the requirement of hardcoding string length, I would throw in a static assertion, making sure you have to change it when you change the string.

#define FOREACH_0(f, a, b)
#define FOREACH_1(f, a, b)   f(0, a, b),
#define FOREACH_2(f, a, b)   FOREACH_1(f, a, b) f(1, a, b),
#define FOREACH_3(f, a, b)   FOREACH_2(f, a, b) f(2, a, b),
#define FOREACH_4(f, a, b)   FOREACH_3(f, a, b) f(3, a, b),
#define FOREACH_5(f, a, b)   FOREACH_4(f, a, b) f(4, a, b),
#define FOREACH_6(f, a, b)   FOREACH_5(f, a, b) f(5, a, b),
#define FOREACH_7(f, a, b)   FOREACH_6(f, a, b) f(6, a, b),
#define FOREACH_8(f, a, b)   FOREACH_7(f, a, b) f(7, a, b),
#define FOREACH_9(f, a, b)   FOREACH_8(f, a, b) f(8, a, b),
#define FOREACH_10(f, a, b)  FOREACH_9(f, a, b) f(9, a, b),
#define FOREACH_N(_10,_9,_8,_7,_6,_5,_4,_3,_2,_1,...)  FOREACH_##N
#define FOREACH(f, a, b, ...) FOREACH_N(__VA_ARGS__,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1)(f, a, b)
    
#define STATIC_ASSERT(...)  \
    sizeof(struct{_Static_assert(__VA_ARGS__); int dummy;})

#define MODIFY_CB(idx, str, num)  str[idx]^num

#define MODIFY(str, strlen, num)  { \
    FOREACH_##strlen(MODIFY_CB, str, num) \
    0 * STATIC_ASSERT(sizeof(str) == strlen + 1) \
    }

char text[] = MODIFY("test text", 9, 10);

Expanding the code to handle more cases is trivial - it's just adding more FOREACH_*.

Notes: identifiers starting with _ and upper case letter are reserved, you are not allowed to define _MODIFY, do not use such identifiers.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.