├── .gitmodules ├── .editorconfig ├── release_x64.bat ├── release_x86.bat ├── .gitignore ├── appveyor.yml ├── README.md ├── Katy ├── offsets.x64.ini ├── offsets.x86.ini ├── shared.h ├── Katy.vcxproj └── main.cpp ├── Katy.sln └── Injector ├── Injector.vcxproj └── main.cpp /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "minhook"] 2 | path = minhook 3 | url = https://github.com/TsudaKageyu/minhook.git 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | end_of_line = crlf 6 | insert_final_newline = true 7 | 8 | [*.{h,c,cpp}] 9 | charset = utf-8 10 | indent_style = space 11 | indent_size = 4 12 | 13 | [*.{yml}] 14 | charset = utf-8 15 | indent_style = space 16 | indent_size = 2 -------------------------------------------------------------------------------- /release_x64.bat: -------------------------------------------------------------------------------- 1 | git pull origin master 2 | git submodule update --init --recursive 3 | @IF %ERRORLEVEL% NEQ 0 GOTO err 4 | 5 | "C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" /m Katy.sln /p:Configuration=Release "/p:Platform=x64" 6 | @IF %ERRORLEVEL% NEQ 0 GOTO err 7 | 8 | @exit /B 0 9 | :err 10 | @PAUSE 11 | @exit /B 1 -------------------------------------------------------------------------------- /release_x86.bat: -------------------------------------------------------------------------------- 1 | git pull origin master 2 | git submodule update --init --recursive 3 | @IF %ERRORLEVEL% NEQ 0 GOTO err 4 | 5 | "C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" /m Katy.sln /p:Configuration=Release "/p:Platform=Win32" 6 | @IF %ERRORLEVEL% NEQ 0 GOTO err 7 | 8 | @exit /B 0 9 | :err 10 | @PAUSE 11 | @exit /B 1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | [D|d]ebug/* 3 | [R|r]elease/* 4 | ipch/* 5 | x64/* 6 | .vs/* 7 | 8 | *.exe 9 | *.dll 10 | *.lib 11 | *.obj 12 | *.pdb 13 | *.ilk 14 | *.idb 15 | *.ipch 16 | *.db 17 | 18 | *.sdf 19 | *.opensdf 20 | *.opendb 21 | #*.sln 22 | *.suo 23 | #*.vcxproj* 24 | *.vcxproj.user 25 | *.vcxproj.filters 26 | 27 | *.tlog 28 | *.log 29 | 30 | *.pkt 31 | *.bin 32 | *.rar 33 | *.7z 34 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Specify version format 2 | version: 1.0.{build} 3 | 4 | # build platform, i.e. Win32 (instead of x86), x64, Any CPU. This setting is optional. 5 | platform: 6 | - Win32 7 | - x64 8 | 9 | # build configuration, i.e. Debug, Release, etc. 10 | configuration: 11 | - Debug 12 | - Release 13 | 14 | clone_depth: 1 15 | 16 | before_build: 17 | - cmd: git submodule update --init --recursive 18 | 19 | # build settings 20 | build: 21 | project: Katy.sln 22 | parallel: true 23 | verbosity: minimal 24 | 25 | # unit test 26 | test: off 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Katy 2 | Katy - Wow packet sniffer for (x86, x64 client) 3 | 4 | [![Build status](https://ci.appveyor.com/api/projects/status/trp02gu6y6k3diya?svg=true)](https://ci.appveyor.com/project/Konctantin/katy) 5 | 6 | # Build instructions 7 | First grab the code using git: 8 | 9 | ``` 10 | git clone https://github.com/Konctantin/Katy.git 11 | cd Katy 12 | git submodule update --init --recursive 13 | ``` 14 | 15 | Use Visual Studio 2015 to build it once you have all dependencies. 16 | 17 | #Dump format 18 | 19 | ```c++ 20 | struct MainHeader 21 | { 22 | char signature[3]; // 'PKT' 23 | byte version[2]; // 0x01, 0x03 24 | byte snifferID; 25 | uint build; 26 | char language[4]; // Client locale: 'enGB', 'enUS', 'deDE', 'ruRU' and ect. 27 | byte sessionKey[40]; // all zero 28 | uint unixTime; 29 | uint tickCount; 30 | uint optionalHeaderLength; 31 | }; 32 | byte[optionalHeaderLength] optionalData; 33 | ``` 34 | ```c++ 35 | struct ChunkHeader 36 | { 37 | char direction[4]; // 'SMSG', 'CMSG' 38 | uint sessionID; 39 | uint tickCount; 40 | uint optionalDataLength; 41 | uint dataLength; 42 | }; 43 | byte[optionalDataLength] optionalData; 44 | byte[dataLength] data; 45 | ``` 46 | 47 | # Dependencies: 48 | * https://github.com/TsudaKageyu/minhook -------------------------------------------------------------------------------- /Katy/offsets.x64.ini: -------------------------------------------------------------------------------- 1 | [search] 2 | send=48 89 5C 24 10 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 30 48 8B D9 48 81 C1 90 00 00 00 3 | recv=4C 8B DC 49 89 5B 10 49 89 73 18 57 48 83 EC 50 48 8B D9 8B 8C 24 80 00 00 00 4 | lang=ruRU 5 | 6 | #WOD 7 | [20886] 8 | send=0x0475C80 9 | recv=0x0473700 10 | lang=0x179D630 11 | 12 | #legion beta 13 | [21021] 14 | send=0x04DE250 15 | recv=0x04DD2A0 16 | lang=0x1B20898 17 | 18 | [21348] 19 | send=0x04767C0 20 | recv=0x0474350 21 | lang=0x1616958 22 | 23 | [21355] 24 | send=0x0476640 25 | recv=0x04741D0 26 | lang=0x1616958 27 | 28 | [21463] 29 | send=0x04764D0 30 | recv=0x0474060 31 | lang=0x1616958 32 | 33 | [21676] 34 | send=0x004764D0 35 | recv=0x00474060 36 | lang=0x01616958 37 | 38 | [21742] 39 | send=0x004764D0 40 | recv=0x00474060 41 | lang=0x01616958 42 | 43 | # legion 44 | [22289] 45 | send=0x00424D70 46 | recv=0x00424370 47 | lang=0x0181D564 48 | 49 | [22293] 50 | send=0x00424D70 51 | recv=0x00424370 52 | lang=0x0181D564 53 | 54 | [22345] 55 | send=0x004252F0 56 | recv=0x004248F0 57 | lang=0x0181F644 58 | 59 | [22396] 60 | send=0x004257B0 61 | recv=0x00424DB0 62 | lang=0x0181F684 63 | 64 | [22410] 65 | send=0x00425980 66 | recv=0x00424F80 67 | lang=0x0181F684 68 | 69 | [22423] 70 | send=0x00425850 71 | recv=0x00424E50 72 | lang=0x018206C4 73 | 74 | [22498] 75 | send=0x00426170 76 | recv=0x00425680 77 | lang=0x01825744 78 | 79 | [22522] 80 | send=0x004257E0 81 | recv=0x00424CF0 82 | lang=0x01825744 83 | 84 | [22989] 85 | send=0x00423E20 86 | recv=0x00423330 87 | lang=0x00000000 88 | -------------------------------------------------------------------------------- /Katy.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Katy", "Katy\Katy.vcxproj", "{EF46EE76-1AC6-4310-B9BD-ED6617020B3F}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE} = {F142A341-5EE0-442D-A15F-98AE9B48DBAE} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Injector", "Injector\Injector.vcxproj", "{23EC2AA1-8489-4D23-8B18-36A72E84B79E}" 12 | EndProject 13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libMinHook", "minhook\build\VC15\libMinHook.vcxproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Win32 = Debug|Win32 18 | Debug|x64 = Debug|x64 19 | Release|Win32 = Release|Win32 20 | Release|x64 = Release|x64 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F}.Debug|Win32.ActiveCfg = Debug|Win32 24 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F}.Debug|Win32.Build.0 = Debug|Win32 25 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F}.Debug|x64.ActiveCfg = Debug|x64 26 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F}.Debug|x64.Build.0 = Debug|x64 27 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F}.Release|Win32.ActiveCfg = Release|Win32 28 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F}.Release|Win32.Build.0 = Release|Win32 29 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F}.Release|x64.ActiveCfg = Release|x64 30 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F}.Release|x64.Build.0 = Release|x64 31 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E}.Debug|Win32.ActiveCfg = Debug|Win32 32 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E}.Debug|Win32.Build.0 = Debug|Win32 33 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E}.Debug|x64.ActiveCfg = Debug|x64 34 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E}.Debug|x64.Build.0 = Debug|x64 35 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E}.Release|Win32.ActiveCfg = Release|Win32 36 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E}.Release|Win32.Build.0 = Release|Win32 37 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E}.Release|x64.ActiveCfg = Release|x64 38 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E}.Release|x64.Build.0 = Release|x64 39 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.ActiveCfg = Debug|Win32 40 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|Win32.Build.0 = Debug|Win32 41 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64 42 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64 43 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.ActiveCfg = Release|Win32 44 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|Win32.Build.0 = Release|Win32 45 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64 46 | {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64 47 | EndGlobalSection 48 | GlobalSection(SolutionProperties) = preSolution 49 | HideSolutionNode = FALSE 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /Katy/offsets.x86.ini: -------------------------------------------------------------------------------- 1 | [search] 2 | send=55 8B EC 83 EC 10 53 56 8B F1 57 8D 9E 8C 00 00 00 8B CB 3 | recv=55 8B EC 51 FF 05 ?? ?? ?? ?? 8D 45 FC 4 | lang=ruRU 5 | 6 | # Clasic 7 | [5875] 8 | send=0x1B5630 9 | recv=0x137AA0 10 | lang=0x000000 11 | 12 | # TBC 13 | [8606] 14 | send=0x0203B0 15 | recv=0x15F440 16 | lang=0x000000 17 | 18 | # WotLK 19 | [12340] 20 | send=0x0675F0 21 | recv=0x231FE0 22 | lang=0x000000 23 | 24 | # Cata 25 | [13623] 26 | send=0x15EF20 27 | recv=0x090360 28 | lang=0x000000 29 | 30 | [15595] 31 | send=0x089590 32 | recv=0x0873D0 33 | lang=0x000000 34 | 35 | # MOP 36 | [16135] 37 | send=0x3F9AE0 38 | recv=0x3F7710 39 | lang=0x000000 40 | 41 | [16357] 42 | send=0x40C5D0 43 | recv=0x40A210 44 | lang=0x000000 45 | 46 | [16650] 47 | send=0x448D10 48 | recv=0x446720 49 | lang=0x000000 50 | 51 | [16709] 52 | send=0x448FB0 53 | recv=0x446A00 54 | lang=0x000000 55 | 56 | [16826] 57 | send=0x448E40 58 | recv=0x446880 59 | lang=0x000000 60 | 61 | [16981] 62 | send=0x363B57 63 | recv=0x361C6D 64 | lang=0x000000 65 | 66 | [16983] 67 | send=0x36400D 68 | recv=0x362123 69 | lang=0x000000 70 | 71 | [16992] 72 | send=0x36424A 73 | recv=0x362360 74 | lang=0x000000 75 | 76 | [17055] 77 | send=0x363F76 78 | recv=0x36206E 79 | lang=0x000000 80 | 81 | [17056] 82 | send=0x3E43D9 83 | recv=0x3E1ECC 84 | lang=0x000000 85 | 86 | [17093] 87 | send=0x3EED60 88 | recv=0x3EC853 89 | lang=0x000000 90 | 91 | [17116] 92 | send=0x364654 93 | recv=0x36276A 94 | lang=0x000000 95 | 96 | [17124] 97 | send=0x3F3B0F 98 | recv=0x3F1490 99 | lang=0x000000 100 | 101 | [17128] 102 | send=0x363C88 103 | recv=0x361D9B 104 | lang=0x000000 105 | 106 | [17359] 107 | send=0x391942 108 | recv=0x38F9C5 109 | lang=0x000000 110 | 111 | [17371] 112 | send=0x39192A 113 | recv=0x38F9AD 114 | lang=0x000000 115 | 116 | [17399] 117 | send=0x39199E 118 | recv=0x38FA21 119 | lang=0x000000 120 | 121 | [17538] 122 | send=0x38F1A9 123 | recv=0x38D225 124 | lang=0x000000 125 | 126 | [17658] 127 | send=0x3988D7 128 | recv=0x3965BB 129 | lang=0xE7080C 130 | 131 | [17688] 132 | send=0x3988D7 133 | recv=0x3965BB 134 | lang=0xE7080C 135 | 136 | [17898] 137 | send=0x399B6A 138 | recv=0x3979B2 139 | lang=0xE73344 140 | 141 | [17930] 142 | send=0x39A93E 143 | recv=0x398786 144 | lang=0xE75344 145 | 146 | [17956] 147 | send=0x39A66A 148 | recv=0x398482 149 | lang=0xE75344 150 | 151 | [18019] 152 | send=0x39A8E3 153 | recv=0x3986FB 154 | lang=0xE75344 155 | 156 | [18291] 157 | send=0x399DCD 158 | recv=0x397CC3 159 | lang=0xE7582C 160 | 161 | [18414] 162 | send=0x399DF8 163 | recv=0x397CEE 164 | lang=0xE7582C 165 | 166 | # WOD 167 | [19033] 168 | send=0x2539ED 169 | recv=0x251C5D 170 | lang=0xE84404 171 | 172 | [19034] 173 | send=0x2537A5 174 | recv=0x251C0D 175 | lang=0xE84404 176 | 177 | [19103] 178 | send=0x254D40 179 | recv=0x2531A5 180 | lang=0xE8566C 181 | 182 | [19116] 183 | send=0x255171 184 | recv=0x2535D6 185 | lang=0xE867AC 186 | 187 | [19243] 188 | send=0x254FC8 189 | recv=0x25342D 190 | lang=0xE867AC 191 | 192 | [19342] 193 | send=0x2556F0 194 | recv=0x253B55 195 | lang=0xE867AC 196 | 197 | [19678] 198 | send=0x264162 199 | recv=0x262C53 200 | lang=0xF2382C 201 | 202 | [19702] 203 | send=0x2641CD 204 | recv=0x26262D 205 | lang=0xF2382C 206 | 207 | [19802] 208 | send=0x2656CF 209 | recv=0x263B42 210 | lang=0xED5854 211 | 212 | [19831] 213 | send=0x2656CF 214 | recv=0x263B42 215 | lang=0xED5854 216 | 217 | [20182] 218 | send=0x290E1F 219 | recv=0x28F259 220 | lang=0xF94038 221 | 222 | [20201] 223 | send=0x2907C8 224 | recv=0x28EC02 225 | lang=0xF94038 226 | 227 | [20216] 228 | send=0x290B5C 229 | recv=0x28EF96 230 | lang=0xF94038 231 | 232 | [20253] 233 | send=0x29093E 234 | recv=0x28ED78 235 | lang=0xF94018 236 | 237 | [20338] 238 | send=0x29093E 239 | recv=0x28ED78 240 | lang=0xF94018 241 | 242 | [20444] 243 | send=0x29490F 244 | recv=0x292D51 245 | lang=0xFA0DC0 246 | 247 | [20490] 248 | send=0x2948A3 249 | recv=0x292CE5 250 | lang=0xFA0DC0 251 | 252 | [20574] 253 | send=0x2948FA 254 | recv=0x292D3C 255 | lang=0xFA0DC0 256 | 257 | [20726] 258 | send=0x294507 259 | recv=0x292947 260 | lang=0xFA05D0 261 | 262 | [20779] 263 | send=0x294507 264 | recv=0x292947 265 | lang=0xFA05D0 266 | 267 | [20886] 268 | send=0x2944C4 269 | recv=0x292904 270 | lang=0xFA05D0 271 | 272 | #legion beta 273 | [21021] 274 | send=0x3406C5 275 | recv=0x292904 276 | lang=0xFA05D0 277 | 278 | [21348] 279 | send=0x295950 280 | recv=0x293DA3 281 | lang=0xE97C90 282 | 283 | [21355] 284 | send=0x295A5A 285 | recv=0x293EAD 286 | lang=0xE97C90 287 | 288 | [21463] 289 | send=0x295A0B 290 | recv=0x293E5E 291 | lang=0xE97C90 292 | 293 | [21676] 294 | send=0x00295862 295 | recv=0x00293CB5 296 | lang=0x00E97C90 297 | 298 | [21742] 299 | send=0x00295A0B 300 | recv=0x00293E5E 301 | lang=0x00E97C90 302 | 303 | [22045] 304 | send=0x00333F03 305 | recv=0x0033371B 306 | lang=0x01073C04 307 | 308 | # Legion 309 | [22280] 310 | send=0x002CBF6E 311 | recv=0x002CBEB7 312 | lang=0x00F35654 313 | 314 | [22289] 315 | send=0x002CBC72 316 | recv=0x002CBBBB 317 | lang=0x00F34654 318 | 319 | [22293] 320 | send=0x002CBC72 321 | recv=0x002CBBBB 322 | lang=0x00F34654 323 | 324 | [22345] 325 | send=0x00293CB5 326 | recv=0x002CB3EA 327 | lang=0x00F3579C 328 | 329 | [22396] 330 | send=0x002CB6C7 331 | recv=0x002CB610 332 | lang=0x00F357BC 333 | 334 | [22410] 335 | send=0x002CBC43 336 | recv=0x002CBB8C 337 | lang=0x00F357BC 338 | 339 | [22423] 340 | send=0x002CBDE8 341 | recv=0x002CBD31 342 | lang=0x00F357C4 343 | 344 | [22498] 345 | send=0x002CBFF1 346 | recv=0x002CBF3A 347 | lang=0x00F3A8BC 348 | 349 | [22522] 350 | send=0x002CC266 351 | recv=0x002CC1AF 352 | lang=0x00F3A8BC 353 | 354 | [22908] 355 | send=0x002C4C8F 356 | recv=0x002C4BD8 357 | lang=0x00F8A784 358 | 359 | [22989] 360 | send=0x002C5223 361 | recv=0x002C516C 362 | lang=0x00F8B7A4 363 | -------------------------------------------------------------------------------- /Katy/shared.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define CMSG 0x47534D43 // client to server message 8 | #define SMSG 0x47534D53 // server to client message 9 | 10 | using namespace std; 11 | 12 | #if _WIN64 13 | const PCHAR offsetFileName = "offsets.x64.ini"; 14 | #else 15 | const PCHAR offsetFileName = "offsets.x86.ini"; 16 | #endif 17 | 18 | 19 | #pragma pack(push, 1) 20 | typedef struct _PktHeader { 21 | char Magik[3] = { 'P', 'K', 'T' }; 22 | WORD Version = 0x0301; 23 | BYTE SnifferId = 15; 24 | DWORD Build; 25 | char Locale[4] = { 'x','x','X','X' }; 26 | char SessionKey[40] = { 0 }; 27 | DWORD RawTime; 28 | DWORD TickCount; 29 | DWORD OptHeaderLen = sizeof(DWORD); 30 | DWORD Expansion; 31 | } PktHeader; 32 | #pragma pack(pop) 33 | 34 | typedef struct _CDataStore { 35 | PVOID vTable; 36 | PBYTE buffer; 37 | DWORD base; 38 | DWORD alloc; 39 | DWORD size; 40 | DWORD read; 41 | } CDataStore; 42 | 43 | typedef struct _ProtoEntry { 44 | LPVOID send; 45 | LPVOID recv; 46 | PCHAR name; 47 | } ProtoEntry; 48 | 49 | typedef struct _WowInfo { 50 | DWORD send; 51 | DWORD recv; 52 | DWORD lang; 53 | 54 | bool IsEmpty() { return !send || !recv; } 55 | } Offsets; 56 | 57 | bool GetVerInfoFromProcess(HANDLE hProcess, PDWORD build, PDWORD expansion) 58 | { 59 | char processExePath[MAX_PATH]; 60 | DWORD processExePathSize = hProcess 61 | ? GetModuleFileNameEx(hProcess, NULL, processExePath, MAX_PATH) 62 | : GetModuleFileName(NULL, processExePath, MAX_PATH); 63 | 64 | if (!processExePathSize) 65 | { 66 | printf("ERROR: Can't get path of the process' exe, ErrorCode: %u\n", GetLastError()); 67 | return false; 68 | } 69 | 70 | printf("ExePath: %s\n", processExePath); 71 | 72 | DWORD fileVersionInfoSize = GetFileVersionInfoSize(processExePath, NULL); 73 | if (!fileVersionInfoSize) 74 | { 75 | printf("ERROR: Can't get size of the file version info, ErrorCode: %u\n", GetLastError()); 76 | return false; 77 | } 78 | 79 | PBYTE fileVersionInfoBuffer = new BYTE[fileVersionInfoSize]; 80 | if (!GetFileVersionInfo(processExePath, 0, fileVersionInfoSize, fileVersionInfoBuffer)) 81 | { 82 | printf("ERROR: Can't get file version info, ErrorCode: %u\n", GetLastError()); 83 | delete[] fileVersionInfoBuffer; 84 | return false; 85 | } 86 | 87 | VS_FIXEDFILEINFO* fileInfo = NULL; 88 | if (!VerQueryValue(fileVersionInfoBuffer, "\\", (LPVOID*)&fileInfo, NULL)) 89 | { 90 | printf("ERROR: File version info query is failed.\n"); 91 | delete[] fileVersionInfoBuffer; 92 | return false; 93 | } 94 | 95 | *build = (WORD)( fileInfo->dwFileVersionLS & 0xFFFF); 96 | *expansion = (WORD)((fileInfo->dwFileVersionMS >> 16) & 0xFFFF); 97 | 98 | delete[] fileVersionInfoBuffer; 99 | return true; 100 | } 101 | 102 | DWORD FindOffset(const string pattern) 103 | { 104 | #define ANY_BYTE 0xFFFF 105 | vector patternList; 106 | 107 | for (size_t i = 0; i < pattern.length(); i += 2) 108 | { 109 | auto part = pattern.substr(i, 2); 110 | if (part[0] == '?') 111 | { 112 | patternList.push_back(ANY_BYTE); 113 | } 114 | else 115 | { 116 | int val = stoul(part, nullptr, 16); 117 | patternList.push_back(val); 118 | } 119 | if (part.length() > 1 && part[1] != ' ') 120 | ++i; 121 | } 122 | 123 | MODULEINFO info; 124 | auto baseAddress = GetModuleHandle(NULL); 125 | GetModuleInformation(GetCurrentProcess(), baseAddress, &info, sizeof(info)); 126 | 127 | bool found = false; 128 | for (auto offset = (DWORD_PTR)baseAddress; 129 | offset + patternList.size() < (DWORD_PTR)(baseAddress + info.SizeOfImage); 130 | ++offset) 131 | { 132 | found = true; 133 | for (size_t i = 0; i < patternList.size(); i++) 134 | { 135 | if (patternList[i] != ANY_BYTE // sucessfull any byte "??" 136 | && (BYTE)patternList[i] != *(BYTE*)(offset + i)) 137 | { 138 | found = false; 139 | break; 140 | } 141 | } 142 | 143 | if (found) 144 | { 145 | auto addr = offset - (DWORD_PTR)baseAddress; 146 | return DWORD(addr); 147 | } 148 | } 149 | 150 | return 0; 151 | } 152 | 153 | void CheckPatterns(const char* fileName, Offsets* offsets, DWORD build) 154 | { 155 | printf("\nOffsets not found. Trying to find using a pattern\n\n"); 156 | char buff[MAX_PATH]; 157 | 158 | GetPrivateProfileString("search", "send", "", buff, sizeof(buff), fileName); 159 | offsets->send = FindOffset(string(buff)); 160 | printf("Send offset: 0x%08X\n", offsets->send); 161 | 162 | GetPrivateProfileString("search", "recv", "", buff, sizeof(buff), fileName); 163 | offsets->recv = FindOffset(string(buff)); 164 | printf("Recv offset: 0x%08X\n", offsets->recv); 165 | 166 | if (!offsets->IsEmpty()) 167 | { 168 | char section[10]; 169 | char send[11]; 170 | char recv[11]; 171 | 172 | _snprintf(section, sizeof(section), "%i", build); 173 | _snprintf(send, sizeof(send), "0x%08X", offsets->send); 174 | _snprintf(recv, sizeof(recv), "0x%08X", offsets->recv); 175 | 176 | WritePrivateProfileString(section, "send", send, fileName); 177 | WritePrivateProfileString(section, "recv", recv, fileName); 178 | WritePrivateProfileString(section, "lang", "0x00000000", fileName); 179 | 180 | printf("All offsets saved successfully to %s\n\n", fileName); 181 | } 182 | } 183 | 184 | bool GetWowInfo(const HANDLE hProcess, const HINSTANCE moduleHandle, PktHeader* header, Offsets* offsets) 185 | { 186 | char fileName[MAX_PATH]; 187 | char dllPath[MAX_PATH]; 188 | char section[6]; 189 | 190 | GetModuleFileName((HMODULE)moduleHandle, dllPath, MAX_PATH); 191 | PathRemoveFileSpec(dllPath); 192 | 193 | if (!GetVerInfoFromProcess(hProcess, &header->Build, &header->Expansion)) 194 | { 195 | printf("ERROR: Can't get wow version info!\n\n"); 196 | return false; 197 | } 198 | 199 | _snprintf(fileName, MAX_PATH, "%s\\%s", dllPath, offsetFileName); 200 | _snprintf(section, 6, "%i", header->Build); 201 | 202 | if (_access(fileName, 0) == -1) 203 | { 204 | printf("ERROR: File \"%s\" does not exist.\n", fileName); 205 | printf("\n%s template:\n", offsetFileName); 206 | printf("[%u]\n", header->Build); 207 | printf("send=0xDEADBEEF\n"); 208 | printf("recv=0xDEADBEEF\n"); 209 | printf("lang=0xDEADBEEF\n\n"); 210 | return false; 211 | } 212 | 213 | offsets->send = GetPrivateProfileInt(section, "send", 0, fileName); 214 | offsets->recv = GetPrivateProfileInt(section, "recv", 0, fileName); 215 | offsets->lang = GetPrivateProfileInt(section, "lang", 0, fileName); 216 | 217 | // default lang 218 | GetPrivateProfileString("search", "lang", "xxXX", &header->Locale[0], sizeof(header->Locale) + 1, fileName); 219 | 220 | // check offsets by patterns 221 | if (offsets->IsEmpty()) 222 | { 223 | CheckPatterns(fileName, offsets, header->Build); 224 | } 225 | 226 | return !offsets->IsEmpty(); 227 | } -------------------------------------------------------------------------------- /Injector/Injector.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {23EC2AA1-8489-4D23-8B18-36A72E84B79E} 23 | Win32Proj 24 | 8.1 25 | Injector 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | MultiByte 33 | 34 | 35 | Application 36 | true 37 | v141 38 | MultiByte 39 | 40 | 41 | Application 42 | false 43 | v141 44 | true 45 | MultiByte 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | true 72 | $(ProjectName).$(PlatformTarget) 73 | 74 | 75 | true 76 | $(ProjectName).$(PlatformTarget) 77 | $(SolutionDir)$(Configuration)\ 78 | 79 | 80 | false 81 | $(ProjectName).$(PlatformTarget) 82 | 83 | 84 | false 85 | $(ProjectName).$(PlatformTarget) 86 | $(SolutionDir)$(Configuration)\ 87 | 88 | 89 | 90 | 91 | 92 | Level3 93 | Disabled 94 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 95 | 4996 96 | 97 | 98 | Console 99 | true 100 | Shlwapi.lib;Version.lib;%(AdditionalDependencies) 101 | 102 | 103 | 104 | 105 | 106 | 107 | Level3 108 | Disabled 109 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 110 | 4996 111 | 112 | 113 | Console 114 | true 115 | Shlwapi.lib;Version.lib;%(AdditionalDependencies) 116 | 117 | 118 | 119 | 120 | Level3 121 | 122 | 123 | MaxSpeed 124 | true 125 | true 126 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 127 | 4996 128 | 129 | 130 | Console 131 | false 132 | true 133 | true 134 | Shlwapi.lib;Version.lib;%(AdditionalDependencies) 135 | 136 | 137 | 138 | 139 | Level3 140 | 141 | 142 | MaxSpeed 143 | true 144 | true 145 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 146 | 4996 147 | 148 | 149 | Console 150 | false 151 | true 152 | true 153 | Shlwapi.lib;Version.lib;%(AdditionalDependencies) 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /Katy/Katy.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {EF46EE76-1AC6-4310-B9BD-ED6617020B3F} 23 | Win32Proj 24 | 8.1 25 | Katy 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v141 32 | MultiByte 33 | 34 | 35 | DynamicLibrary 36 | true 37 | v141 38 | MultiByte 39 | 40 | 41 | DynamicLibrary 42 | false 43 | v141 44 | true 45 | MultiByte 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v141 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | true 72 | $(ProjectName).$(PlatformTarget) 73 | $(SolutionDir)minhook\include;$(IncludePath) 74 | 75 | 76 | true 77 | $(ProjectName).$(PlatformTarget) 78 | $(SolutionDir)$(Configuration)\ 79 | $(SolutionDir)minhook\include;$(IncludePath) 80 | 81 | 82 | false 83 | $(ProjectName).$(PlatformTarget) 84 | $(SolutionDir)minhook\include;$(IncludePath) 85 | 86 | 87 | false 88 | $(ProjectName).$(PlatformTarget) 89 | $(SolutionDir)$(Configuration)\ 90 | $(SolutionDir)minhook\include;$(IncludePath) 91 | 92 | 93 | 94 | NotUsing 95 | Level3 96 | Disabled 97 | WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 98 | 99 | 4996 100 | 101 | 102 | Windows 103 | true 104 | $(SolutionDir)lib\$(Configuration)\libMinHook.$(PlatformTarget).lib;Shlwapi.lib;Version.lib;%(AdditionalDependencies) 105 | /NODEFAULTLIB:LIBCMT %(AdditionalOptions) 106 | 107 | 108 | copy $(ProjectDir)offsets.$(PlatformTarget).ini $(OutDir) 109 | 110 | 111 | 112 | 113 | NotUsing 114 | Level3 115 | Disabled 116 | WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 117 | 118 | 119 | 4996 120 | 121 | 122 | Windows 123 | true 124 | $(SolutionDir)lib\$(Configuration)\libMinHook.$(PlatformTarget).lib;Shlwapi.lib;Version.lib;%(AdditionalDependencies) 125 | /NODEFAULTLIB:LIBCMT %(AdditionalOptions) 126 | 127 | 128 | copy $(ProjectDir)offsets.$(PlatformTarget).ini $(OutDir) 129 | 130 | 131 | 132 | 133 | Level3 134 | NotUsing 135 | MaxSpeed 136 | true 137 | true 138 | WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 139 | 140 | 4996 141 | 142 | 143 | Windows 144 | false 145 | true 146 | true 147 | $(SolutionDir)lib\$(Configuration)\libMinHook.$(PlatformTarget).lib;Shlwapi.lib;Version.lib;%(AdditionalDependencies) 148 | /NODEFAULTLIB:LIBCMT %(AdditionalOptions) 149 | 150 | 151 | copy $(ProjectDir)offsets.$(PlatformTarget).ini $(OutDir) 152 | 153 | 154 | 155 | 156 | Level3 157 | NotUsing 158 | MaxSpeed 159 | true 160 | true 161 | WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 162 | 163 | 164 | 4996 165 | 166 | 167 | Windows 168 | false 169 | true 170 | true 171 | $(SolutionDir)lib\$(Configuration)\libMinHook.$(PlatformTarget).lib;Shlwapi.lib;Version.lib;%(AdditionalDependencies) 172 | /NODEFAULTLIB:LIBCMT %(AdditionalOptions) 173 | 174 | 175 | copy $(ProjectDir)offsets.$(PlatformTarget).ini $(OutDir) 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /Injector/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if _WIN64 10 | char* lookingProcessName[] = { "Wow-64.exe", "WowT-64.exe", "WowB-64.exe" }; 11 | char* injectDLLName = "Katy.x64.dll"; 12 | #else 13 | char* lookingProcessName[] = { "Wow.exe", "WowT.exe", "WowB.exe" }; 14 | char* injectDLLName = "Katy.x86.dll"; 15 | #endif 16 | 17 | // gets PIDs of the processes which found by name 18 | std::map GetProcessList(); 19 | 20 | // returns true if the specific process already injeted with the specific DLL 21 | bool IsProcessAlreadyInjected(DWORD PID, const char* moduleName); 22 | 23 | // opens client's process targeted by PID 24 | HANDLE OpenClientProcess(DWORD processID); 25 | 26 | // injects a DLL (by location) to the targeted process (by PID) 27 | bool InjectDLL(DWORD processID, const char* processName, const char* dllLocation); 28 | 29 | int main(int argc, char* argv[]) 30 | { 31 | SetConsoleTitle("WoW injector"); 32 | printf("Welcome to WoW injector.\n"); 33 | 34 | if (argc > 3) 35 | { 36 | printf("ERROR: Invalid parameters. "); 37 | printf("\"Injector.exe [wow_exe_name] [dll_name]\" should be used.\n\n"); 38 | system("pause"); 39 | return 0; 40 | } 41 | else if (argc > 1) 42 | lookingProcessName[0] = argv[1]; 43 | else if (argc > 2) 44 | injectDLLName = argv[2]; 45 | 46 | DWORD processID = 0; 47 | char* processName = ""; 48 | 49 | std::map &pids = GetProcessList(); 50 | if (pids.empty()) 51 | { 52 | printf("Looking process: "); 53 | for (const auto& p : lookingProcessName) 54 | printf("'%s' ", p); 55 | printf("NOT found.\n"); 56 | system("pause"); 57 | return 0; 58 | } 59 | else if (pids.size() == 1) 60 | { 61 | processID = pids.begin()->first; 62 | processName = pids.begin()->second; 63 | printf("'%s' process found, PID: %u\n", processName, processID); 64 | 65 | if (IsProcessAlreadyInjected(processID, injectDLLName)) 66 | { 67 | printf("Process is already injected.\n\n"); 68 | system("pause"); 69 | return 0; 70 | } 71 | } 72 | else 73 | { 74 | printf("Multiple processes found.\n"); 75 | printf("Please select one which will be injected.\n\n"); 76 | 77 | std::list injectedPIDs; 78 | unsigned int idx = 1; 79 | for (auto& itr : pids) 80 | { 81 | printf("[%u] PID: %u (%s)\n", idx++, itr.first, itr.second); 82 | if (IsProcessAlreadyInjected(itr.first, injectDLLName)) 83 | { 84 | printf("Already injected!\n\n"); 85 | injectedPIDs.push_back(itr.first); 86 | } 87 | } 88 | 89 | if (pids.size() == injectedPIDs.size()) 90 | { 91 | printf("All the processes are already injected.\n\n"); 92 | system("pause"); 93 | return 0; 94 | } 95 | 96 | unsigned int selectedIndex = 0; 97 | while (1) 98 | { 99 | processID = 0; 100 | selectedIndex = 0; 101 | 102 | printf("Please select a process, use [index]: "); 103 | scanf("%u", &selectedIndex); 104 | 105 | if (selectedIndex > idx - 1) 106 | { 107 | printf("Your index is too big, max index is %u.\n", idx - 1); 108 | continue; 109 | } 110 | else if (selectedIndex == 0) 111 | { 112 | printf("Your index is invalid, 1-%u should be used.\n", idx - 1); 113 | continue; 114 | } 115 | 116 | auto& itr = pids.begin(); 117 | std::advance(itr, selectedIndex - 1); 118 | processID = itr->first; 119 | processName = itr->second; 120 | 121 | if (std::find(injectedPIDs.begin(), injectedPIDs.end(), processID) != injectedPIDs.end()) 122 | { 123 | printf("This process is already injected. "); 124 | printf("Please choose a different one.\n"); 125 | continue; 126 | } 127 | break; 128 | } 129 | printf("\n"); 130 | } 131 | 132 | char injectorPath[MAX_PATH] = { NULL }; 133 | DWORD injectorPathSize = GetModuleFileName(NULL, injectorPath, MAX_PATH); 134 | if (!injectorPathSize) 135 | { 136 | printf("ERROR: Can't get the injector's path, "); 137 | printf("ErrorCode: %u\n\n", GetLastError()); 138 | system("pause"); 139 | return 0; 140 | } 141 | 142 | char* dllPath = new char[MAX_PATH]; 143 | strncpy_s(dllPath, MAX_PATH, injectorPath, injectorPathSize); 144 | 145 | PathRemoveFileSpec(dllPath); 146 | PathAppend(dllPath, injectDLLName); 147 | 148 | printf("DLL: %s\n", dllPath); 149 | 150 | if (InjectDLL(processID, processName, dllPath)) 151 | { 152 | printf("\nInjection of '%s' is successful.\n\n", injectDLLName); 153 | } 154 | else 155 | { 156 | printf("\nInjection of '%s' is NOT successful.\n\n", injectDLLName); 157 | system("pause"); 158 | } 159 | 160 | delete[] dllPath; 161 | #if _DEBUG 162 | system("pause"); 163 | #endif 164 | return 0; 165 | } 166 | 167 | std::map GetProcessList() 168 | { 169 | std::map pids; 170 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 171 | if (hSnapshot == INVALID_HANDLE_VALUE) 172 | { 173 | printf("ERROR: Can't get snapshot from processes, "); 174 | printf("ErrorCode: %u\n", GetLastError()); 175 | return pids; 176 | } 177 | 178 | PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; 179 | if (Process32First(hSnapshot, &processEntry)) 180 | { 181 | do 182 | { 183 | for (const auto& pname : lookingProcessName) 184 | { 185 | if (!_strcmpi(processEntry.szExeFile, pname)) 186 | pids.insert(std::pair(processEntry.th32ProcessID, pname)); 187 | } 188 | } 189 | while (Process32Next(hSnapshot, &processEntry)); 190 | } 191 | CloseHandle(hSnapshot); 192 | return pids; 193 | } 194 | 195 | bool IsProcessAlreadyInjected(DWORD PID, const char* moduleName) 196 | { 197 | HANDLE clientProcess = OpenClientProcess(PID); 198 | if (clientProcess) 199 | { 200 | HMODULE modules[MAX_PATH]; 201 | DWORD bytesReq = 0; 202 | if (!EnumProcessModules(clientProcess, modules, sizeof(modules), &bytesReq)) 203 | { 204 | printf("Can't get process' modules. ErrorCode: %u\n", GetLastError()); 205 | CloseHandle(clientProcess); 206 | return false; 207 | } 208 | 209 | for (const auto& module : modules) 210 | { 211 | char modulePath[MAX_PATH]; 212 | if (GetModuleFileNameEx(clientProcess, module, modulePath, MAX_PATH)) 213 | { 214 | PathStripPath(modulePath); 215 | if (!strcmp(modulePath, moduleName)) 216 | { 217 | CloseHandle(clientProcess); 218 | return true; 219 | } 220 | } 221 | } 222 | } 223 | else 224 | { 225 | printf("Process can't be opened. "); 226 | printf("So assume that there is no injection.\n"); 227 | CloseHandle(clientProcess); 228 | return false; 229 | } 230 | CloseHandle(clientProcess); 231 | return false; 232 | } 233 | 234 | HANDLE OpenClientProcess(DWORD processID) 235 | { 236 | HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | 237 | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION | 238 | PROCESS_CREATE_THREAD, FALSE, processID); 239 | 240 | if (!hProcess) 241 | { 242 | if (GetLastError() == ERROR_ACCESS_DENIED) 243 | { 244 | printf("Process open is failed, ERROR_ACCESS_DENIED.\n"); 245 | printf("Trying to override client's security descriptor (DACL) "); 246 | printf("and will try a re-open.\n"); 247 | 248 | DWORD error = 0; 249 | PACL dacl; 250 | PSECURITY_DESCRIPTOR securityDescriptor; 251 | 252 | error = GetSecurityInfo(GetCurrentProcess(), SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, NULL, &securityDescriptor); 253 | if (error) 254 | { 255 | printf("ERROR: Can't get injector's security secriptor, "); 256 | printf("ErrorCode: %u\n", error); 257 | return NULL; 258 | } 259 | 260 | // tries again to open the client process but 261 | // only with an access wich can override its DACL 262 | hProcess = OpenProcess(WRITE_DAC, FALSE, processID); 263 | if (!hProcess) 264 | { 265 | LocalFree(securityDescriptor); 266 | printf("ERROR: Process open is failed with only "); 267 | printf("WRITE_DAC access, ErrorCode: %u\n", GetLastError()); 268 | return NULL; 269 | } 270 | 271 | // overrides client's DACL with injector's DACL 272 | error = SetSecurityInfo(hProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, 0, 0, dacl, 0); 273 | if (error) 274 | { 275 | LocalFree(securityDescriptor); 276 | CloseHandle(hProcess); 277 | printf("ERROR: Can't override client's DACL, "); 278 | printf("ErrorCode: %u\n", error); 279 | return NULL; 280 | } 281 | 282 | LocalFree(securityDescriptor); 283 | CloseHandle(hProcess); 284 | hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); 285 | } 286 | if (!hProcess) 287 | { 288 | printf("ERROR: Process open is failed, "); 289 | printf("ErrorCode: %u\n", GetLastError()); 290 | return NULL; 291 | } 292 | } 293 | return hProcess; 294 | } 295 | 296 | bool InjectDLL(DWORD processID, const char* processName, const char* dllLocation) 297 | { 298 | HMODULE hModule = GetModuleHandle("kernel32.dll"); 299 | if (!hModule) 300 | { 301 | printf("ERROR: Can't get 'kernel32.dll' handle, "); 302 | printf("ErrorCode: %u\n", GetLastError()); 303 | return false; 304 | } 305 | 306 | FARPROC loadLibraryAddress = GetProcAddress(hModule, "LoadLibraryA"); 307 | if (!loadLibraryAddress) 308 | { 309 | printf("ERROR: Can't get function 'LoadLibraryA' address, "); 310 | printf("ErrorCode: %u\n", GetLastError()); 311 | return false; 312 | } 313 | 314 | HANDLE hProcess = OpenClientProcess(processID); 315 | if (!hProcess) 316 | { 317 | printf("Process [%u] '%s' open is failed.\n", processID, processName); 318 | return false; 319 | } 320 | printf("\nProcess [%u] '%s' is opened.\n", processID, processName); 321 | 322 | LPVOID allocatedMemoryAddress = VirtualAllocEx(hProcess, NULL, strlen(dllLocation), MEM_COMMIT, PAGE_READWRITE); 323 | if (!allocatedMemoryAddress) 324 | { 325 | printf("ERROR: Virtual memory allocation is failed, "); 326 | printf("ErrorCode: %u.\n", GetLastError()); 327 | CloseHandle(hProcess); 328 | return false; 329 | } 330 | 331 | if (!WriteProcessMemory(hProcess, allocatedMemoryAddress, dllLocation, strlen(dllLocation), NULL)) 332 | { 333 | printf("ERROR: Process memory writing is failed, "); 334 | printf("ErrorCode: %u\n", GetLastError()); 335 | VirtualFreeEx(hProcess, allocatedMemoryAddress, 0, MEM_RELEASE); 336 | CloseHandle(hProcess); 337 | return false; 338 | } 339 | 340 | HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddress, allocatedMemoryAddress, 0, NULL); 341 | if (!hRemoteThread) 342 | { 343 | printf("ERROR: Remote thread creation is failed, "); 344 | printf("ErrorCode: %u\n", GetLastError()); 345 | VirtualFreeEx(hProcess, allocatedMemoryAddress, 0, MEM_RELEASE); 346 | CloseHandle(hProcess); 347 | return false; 348 | } 349 | 350 | WaitForSingleObject(hRemoteThread, INFINITE); 351 | 352 | VirtualFreeEx(hProcess, allocatedMemoryAddress, 0, MEM_RELEASE); 353 | CloseHandle(hRemoteThread); 354 | CloseHandle(hProcess); 355 | 356 | return true; 357 | } -------------------------------------------------------------------------------- /Katy/main.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "shared.h" 8 | #include 9 | #include "MinHook.h" 10 | 11 | using namespace std; 12 | 13 | #define KATY "Katy, WoW injector packet sniffer" 14 | 15 | mutex mtx; 16 | HINSTANCE instanceDLL = NULL; 17 | FILE* fileDump = NULL; 18 | 19 | Offsets offsets; 20 | PktHeader header; 21 | 22 | LPVOID recvDetour = NULL, sendDetour = NULL; 23 | volatile long cmsgCount = 0L, smsgCount = 0L; 24 | volatile bool isRuning = false; 25 | 26 | char dllPath[MAX_PATH] = { NULL }; 27 | 28 | void DumpPacket(DWORD packetType, DWORD connectionId, DWORD opcode, DWORD size, PBYTE buffer) 29 | { 30 | mtx.lock(); 31 | 32 | time_t rawTime; 33 | time(&rawTime); 34 | DWORD tickCount = GetTickCount(); 35 | 36 | if (!fileDump) 37 | { 38 | tm* date = localtime(&rawTime); 39 | header.TickCount = tickCount; 40 | header.RawTime = (DWORD)rawTime; 41 | 42 | char fileName[MAX_PATH]; 43 | PathRemoveFileSpec(dllPath); 44 | _snprintf(fileName, MAX_PATH, 45 | "wowsniff_%s_%u_%u_%d-%02d-%02d_%02d-%02d-%02d.pkt", 46 | header.Locale, header.Expansion, header.Build, 47 | date->tm_year + 1900, 48 | date->tm_mon + 1, 49 | date->tm_mday, 50 | date->tm_hour, 51 | date->tm_min, 52 | date->tm_sec); 53 | 54 | printf("Sniff dump: %s\n\n", fileName); 55 | 56 | char fullFileName[MAX_PATH]; 57 | _snprintf(fullFileName, MAX_PATH, "%s\\%s", dllPath, fileName); 58 | fileDump = fopen(fullFileName, "wb"); 59 | 60 | fwrite(&header, sizeof(header), 1, fileDump); 61 | fflush(fileDump); 62 | } 63 | 64 | DWORD fullSize = size + sizeof(DWORD); 65 | const DWORD optHeaderLen = 0; 66 | 67 | fwrite((PDWORD)&packetType, 4, 1, fileDump); // direction of the packet 68 | fwrite((PDWORD)&connectionId, 4, 1, fileDump); // connection id 69 | fwrite((PDWORD)&tickCount, 4, 1, fileDump); // timestamp of the packet 70 | fwrite((PDWORD)&optHeaderLen, 4, 1, fileDump); // optional data size 71 | fwrite((PDWORD)&fullSize, 4, 1, fileDump); // size of the packet + opcode lenght 72 | fwrite((PDWORD)&opcode, 4, 1, fileDump); // opcode 73 | 74 | fwrite(buffer, size, 1, fileDump); // data 75 | 76 | #if _DEBUG 77 | printf("%s Opcode: 0x%04X Size: %-8u\n", packetType == CMSG ? "CMSG" : "SMSG", opcode, size); 78 | #endif 79 | 80 | if (packetType == CMSG) 81 | InterlockedAdd(&cmsgCount, 1L); 82 | 83 | if (packetType == SMSG) 84 | InterlockedAdd(&smsgCount, 1L); 85 | 86 | fflush(fileDump); 87 | 88 | mtx.unlock(); 89 | } 90 | 91 | #if _WIN64 92 | 93 | void __fastcall SendHook(LPVOID a1, CDataStore* ds, DWORD connectionId) 94 | { 95 | if (header.Build >= 21336) 96 | { 97 | // skip 4 bytes 98 | DumpPacket(CMSG, connectionId, *(WORD*)(ds->buffer + 4), ds->size - 6, ds->buffer + 6); 99 | } 100 | else 101 | { 102 | DumpPacket(CMSG, connectionId, *(DWORD*)ds->buffer, ds->size - 4, ds->buffer + 4); 103 | } 104 | reinterpret_cast(sendDetour)(a1, ds, connectionId); 105 | } 106 | 107 | DWORD_PTR __fastcall RecvHook_WOD(LPVOID a1, LPVOID a2, LPVOID a3, PBYTE buff, DWORD size) 108 | { 109 | if (header.Build >= 21336) 110 | { 111 | DumpPacket(SMSG, 0, *(WORD*)buff, size - 2, buff + 2); 112 | } 113 | else 114 | { 115 | DumpPacket(SMSG, 0, *(DWORD*)buff, size - 4, buff + 4); 116 | } 117 | return reinterpret_cast(recvDetour)(a1, a2, a3, buff, size); 118 | } 119 | 120 | DWORD_PTR __fastcall RecvHook_Legion(LPVOID a1, LPVOID a2, LPVOID a3, PBYTE buff, DWORD size) 121 | { 122 | DumpPacket(SMSG, 0, *(WORD*)buff, size - 2, buff + 2); 123 | return reinterpret_cast(recvDetour)(a1, a2, a3, buff, size); 124 | } 125 | 126 | const ProtoEntry ProtoTable[] = { 127 | /* 0 */{ &SendHook, NULL , "Aplha" }, 128 | /* 1 */{ &SendHook, NULL , "Vanilla" }, 129 | /* 2 */{ &SendHook, NULL , "TBC" }, 130 | /* 3 */{ &SendHook, NULL , "WotLK" }, 131 | /* 4 */{ &SendHook, NULL , "Cataclysm" }, 132 | /* 5 */{ &SendHook, NULL , "MOP" }, 133 | /* 6 */{ &SendHook, &RecvHook_WOD , "WOD" }, 134 | /* 7 */{ &SendHook, &RecvHook_Legion, "Legion" }, 135 | /* 8 */{ NULL , NULL , "Next" }, 136 | }; 137 | 138 | #else 139 | 140 | DWORD __fastcall SendHook(LPVOID self, LPVOID dummy, CDataStore* ds, DWORD connectionId) 141 | { 142 | if (header.Build >= 21336) 143 | { 144 | // skip 4 bytes 145 | DumpPacket(CMSG, connectionId, *(WORD*)(ds->buffer + 4), ds->size - 6, ds->buffer + 6); 146 | } 147 | else 148 | { 149 | DumpPacket(CMSG, connectionId, *(DWORD*)ds->buffer, ds->size - 4, ds->buffer + 4); 150 | } 151 | 152 | typedef DWORD(__thiscall *proto)(LPVOID, CDataStore*, DWORD); 153 | return reinterpret_cast(sendDetour)(self, ds, connectionId); 154 | } 155 | 156 | #pragma region RecvHook 157 | 158 | DWORD __fastcall RecvHook(LPVOID self, LPVOID dummy, LPVOID param1, CDataStore* ds) 159 | { 160 | DumpPacket(SMSG, 0, *(WORD*)ds->buffer, ds->size - 2, ds->buffer + 2); 161 | typedef DWORD(__thiscall *proto)(LPVOID, LPVOID, CDataStore*); 162 | return reinterpret_cast(recvDetour)(self, param1, ds); 163 | } 164 | 165 | DWORD __fastcall RecvHook_TBC(LPVOID self, LPVOID dummy, LPVOID param1, CDataStore* ds, LPVOID param3) 166 | { 167 | DumpPacket(SMSG, 0, *(WORD*)ds->buffer, ds->size - 2, ds->buffer + 2); 168 | typedef DWORD(__thiscall *proto)(LPVOID, LPVOID, CDataStore*, LPVOID); 169 | return reinterpret_cast(recvDetour)(self, param1, ds, param3); 170 | } 171 | 172 | DWORD __fastcall RecvHook_MOP(LPVOID self, LPVOID dummy, LPVOID param1, CDataStore* ds, LPVOID param3) 173 | { 174 | DumpPacket(SMSG, 0, *(DWORD*)ds->buffer, ds->size - 4, ds->buffer + 4); 175 | typedef DWORD(__thiscall *proto)(LPVOID, LPVOID, CDataStore*, LPVOID); 176 | return reinterpret_cast(recvDetour)(self, param1, ds, param3); 177 | } 178 | 179 | DWORD __fastcall RecvHook_WOD(LPVOID self, LPVOID dummy, LPVOID param1, LPVOID param2, CDataStore* ds, LPVOID param4) 180 | { 181 | if (header.Build >= 21336) 182 | { 183 | DumpPacket(SMSG, 0, *(WORD*)ds->buffer, ds->size - 2, ds->buffer + 2); 184 | } 185 | else 186 | { 187 | DumpPacket(SMSG, 0, *(DWORD*)ds->buffer, ds->size - 4, ds->buffer + 4); 188 | } 189 | 190 | typedef DWORD(__thiscall *proto)(LPVOID, LPVOID, LPVOID, CDataStore*, LPVOID); 191 | return reinterpret_cast(recvDetour)(self, param1, param2, ds, param4); 192 | } 193 | 194 | DWORD __fastcall RecvHook_Legion(LPVOID self, LPVOID dummy, LPVOID param1, LPVOID param2, CDataStore* ds, LPVOID param4) 195 | { 196 | DumpPacket(SMSG, 0, *(WORD*)ds->buffer, ds->size - 2, ds->buffer + 2); 197 | typedef DWORD(__thiscall *proto)(LPVOID, LPVOID, LPVOID, CDataStore*, LPVOID); 198 | return reinterpret_cast(recvDetour)(self, param1, param2, ds, param4); 199 | } 200 | 201 | #pragma endregion 202 | 203 | const ProtoEntry ProtoTable[] = { 204 | /* 0 */{ NULL , NULL , "Aplha" }, 205 | /* 1 */{ &SendHook, &RecvHook , "Vanilla" }, 206 | /* 2 */{ &SendHook, &RecvHook_TBC , "TBC" }, 207 | /* 3 */{ &SendHook, &RecvHook_TBC , "WotLK" }, 208 | /* 4 */{ &SendHook, &RecvHook_TBC , "Cataclysm" }, 209 | /* 5 */{ &SendHook, &RecvHook_MOP , "MOP" }, 210 | /* 6 */{ &SendHook, &RecvHook_WOD , "WOD" }, 211 | /* 7 */{ &SendHook, &RecvHook_Legion, "Legion" }, 212 | /* 8 */{ NULL , NULL , "Next" }, 213 | }; 214 | 215 | #endif 216 | 217 | BOOL __stdcall SignalHandler(DWORD type) 218 | { 219 | printf("\nQuiting...\n"); 220 | isRuning = false; 221 | return TRUE; 222 | } 223 | 224 | bool CreateConsole() 225 | { 226 | if (!AllocConsole()) 227 | return false; 228 | 229 | if (!SetConsoleCtrlHandler(SignalHandler, TRUE)) 230 | return false; 231 | 232 | auto outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); 233 | if (!outputHandle || outputHandle == INVALID_HANDLE_VALUE) 234 | return false; 235 | 236 | SetConsoleTitle(KATY); 237 | 238 | freopen("CONOUT$", "w", stdout); 239 | isRuning = true; 240 | return true; 241 | } 242 | 243 | DWORD MainThreadControl(LPVOID param) 244 | { 245 | if (!CreateConsole()) 246 | FreeLibraryAndExitThread(instanceDLL, 0); 247 | 248 | printf("Welcome to Katy, a WoW injector paket sniffer.\n"); 249 | printf("Katy is distributed under the GNU GPLv3 license.\n"); 250 | printf("Source code is available at: http://github.com/Konctantin/Katy\n\n"); 251 | 252 | DWORD dllPathSize = GetModuleFileName(instanceDLL, dllPath, MAX_PATH); 253 | if (!dllPathSize) 254 | { 255 | printf("\nERROR: Can't get the injected DLL's location, ErrorCode: %u\n\n", GetLastError()); 256 | system("pause"); 257 | FreeLibraryAndExitThread(instanceDLL, 0); 258 | } 259 | 260 | printf("DLL path: %s\n", dllPath); 261 | 262 | if (!GetWowInfo(NULL, instanceDLL, &header, &offsets)) 263 | { 264 | printf("Can't determine build number.\n\n"); 265 | system("pause"); 266 | FreeLibraryAndExitThread(instanceDLL, 0); 267 | } 268 | 269 | if (!header.Build) 270 | { 271 | printf("Can't determine build number.\n\n"); 272 | system("pause"); 273 | FreeLibraryAndExitThread(instanceDLL, 0); 274 | } 275 | 276 | if (header.Expansion >= _countof(ProtoTable)) 277 | { 278 | printf("\nERROR: Unsupported expansion (%u) ", header.Expansion); 279 | system("pause"); 280 | FreeLibraryAndExitThread(instanceDLL, 0); 281 | } 282 | 283 | printf("Detected build number: %hu expansion: %hu\n", header.Build, header.Expansion); 284 | 285 | if (offsets.IsEmpty()) 286 | { 287 | printf("ERROR: This build %u expansion %u is not supported.\n\n", header.Build, header.Expansion); 288 | system("pause"); 289 | FreeLibraryAndExitThread(instanceDLL, 0); 290 | } 291 | 292 | auto baseAddress = (DWORD_PTR)GetModuleHandle(NULL); 293 | 294 | // locale stored in reversed string (enGB as BGne...) 295 | if (offsets.lang) 296 | { 297 | *(DWORD*)header.Locale = _byteswap_ulong(*(DWORD*)(baseAddress + offsets.lang)); 298 | printf("Detected client locale: %s\n", header.Locale); 299 | } 300 | else 301 | { 302 | printf("Use default locale: %s\n", header.Locale); 303 | } 304 | 305 | auto proto = ProtoTable[header.Expansion]; 306 | if (!proto.send || !proto.recv) 307 | { 308 | printf("\nERROR: Unsupported expansion (%u)\n", header.Expansion); 309 | system("pause"); 310 | FreeLibraryAndExitThread(instanceDLL, 0); 311 | } 312 | 313 | #if DEBUG 314 | printf("Found '%s' hooks!\n", proto.name); 315 | #endif 316 | 317 | MH_STATUS status = MH_CreateHook((LPVOID)(baseAddress + offsets.send), proto.send, &sendDetour); 318 | if (status != MH_OK) 319 | { 320 | printf("\nERROR create send '%s' hook (%u) '%s'\n", proto.name, status, MH_StatusToString(status)); 321 | system("pause"); 322 | FreeLibraryAndExitThread(instanceDLL, 0); 323 | } 324 | 325 | status = MH_CreateHook((LPVOID)(baseAddress + offsets.recv), proto.recv, &recvDetour); 326 | if (status != MH_OK) 327 | { 328 | printf("\nERROR create recv '%s' hook (%u) '%s'\n", proto.name, status, MH_StatusToString(status)); 329 | system("pause"); 330 | FreeLibraryAndExitThread(instanceDLL, 0); 331 | } 332 | 333 | status = MH_EnableHook(MH_ALL_HOOKS); 334 | if (status != MH_OK) 335 | { 336 | printf("\nERROR enable '%s' hooks (%u) '%s'\n", proto.name, status, MH_StatusToString(status)); 337 | system("pause"); 338 | FreeLibraryAndExitThread(instanceDLL, 0); 339 | } 340 | 341 | printf("\n%s hooks is installed.\n\n", proto.name); 342 | 343 | printf("Press CTRL-C to stop sniffing (and exit from the sniffer).\n"); 344 | printf("Note: you can simply re-attach the sniffer without restarting the WoW.\n\n"); 345 | 346 | char titleBuff[100]; 347 | while (isRuning) 348 | { 349 | _snprintf(titleBuff, sizeof(titleBuff), "%s. CMSG: %u SMSG: %u", KATY, cmsgCount, smsgCount); 350 | SetConsoleTitle(titleBuff); 351 | Sleep(100); 352 | } 353 | 354 | MH_DisableHook(MH_ALL_HOOKS); 355 | printf("All hook disabled.\n"); 356 | FreeConsole(); 357 | FreeLibraryAndExitThread(instanceDLL, 0); 358 | return 0; 359 | } 360 | 361 | BOOL APIENTRY DllMain(HINSTANCE instDLL, DWORD reason, LPVOID /* reserved */) 362 | { 363 | if (reason == DLL_PROCESS_ATTACH) 364 | { 365 | instanceDLL = instDLL; 366 | DisableThreadLibraryCalls(instDLL); 367 | MH_Initialize(); 368 | CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&MainThreadControl, NULL, 0, NULL); 369 | } 370 | else if (reason == DLL_PROCESS_DETACH) 371 | { 372 | if (fileDump) 373 | fclose(fileDump); 374 | MH_Uninitialize(); 375 | } 376 | return TRUE; 377 | } --------------------------------------------------------------------------------