Mingw je myslim tvrde zadratovane proti msvcrt, to nechci, nebude to chodit.
Tak jsem se do toho pustil. Slinkuju helloworld

. Nejvetsi problem byl tedy poslepovat z jinych projektu konvertor ELF to EXE/PE, aby to umelo spravne prehazet ELF z noveho gcc, generuji se tam nektere veci trochu jinak nez jsem byl zvykly. Navic mi IDA hazi ze import tabulku mam poskozenou, tezko rict co tam mam blbe - EXE ale normalne chodi.
Vysledek:
#include "../windefs.h"
#define STD_INPUT_HANDLE ((DWORD)-10)
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define STD_ERROR_HANDLE ((DWORD)-12)
WINBASEAPI
HANDLE
WINAPI
GetStdHandle(
_In_ DWORD nStdHandle
);
WINBASEAPI
BOOL
WINAPI
WriteFile(
_In_ HANDLE hFile,
LPCVOID lpBuffer,
_In_ DWORD nNumberOfBytesToWrite,
_Out_opt_ LPDWORD lpNumberOfBytesWritten,
_Inout_opt_ void *
);
WINBASEAPI
DECLSPEC_NORETURN
VOID
WINAPI
ExitProcess(
_In_ UINT uExitCode
);
typedef int ssize_t;
typedef int size_t;
[b]
static inline ssize_t write(int fd, const void *buf, size_t count) __attribute__((always_inline));
static inline void exit(int status) __attribute__((always_inline));[/b]
int main()
{
const char *hellostr = "Hello world\n";
write( GetStdHandle(STD_OUTPUT_HANDLE), hellostr, lstrlenA(hellostr));
exit(0);
}
void exit(int status) {ExitProcess(status);}
ssize_t write(int fd, const void *buf, size_t count)
{
DWORD bw;
WriteFile(fd, buf, count, &bw, NULL);
return bw;
}
V disassembleru z toho vypadne:
.text:00401000 _text segment para public 'CODE' use32
.text:00401000 assume cs:_text
.text:00401000 ;org 401000h
.text:00401000 assume es:nothing, ss:nothing, ds:_text, fs:nothing, gs:nothing
.text:00401000 public start
.text:00401000 start dd 0FB1E0FF3h ; toto je endbr32 :)
.text:00401004 ; ---------------------------------------------------------------------------
.text:00401004 lea ecx, [esp+4]
.text:00401008 and esp, 0FFFFFFF0h
.text:0040100B push dword ptr [ecx-4]
.text:0040100E push ebp
.text:0040100F mov ebp, esp
.text:00401011 push ebx
.text:00401012 push ecx
.text:00401013 sub esp, 1Ch
.text:00401016 push offset aHelloWorld ; "Hello world\n"
.text:0040101B call lstrlenA
.text:00401020 mov dword ptr [esp], 0FFFFFFF5h
.text:00401027 mov ebx, eax
.text:00401029 call GetStdHandle
.text:0040102E lea edx, [ebp-0Ch]
.text:00401031 push 0
.text:00401033 push edx
.text:00401034 push ebx
.text:00401035 push offset aHelloWorld ; "Hello world\n"
.text:0040103A push eax
.text:0040103B call WriteFile
.text:00401040 push 0
.text:00401042 call ExitProcess
.text:00401047 ; ---------------------------------------------------------------------------
.text:00401047 lea esp, [ebp-8]
.text:0040104A xor eax, eax
.text:0040104C pop ecx
.text:0040104D pop ebx
.text:0040104E pop ebp
.text:0040104F lea esp, [ecx-4]
.text:00401052 retn
Pres ty inline pujde vyresit spousta veci relativne efektivne, napr. KERNEL32.dll:lstrlenA() budu aliasovat na strlen stejne jako to delam u exitu -> KERNEL32:ExitProcess(). Takhle asi udelam 2 verze knihovny, jedna ktera se bude snazit o maximalni kompatibilitu (tam nebude zadny inline-pro pouziti s dalsimi knihovnami/bloby/atd. ktere budou ocekavat existenci vsech symbolu) a druha ktera nageneruje z prvni knihovny ty inlines (a tim se to rovnou bude preklapet na Windows API).
Tusite jak primet gcc k tomu aby negeneroval ten bordel s endbr/stackem/frameptr ? Moje cmdline + verze:
gcc -m32 -c hello.c -Os -fno-PIC -fno-stack-protector -fomit-frame-pointer # gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)
Pro rypaly: ten kod je proof of concept!