Self_Modifying-Code

Anti-Analysis_103
Everybody can change, but what about code ?
I would like to say they are loyal… but sorryy they are not :'(
Don’t worry, you can detect it before it upsets you.
Overview
Before all the problems just imagine, your code is changing according to your conditions. It looks like it’s executing a function, but then that code never ever working.
Don’t you think it would be extremely difficult for analysis process ? All the static analysis is wrong (if you can’t detect self modifying) and confusing at dynamic analysis.
To summarize briefly, opcodes at “.TEXT”(.code) section will change at runtime so static analysis will be harder than dynamic analysis. It will be fixed at runtime.
Primitive
Yeah it is easy to say to change the code. Wait… do you know default permission of executable files?
We need to +W permission, but executable files have just RX perm, so first of all we need to write permission.
int main()
{
get_write_perm();
self_func((int*)init_func,(int *)second_func);
// init_func return 6;
// second_func return 0;
std::cout << second_func()<<" Func value " << std::endl;
return 0;
}
Look at that code it seems magical. We are not interested in how to get +WRITE permission in this article, we just get it. If you want to look deeply you can reach that code at link.
Wrong Output
int second_func() {
std::string b = "ifyoucan";
MessageBoxA(0, b.c_str(), "catchme", NULL);
char buffer[MAX_PATH];
DWORD length = GetTempPathA(MAX_PATH, buffer);
MessageBoxA(0, buffer, "path", NULL);
// you need to close optimization settings to use this junk codes
// Junk code reason -> need to more size than which is written to this function
int junk1 = { 5 };
junk1 = junk1 + 26;
junk1 = junk1 + (int)second_func;
junk1 = junk1 ^ 0x55;
junk1 = junk1 * 3;
// junk1 = junk1 - (int)init_func();
spiynxx asd; //Junk class defined at ".h" file
int a = 0;
return a;
};
int init_func(int size) {
MessageBoxA(0, "iGotYou", "catchme", NULL);
int a = 6;
return a;
};
If we call second_func, return value will be “0” and two messagebox will be appear.
Execute->


Yeah I didn’t mentioned “a function” at main, as expected all the work is happening in the self_func. Lets inspect it.
int self_func(int* source_addr, int* dest_addr) {
BYTE* init_func_address = (BYTE*)(void*)source_addr;
BYTE* second_func_address = (BYTE*)(void*)dest_addr;
for (int i = 0; i < 0x100; i++) {
second_func_address[i] = init_func_address[i];
if ((int)init_func_address[i] == 0xc3 || (int)init_func_address[i] == 0xc2 || (int)init_func_address[i] == 0xca || (int)init_func_address[i] == 0xcb)
goto label2;
}
label2:
return 0;
}
Our first parameter is source address, second parameter is destination address. For loop counter is not important if you don’t replace a huge function. If you try to replace huge function you need to rearrange it. Maybe getting size of function with labels can help you.
In our situation, that source function can not be more than 256byte, so we break the loop with “ret” instruction’s opcode(most probably -> c3 || c2).
Now the fatal part ->
second_func_address[i] = init_func_address[i];
At this line, we copy opcodes at our source address(init_func) to destination address(second_func).
Of course we will look it at debugger.
Replace Function

These are functions’s opcodes and both are different.

This marked line is getting byte(opcode) at source function and assign it to dl register. Next instruction is copying source function’s byte to destination function.

Here it is. Now they are same function. But we didn’t touch more than init_func size. Who cares? function will execute until ret (C3) instruction and we give it, so after “ret” isn’t important.
Protect Size
I Mentioned for loop size but what if we try to put bigger function into smaller one.
We would be corrupt memory!! So we need to provide destination to have more size than source.
Solution is simple->
Close-> Compiler optimization
Add-> Junk code
We need to close optimization because “junk code” is unnecessary, so compiler usually automatically fixes it(probably remove).
bool JunkCode8200 = true;
if (JunkCode8200 == true)
JunkCode8200 = false;
try {
JunkCode8200 = true;
}
catch (...) {}
try {
JunkCode8200 = false;
}
catch (...) {}
try {
JunkCode8200 = false;
}
catch (...) {}
bool While5512 = true;
while (While5512 == true) {
JunkCode8200 = false;
While5512 = false;
}
if (JunkCode8200 == true)
JunkCode8200 = true;
else
JunkCode8200 = true;
bool While4363 = true;
while (While4363 == true) {
JunkCode8200 = true;
While4363 = false;
}
bool While9845 = true;
while (While9845 == true) {
JunkCode8200 = false;
While9845 = false;
}
try {
JunkCode8200 = false;
}
catch (...) {}
if (JunkCode8200 == true)
JunkCode8200 = false;
This one is a simple junk code insertion. You can increase function size like this.
Optimization closing->
Project->Properties->C/C+±>Optimization -> in the “Optimization” field, select disabled
Bonus
.code
MySegment segment read write execute
SomeFunction proc
mov eax, ecx ;
mov dword ptr MyFunc, 9005e883h
mov dword ptr MyFunc[+4h],9090c0ffh
;mov dword ptr MyFunc[+8h],90c39090h
mov dword ptr func , 9005e883h
mov dword ptr func[+4h],90c39090h
MyFunc:
add eax, 5 ; sub eax,5
nop ;nop
nop ;inc eax
add eax,5 ;nop nop
add eax,5 ;add eax,5
mov dword ptr func2,9090c031h
jmp oyuk
func2:
add eax,10 ;xor eax - nop - nop ; sub eax,5 -nop
nop
func:
mov eax, 10 ;sub eax 5 ; inc eax
nop
add eax, 5 ; ret
ret
ret
oyuk:
dec eax
dec eax
mov dword ptr func , 9090c0ffh ; inc eax - nop - nop
mov dword ptr func2[+4h] ,9005e883h ; sub eax,5 - nop
;mov dword ptr func2[+950h] ,9005e883h
add eax,5
sub eax,5
jmp func2
SomeFunction endp
MySegment ends
end
This one is my first crush. My whole reverse engineering story started with this code.
So what is it doing ?
If you look at the top there is “read write execute” permission. It means no need to struggle with virtualprotect.
For this example, you need to struggle with opcodes. Also I’m not going to explain it :))
You can resolve opcodes to assembly and get opcodes of assembly instructions with this link
Detection
Actually this part is easy. If your “.TEXT” section has +W permission you can think it may change opcodes, otherwise why does it take the +W permission?

Also if you saw some bytes which is meaningless, you can check if these opcodes or not.
Sometimes it could be shellcode or whatever, but we know it is worth to inspect.
SourceCodes
~It is a rough road that leads to the heights of greatness

