Skip to content

Commit

Permalink
Feature: Windows PE builder
Browse files Browse the repository at this point in the history
  • Loading branch information
mkostoevr committed Apr 21, 2020
1 parent 7ad4a92 commit 51eb60b
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 6 deletions.
11 changes: 6 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*.o
*.kex
*.exe
*.com
/*.c
/.vscode/*
/.vs/*
/*.exe
/test/*.exe
/test/*.com
/test/*.kex
/test/*.c
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

debug:
tcc src/main.c -Iinclude -DONE_SOURCE -Wall -Wextra -Werror -o obfmc.exe
obfmc test/hello-world.b -o hello-world --c --kos32 --dos16
obfmc.exe test/99-bottles.b -o test/99-bottles --c --kos32 --dos16 --win32
obfmc.exe test/hanoi.b -o test/hanoi --c --kos32 --dos16 --win32
obfmc.exe test/hello-world-long.b -o test/hello-world-long --c --kos32 --dos16 --win32
obfmc.exe test/hello-world.b -o test/hello-world --c --kos32 --dos16 --win32
obfmc.exe test/rot13.b -o test/rot13 --c --kos32 --dos16 --win32

release:
clang src/main.c -Iinclude -DONE_SOURCE -Wall -Wextra -o obfmc.exe
Expand Down
1 change: 1 addition & 0 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Remove loops known to never be entered. This is the case for loops opened at the
Replace copy and multiplication loops with constant time operations. E.g. "[->>+++<+<]" = two RMUL(2, 3), RMUL(1,1)), SET(0).
Constant propogation (whole program may be evaluated in compile time if it has no input).
Fix undefined behaviour of almost all builders (see: "//! FIXME: UB").
Short branch optimization (gen.8086.c, gen.i386.c).
1 change: 1 addition & 0 deletions include/obfmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum {
ERROR_OUTPUT_FILE_IS_NOT_CREATED,
ERROR_CAN_NOT_OPEN_RUNTIME,
ERROR_ILLEGAL_INSTRUCTION,
ERROR_PLACE_FOR_CODE_NOT_FOUND,
};

int bfInit(Bf *restrict bf, size_t sourceSize, char *restrict source);
Expand Down
73 changes: 73 additions & 0 deletions runtime/win32.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
format PE console
entry start

section '.idata' import data readable writeable

dd 0,0,0,RVA kernel_name,RVA kernel_table
dd 0,0,0,RVA crt_name,RVA crt_table
dd 0,0,0,0,0

kernel_table:
ExitProcess dd RVA _ExitProcess
GetProcessHeap dd RVA _GetProcessHeap
HeapAlloc dd RVA _HeapAlloc
dd 0
crt_table:
putchar dd RVA _putchar
getchar dd RVA _getchar
dd 0

kernel_name db 'KERNEL32.DLL',0
crt_name db 'MSVCRT.DLL',0

_ExitProcess dw 0
db 'ExitProcess',0
_HeapAlloc dw 0
db 'HeapAlloc',0
_GetProcessHeap dw 0
db 'GetProcessHeap',0
_putchar dw 0
db 'putchar', 0
_getchar dw 0
db '_getch', 0

section '.text' code readable executable

bf_putchar:
push eax
push ebx
push ecx
push dword[eax]
call [putchar]
add esp, 4
pop ecx
pop ebx
pop eax
ret

bf_getchar:
push ebx
push ecx
push eax
call [getchar]
mov ebx, eax
pop eax
mov byte[eax], bl
pop ecx
pop ebx
ret

start:

call [GetProcessHeap]
push 30000
push 8
push eax
call [HeapAlloc]
mov ebx, bf_putchar
mov ecx, bf_getchar
call brainfuck
push 0
call [ExitProcess]
brainfuck:
db "PLACE_FOR_BRAINFUCK_CODE"
Binary file added runtime/win32.bin
Binary file not shown.
100 changes: 100 additions & 0 deletions src/build.win32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <obfmc.h>

unsigned aligned(unsigned n, int align) {
return (n + align-1) & ~(align-1);
}

int getOffsetOfSpecialMark(char *code, char *max) {
int size = 0;

while (code < max &&
memcmp(code, "PLACE_FOR_BRAINFUCK_CODE", strlen("PLACE_FOR_BRAINFUCK_CODE")))
{ code++; size++; }
if (code >= max) { return 0; }
return size;
}

//! ACHTUNG: Look at "HARDCODED" achtungs before runtime modification
int buildWin32(Bf *restrict bf, const char *restrict name) {
int errorCode = 0;
int runtimeSize = 0;
int imageSize = 0;
char *image = NULL;
size_t codeSize = 0;
char *code = NULL;

{
FILE *runtime = NULL;

if (!(runtime = fopen("runtime/win32.bin", "rb"))) { return ERROR_CAN_NOT_OPEN_RUNTIME; }
// get size of runtime
fseek(runtime, 0, SEEK_END);
runtimeSize = ftell(runtime);
rewind(runtime);
// get code and its size
if ((errorCode = genI386(bf, &codeSize, &code))) { return errorCode; }
// allocate enough memory for runtime (which is aligned and so have space for our code)
// possilbly (if app code will be large) we will need expand image later
imageSize = runtimeSize;
if (!(image = malloc(imageSize))) { return ERROR_OUT_OF_MEMORY; }
// copy runtime code
fread(image, 1, runtimeSize, runtime);
fclose(runtime);
}
{
int codeSectionOffset = 0;
int sizeOfRuntimeCode = 0;
int maxCodeInSection = 0;
int codeOffset = 0;

// check if we need to expand image
codeSectionOffset = *(int *)(image + 0x1b4); //! ACHTUNG: HARDCODED
if (!(sizeOfRuntimeCode = getOffsetOfSpecialMark(image + codeSectionOffset,
image + runtimeSize))) { return ERROR_PLACE_FOR_CODE_NOT_FOUND; }
codeOffset = codeSectionOffset + sizeOfRuntimeCode;
maxCodeInSection = 0x200 - sizeOfRuntimeCode;
// if need - expand it
if (codeSize > maxCodeInSection) {
int additional512ByteParts = 0;
int extraCode = 0;

extraCode = codeSize - maxCodeInSection;
additional512ByteParts = extraCode / 512 + 1;
// expand image
imageSize += additional512ByteParts * 512;
image = realloc(image, imageSize);
// set up PE file fields
//! FIXME: UB
//! ACHTUNG: hardcoded
if (additional512ByteParts) {
// OptionalHeader::SizeOfCode
*(int *)(image + 0x9c) += additional512ByteParts * 512;
// OptionalHeader::SizeOfImage
*(int *)(image + 0xd0) = 0x2000 + aligned(codeSize + sizeOfRuntimeCode, 0x1000);
// SectionHeader::VirtualSize (.text)
*(int *)(image + 0x1a8) += additional512ByteParts * 512;
// SectionHeader::SizeOfRawDara (.text)
*(int *)(image + 0x1b0) += additional512ByteParts * 512;
}
}
// now we can safely copy brainfuck code
memcpy(image + codeOffset, code, codeSize);
}
// write image to file
{
char newName[strlen(name) + 5];
FILE *fp = NULL;

strcpy(newName, name);
strcat(newName, ".exe");
fp = fopen(newName, "wb");
if (!fp) { return ERROR_OUTPUT_FILE_IS_NOT_CREATED; }
fwrite(image, 1, imageSize, fp);
fclose(fp);
}
return 0;
}
3 changes: 3 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# include "build.c.c"
# include "build.dos16.c"
# include "build.kos32.c"
# include "build.win32.c"
#endif

struct Target {
Expand Down Expand Up @@ -64,6 +65,8 @@ int main(int argc, char **argv) {
addTarget(buildKos32);
} else if (!memcmp(argv[i] + 2, "dos16", strlen("dos16"))) {
addTarget(buildDos16);
} else if (!memcmp(argv[i] + 2, "win32", strlen("win32"))) {
addTarget(buildWin32);
} else if (!memcmp(argv[i] + 2, "c", strlen("c"))) {
addTarget(buildC);
}
Expand Down
28 changes: 28 additions & 0 deletions test/rot13.b
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-,+[ Read first character and start outer character reading loop
-[ Skip forward if character is 0
>>++++[>++++++++<-] Set up divisor (32) for division loop
(MEMORY LAYOUT: dividend copy remainder divisor quotient zero zero)
<+<-[ Set up dividend (x minus 1) and enter division loop
>+>+>-[>>>] Increase copy and remainder / reduce divisor / Normal case: skip forward
<[[>+<-]>>+>] Special case: move remainder back to divisor and increase quotient
<<<<<- Decrement dividend
] End division loop
]>>>[-]+ End skip loop; zero former divisor and reuse space for a flag
>--[-[<->+++[-]]]<[ Zero that flag unless quotient was 2 or 3; zero quotient; check flag
++++++++++++<[ If flag then set up divisor (13) for second division loop
(MEMORY LAYOUT: zero copy dividend divisor remainder quotient zero zero)
>-[>+>>] Reduce divisor; Normal case: increase remainder
>[+[<+>-]>+>>] Special case: increase remainder / move it back to divisor / increase quotient
<<<<<- Decrease dividend
] End division loop
>>[<+>-] Add remainder back to divisor to get a useful 13
>[ Skip forward if quotient was 0
-[ Decrement quotient and skip forward if quotient was 1
-<<[-]>> Zero quotient and divisor if quotient was 2
]<<[<<->>-]>> Zero divisor and subtract 13 from copy if quotient was 1
]<<[<<+>>-] Zero divisor and add 13 to copy if quotient was 0
] End outer skip loop (jump to here if ((character minus 1)/32) was not 2 or 3)
<[-] Clear remainder from first division if second division was skipped
<.[-] Output ROT13ed character from copy and clear it
<-,+ Read next character
]

0 comments on commit 51eb60b

Please sign in to comment.