/* JSRF Decompilation: XDK/Xapi/xapi0.c Entrypoint for Xbox programs. Awkwardly, the placement in the .xbe suggests this is not part of the C runtime (which is normally what provides mainCRTStartup()), but rather part of Xapi. It's been named xapi0 in analogy to crt0 in a C runtime. */ #include "../CRT/stddef.h" #include "../Win32.h" // Things that should get declared in a header somewhere #define XLD_LAUNCH_DASHBOARD_ERROR 1 #define XLD_ERROR_INVALID_XBE 1 extern struct IMAGE_TLS_DIRECTORY { DWORD StartAddressOfRawData; DWORD EndAddressOfRawData; DWORD * AddressOfIndex; void * AddressOfCallbacks; DWORD SizeOfZeroFill; DWORD Characteristics; } _tls_used; extern int _tls_array; extern int _tls_index; extern int XapiTlsSize; DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter); void __stdcall XapiInitProcess(void); BOOL __stdcall CloseHandle (HANDLE hHandle); typedef DWORD (__stdcall * LPTHREAD_START_ROUTINE)(LPVOID lpThreadParameter); HANDLE __stdcall CreateThread( void * lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, DWORD * lpThreadId ); void __stdcall XapiBootToDash( DWORD dwReason, DWORD dwParameter1, DWORD dwParameter2 ); void __stdcall _rtinit(void); void __stdcall _cinit (void); // Every program is supposed to have a main(), so we can just assume its // existence with a declaration here. Interestingly, it's called with // arguments here despite being declared without any on the developer's side. int main( int argc, char * * argv, char * * envp ); // Address: 0x00147FB4 // Status: matching __declspec(naked) DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter) { /* Runs some Xbox-specific initialization and then calls main() It appears to be truly impossible in Visual C++ 7.0 to access the FS register without writing assembly, and also for inline assembly to write to a variable in a register rather than on the stack. Thus, the only apparent way to make the middle part of the function match is through writing the instructions directly. Given that the C parts of this function are so simple that a human and a compiler would reasonably produce the exact same code, and this function exhibits other oddities mentioned in the body, it's likely this whole function was originally written purely in assembly. In the spirit of decompilation, however, we'll lift what we can into C. */ XapiInitProcess(); // Honestly unsure what's being done here, as FS doesn't seem to follow // the same structure as 32-bit Windows __asm { mov eax, fs:[0x20] mov eax, [eax+0x250] test eax, eax je zero mov ecx, [eax+0x24] jmp store /* All this jumping around is strange, as it could easily be a single branch, which is very plain to see if written out as C (in fact, while MSVC's behaviour hasn't been tested, it wouldn't take a very smart compiler to optimize this out if it was written this way in C). It seems likely this is awkward human-written assembly. */ zero: xor ecx, ecx store: test ecx, ecx je end /* This section is suspect because of how it preserves edi despite it not being used anywhere else, as if it was a separately written function. If it was a function, it must have been inlined, but it couldn't be because it must be called from assembly to use the value in ecx that never went on the stack (as passing data from assembly to C has to do). Notably, this function's calling convention does require edi to be preserved, but if left up to the compiler, it will push and pop edi in the prologue and epilogue, not here (which this function must be declared as naked to avoid). It may be that this function was originally written entirely in assembly and declared naked, with the author deciding to preserve edi only in this one section using it. */ push edi mov eax, fs:[0x28] mov edi, fs:[_tls_array] mov edx, [_tls_index] mov edx, [edi + edx*0x4] sub edx, [eax + 0x28] mov byte ptr [ecx], 0x1 add edx, _tls_array mov [ecx + 0x4], edx pop edi end: }; _rtinit(); _cinit(); main(0, NULL, NULL); // main() is not supposed to return, so error out if it does XapiBootToDash(XLD_LAUNCH_DASHBOARD_ERROR, XLD_ERROR_INVALID_XBE, 0); // Return 0 to satisfy signature required for thread functions _asm { xor eax, eax ret 4 }; } // Address: 0x00148023 // Status: matching void mainCRTStartup(void) { /* The true entrypoint of the game, spawning a thread for the rest to run in The linker automatically sets this function to the entrypoint. */ HANDLE thread; // Figure out available thread-local storage (used by CreateThread()) XapiTlsSize = 4 + (( (_tls_used.EndAddressOfRawData - _tls_used.StartAddressOfRawData) + _tls_used.SizeOfZeroFill ) + 15 & 0xFFFFFFF0); // Round up to nearest 0x10 *_tls_used.AddressOfIndex = -1 * XapiTlsSize/4; // Launch program as a thread thread = CreateThread(NULL, 0, mainXapiStartup, NULL, 0, NULL); // Boot back to dashboard with an error message if launching failed if (thread == NULL) XapiBootToDash(XLD_LAUNCH_DASHBOARD_ERROR, XLD_ERROR_INVALID_XBE, 0); CloseHandle(thread); }