├── README.md ├── detection.sln └── detection ├── App.config ├── PeHeaderReader .cs ├── Program.cs ├── Properties └── AssemblyInfo.cs └── detection.csproj /README.md: -------------------------------------------------------------------------------- 1 | Author: Aden Chung Wee Jing (weejing789@gmail.com) 2 | 3 | Company: Countercept (@countercept) 4 | 5 | Website: https://countercept.com 6 | 7 | 8 | How to Compile/execute 9 | 10 | Program must be compiled in x64 and run with adminstrative privliges. 11 | -------------------------------------------------------------------------------- /detection.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.421 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "detection", "detection\detection.csproj", "{4C736F07-FB70-453A-9D8E-ACB036B4038D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Release|Any CPU = Release|Any CPU 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {4C736F07-FB70-453A-9D8E-ACB036B4038D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {4C736F07-FB70-453A-9D8E-ACB036B4038D}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {4C736F07-FB70-453A-9D8E-ACB036B4038D}.Debug|x64.ActiveCfg = Debug|x64 19 | {4C736F07-FB70-453A-9D8E-ACB036B4038D}.Debug|x64.Build.0 = Debug|x64 20 | {4C736F07-FB70-453A-9D8E-ACB036B4038D}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {4C736F07-FB70-453A-9D8E-ACB036B4038D}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {4C736F07-FB70-453A-9D8E-ACB036B4038D}.Release|x64.ActiveCfg = Release|x64 23 | {4C736F07-FB70-453A-9D8E-ACB036B4038D}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {EC09AE34-7D3D-4094-B817-907249FF8A31} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /detection/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /detection/PeHeaderReader .cs: -------------------------------------------------------------------------------- 1 | // Credits: John Stewien 2 | // From: http://code.cheesydesign.com/?p=572 3 | 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Runtime.InteropServices; 8 | using System.IO; 9 | 10 | namespace detection 11 | { 12 | public class PeHeaderReader 13 | { 14 | #region File Header Structures 15 | 16 | public struct IMAGE_DOS_HEADER 17 | { // DOS .EXE header 18 | public UInt16 e_magic; // Magic number 19 | public UInt16 e_cblp; // Bytes on last page of file 20 | public UInt16 e_cp; // Pages in file 21 | public UInt16 e_crlc; // Relocations 22 | public UInt16 e_cparhdr; // Size of header in paragraphs 23 | public UInt16 e_minalloc; // Minimum extra paragraphs needed 24 | public UInt16 e_maxalloc; // Maximum extra paragraphs needed 25 | public UInt16 e_ss; // Initial (relative) SS value 26 | public UInt16 e_sp; // Initial SP value 27 | public UInt16 e_csum; // Checksum 28 | public UInt16 e_ip; // Initial IP value 29 | public UInt16 e_cs; // Initial (relative) CS value 30 | public UInt16 e_lfarlc; // File address of relocation table 31 | public UInt16 e_ovno; // Overlay number 32 | public UInt16 e_res_0; // Reserved words 33 | public UInt16 e_res_1; // Reserved words 34 | public UInt16 e_res_2; // Reserved words 35 | public UInt16 e_res_3; // Reserved words 36 | public UInt16 e_oemid; // OEM identifier (for e_oeminfo) 37 | public UInt16 e_oeminfo; // OEM information; e_oemid specific 38 | public UInt16 e_res2_0; // Reserved words 39 | public UInt16 e_res2_1; // Reserved words 40 | public UInt16 e_res2_2; // Reserved words 41 | public UInt16 e_res2_3; // Reserved words 42 | public UInt16 e_res2_4; // Reserved words 43 | public UInt16 e_res2_5; // Reserved words 44 | public UInt16 e_res2_6; // Reserved words 45 | public UInt16 e_res2_7; // Reserved words 46 | public UInt16 e_res2_8; // Reserved words 47 | public UInt16 e_res2_9; // Reserved words 48 | public UInt32 e_lfanew; // File address of new exe header 49 | } 50 | 51 | [StructLayout(LayoutKind.Sequential)] 52 | public struct IMAGE_DATA_DIRECTORY 53 | { 54 | public UInt32 VirtualAddress; 55 | public UInt32 Size; 56 | } 57 | 58 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 59 | public struct IMAGE_OPTIONAL_HEADER32 60 | { 61 | public UInt16 Magic; 62 | public Byte MajorLinkerVersion; 63 | public Byte MinorLinkerVersion; 64 | public UInt32 SizeOfCode; 65 | public UInt32 SizeOfInitializedData; 66 | public UInt32 SizeOfUninitializedData; 67 | public UInt32 AddressOfEntryPoint; 68 | public UInt32 BaseOfCode; 69 | public UInt32 BaseOfData; 70 | public UInt32 ImageBase; 71 | public UInt32 SectionAlignment; 72 | public UInt32 FileAlignment; 73 | public UInt16 MajorOperatingSystemVersion; 74 | public UInt16 MinorOperatingSystemVersion; 75 | public UInt16 MajorImageVersion; 76 | public UInt16 MinorImageVersion; 77 | public UInt16 MajorSubsystemVersion; 78 | public UInt16 MinorSubsystemVersion; 79 | public UInt32 Win32VersionValue; 80 | public UInt32 SizeOfImage; 81 | public UInt32 SizeOfHeaders; 82 | public UInt32 CheckSum; 83 | public UInt16 Subsystem; 84 | public UInt16 DllCharacteristics; 85 | public UInt32 SizeOfStackReserve; 86 | public UInt32 SizeOfStackCommit; 87 | public UInt32 SizeOfHeapReserve; 88 | public UInt32 SizeOfHeapCommit; 89 | public UInt32 LoaderFlags; 90 | public UInt32 NumberOfRvaAndSizes; 91 | 92 | public IMAGE_DATA_DIRECTORY ExportTable; 93 | public IMAGE_DATA_DIRECTORY ImportTable; 94 | public IMAGE_DATA_DIRECTORY ResourceTable; 95 | public IMAGE_DATA_DIRECTORY ExceptionTable; 96 | public IMAGE_DATA_DIRECTORY CertificateTable; 97 | public IMAGE_DATA_DIRECTORY BaseRelocationTable; 98 | public IMAGE_DATA_DIRECTORY Debug; 99 | public IMAGE_DATA_DIRECTORY Architecture; 100 | public IMAGE_DATA_DIRECTORY GlobalPtr; 101 | public IMAGE_DATA_DIRECTORY TLSTable; 102 | public IMAGE_DATA_DIRECTORY LoadConfigTable; 103 | public IMAGE_DATA_DIRECTORY BoundImport; 104 | public IMAGE_DATA_DIRECTORY IAT; 105 | public IMAGE_DATA_DIRECTORY DelayImportDescriptor; 106 | public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; 107 | public IMAGE_DATA_DIRECTORY Reserved; 108 | } 109 | 110 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 111 | public struct IMAGE_OPTIONAL_HEADER64 112 | { 113 | public UInt16 Magic; 114 | public Byte MajorLinkerVersion; 115 | public Byte MinorLinkerVersion; 116 | public UInt32 SizeOfCode; 117 | public UInt32 SizeOfInitializedData; 118 | public UInt32 SizeOfUninitializedData; 119 | public UInt32 AddressOfEntryPoint; 120 | public UInt32 BaseOfCode; 121 | public UInt64 ImageBase; 122 | public UInt32 SectionAlignment; 123 | public UInt32 FileAlignment; 124 | public UInt16 MajorOperatingSystemVersion; 125 | public UInt16 MinorOperatingSystemVersion; 126 | public UInt16 MajorImageVersion; 127 | public UInt16 MinorImageVersion; 128 | public UInt16 MajorSubsystemVersion; 129 | public UInt16 MinorSubsystemVersion; 130 | public UInt32 Win32VersionValue; 131 | public UInt32 SizeOfImage; 132 | public UInt32 SizeOfHeaders; 133 | public UInt32 CheckSum; 134 | public UInt16 Subsystem; 135 | public UInt16 DllCharacteristics; 136 | public UInt64 SizeOfStackReserve; 137 | public UInt64 SizeOfStackCommit; 138 | public UInt64 SizeOfHeapReserve; 139 | public UInt64 SizeOfHeapCommit; 140 | public UInt32 LoaderFlags; 141 | public UInt32 NumberOfRvaAndSizes; 142 | 143 | public IMAGE_DATA_DIRECTORY ExportTable; 144 | public IMAGE_DATA_DIRECTORY ImportTable; 145 | public IMAGE_DATA_DIRECTORY ResourceTable; 146 | public IMAGE_DATA_DIRECTORY ExceptionTable; 147 | public IMAGE_DATA_DIRECTORY CertificateTable; 148 | public IMAGE_DATA_DIRECTORY BaseRelocationTable; 149 | public IMAGE_DATA_DIRECTORY Debug; 150 | public IMAGE_DATA_DIRECTORY Architecture; 151 | public IMAGE_DATA_DIRECTORY GlobalPtr; 152 | public IMAGE_DATA_DIRECTORY TLSTable; 153 | public IMAGE_DATA_DIRECTORY LoadConfigTable; 154 | public IMAGE_DATA_DIRECTORY BoundImport; 155 | public IMAGE_DATA_DIRECTORY IAT; 156 | public IMAGE_DATA_DIRECTORY DelayImportDescriptor; 157 | public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; 158 | public IMAGE_DATA_DIRECTORY Reserved; 159 | } 160 | 161 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 162 | public struct IMAGE_FILE_HEADER 163 | { 164 | public UInt16 Machine; 165 | public UInt16 NumberOfSections; 166 | public UInt32 TimeDateStamp; 167 | public UInt32 PointerToSymbolTable; 168 | public UInt32 NumberOfSymbols; 169 | public UInt16 SizeOfOptionalHeader; 170 | public UInt16 Characteristics; 171 | } 172 | 173 | // Grabbed the following 2 definitions from http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html 174 | 175 | [StructLayout(LayoutKind.Explicit)] 176 | public struct IMAGE_SECTION_HEADER 177 | { 178 | [FieldOffset(0)] 179 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 180 | public char[] Name; 181 | [FieldOffset(8)] 182 | public UInt32 VirtualSize; 183 | [FieldOffset(12)] 184 | public UInt32 VirtualAddress; 185 | [FieldOffset(16)] 186 | public UInt32 SizeOfRawData; 187 | [FieldOffset(20)] 188 | public UInt32 PointerToRawData; 189 | [FieldOffset(24)] 190 | public UInt32 PointerToRelocations; 191 | [FieldOffset(28)] 192 | public UInt32 PointerToLinenumbers; 193 | [FieldOffset(32)] 194 | public UInt16 NumberOfRelocations; 195 | [FieldOffset(34)] 196 | public UInt16 NumberOfLinenumbers; 197 | [FieldOffset(36)] 198 | public DataSectionFlags Characteristics; 199 | 200 | public string Section 201 | { 202 | get { return new string(Name); } 203 | } 204 | } 205 | 206 | [Flags] 207 | public enum DataSectionFlags : uint 208 | { 209 | /// 210 | /// Reserved for future use. 211 | /// 212 | TypeReg = 0x00000000, 213 | /// 214 | /// Reserved for future use. 215 | /// 216 | TypeDsect = 0x00000001, 217 | /// 218 | /// Reserved for future use. 219 | /// 220 | TypeNoLoad = 0x00000002, 221 | /// 222 | /// Reserved for future use. 223 | /// 224 | TypeGroup = 0x00000004, 225 | /// 226 | /// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. 227 | /// 228 | TypeNoPadded = 0x00000008, 229 | /// 230 | /// Reserved for future use. 231 | /// 232 | TypeCopy = 0x00000010, 233 | /// 234 | /// The section contains executable code. 235 | /// 236 | ContentCode = 0x00000020, 237 | /// 238 | /// The section contains initialized data. 239 | /// 240 | ContentInitializedData = 0x00000040, 241 | /// 242 | /// The section contains uninitialized data. 243 | /// 244 | ContentUninitializedData = 0x00000080, 245 | /// 246 | /// Reserved for future use. 247 | /// 248 | LinkOther = 0x00000100, 249 | /// 250 | /// The section contains comments or other information. The .drectve section has this type. This is valid for object files only. 251 | /// 252 | LinkInfo = 0x00000200, 253 | /// 254 | /// Reserved for future use. 255 | /// 256 | TypeOver = 0x00000400, 257 | /// 258 | /// The section will not become part of the image. This is valid only for object files. 259 | /// 260 | LinkRemove = 0x00000800, 261 | /// 262 | /// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files. 263 | /// 264 | LinkComDat = 0x00001000, 265 | /// 266 | /// Reset speculative exceptions handling bits in the TLB entries for this section. 267 | /// 268 | NoDeferSpecExceptions = 0x00004000, 269 | /// 270 | /// The section contains data referenced through the global pointer (GP). 271 | /// 272 | RelativeGP = 0x00008000, 273 | /// 274 | /// Reserved for future use. 275 | /// 276 | MemPurgeable = 0x00020000, 277 | /// 278 | /// Reserved for future use. 279 | /// 280 | Memory16Bit = 0x00020000, 281 | /// 282 | /// Reserved for future use. 283 | /// 284 | MemoryLocked = 0x00040000, 285 | /// 286 | /// Reserved for future use. 287 | /// 288 | MemoryPreload = 0x00080000, 289 | /// 290 | /// Align data on a 1-byte boundary. Valid only for object files. 291 | /// 292 | Align1Bytes = 0x00100000, 293 | /// 294 | /// Align data on a 2-byte boundary. Valid only for object files. 295 | /// 296 | Align2Bytes = 0x00200000, 297 | /// 298 | /// Align data on a 4-byte boundary. Valid only for object files. 299 | /// 300 | Align4Bytes = 0x00300000, 301 | /// 302 | /// Align data on an 8-byte boundary. Valid only for object files. 303 | /// 304 | Align8Bytes = 0x00400000, 305 | /// 306 | /// Align data on a 16-byte boundary. Valid only for object files. 307 | /// 308 | Align16Bytes = 0x00500000, 309 | /// 310 | /// Align data on a 32-byte boundary. Valid only for object files. 311 | /// 312 | Align32Bytes = 0x00600000, 313 | /// 314 | /// Align data on a 64-byte boundary. Valid only for object files. 315 | /// 316 | Align64Bytes = 0x00700000, 317 | /// 318 | /// Align data on a 128-byte boundary. Valid only for object files. 319 | /// 320 | Align128Bytes = 0x00800000, 321 | /// 322 | /// Align data on a 256-byte boundary. Valid only for object files. 323 | /// 324 | Align256Bytes = 0x00900000, 325 | /// 326 | /// Align data on a 512-byte boundary. Valid only for object files. 327 | /// 328 | Align512Bytes = 0x00A00000, 329 | /// 330 | /// Align data on a 1024-byte boundary. Valid only for object files. 331 | /// 332 | Align1024Bytes = 0x00B00000, 333 | /// 334 | /// Align data on a 2048-byte boundary. Valid only for object files. 335 | /// 336 | Align2048Bytes = 0x00C00000, 337 | /// 338 | /// Align data on a 4096-byte boundary. Valid only for object files. 339 | /// 340 | Align4096Bytes = 0x00D00000, 341 | /// 342 | /// Align data on an 8192-byte boundary. Valid only for object files. 343 | /// 344 | Align8192Bytes = 0x00E00000, 345 | /// 346 | /// The section contains extended relocations. 347 | /// 348 | LinkExtendedRelocationOverflow = 0x01000000, 349 | /// 350 | /// The section can be discarded as needed. 351 | /// 352 | MemoryDiscardable = 0x02000000, 353 | /// 354 | /// The section cannot be cached. 355 | /// 356 | MemoryNotCached = 0x04000000, 357 | /// 358 | /// The section is not pageable. 359 | /// 360 | MemoryNotPaged = 0x08000000, 361 | /// 362 | /// The section can be shared in memory. 363 | /// 364 | MemoryShared = 0x10000000, 365 | /// 366 | /// The section can be executed as code. 367 | /// 368 | MemoryExecute = 0x20000000, 369 | /// 370 | /// The section can be read. 371 | /// 372 | MemoryRead = 0x40000000, 373 | /// 374 | /// The section can be written to. 375 | /// 376 | MemoryWrite = 0x80000000 377 | } 378 | 379 | #endregion File Header Structures 380 | 381 | #region Private Fields 382 | 383 | /// 384 | /// The DOS header 385 | /// 386 | private IMAGE_DOS_HEADER dosHeader; 387 | /// 388 | /// The file header 389 | /// 390 | private IMAGE_FILE_HEADER fileHeader; 391 | /// 392 | /// Optional 32 bit file header 393 | /// 394 | private IMAGE_OPTIONAL_HEADER32 optionalHeader32; 395 | /// 396 | /// Optional 64 bit file header 397 | /// 398 | private IMAGE_OPTIONAL_HEADER64 optionalHeader64; 399 | /// 400 | /// Image Section headers. Number of sections is in the file header. 401 | /// 402 | private IMAGE_SECTION_HEADER[] imageSectionHeaders; 403 | public byte[] allBytes; 404 | 405 | #endregion Private Fields 406 | 407 | #region Public Methods 408 | 409 | public PeHeaderReader(string filePath) 410 | { 411 | // Read in the DLL or EXE and get the timestamp 412 | using (FileStream stream = new FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) 413 | { 414 | BinaryReader reader = new BinaryReader(stream); 415 | dosHeader = FromBinaryReader(reader); 416 | 417 | // Add 4 bytes to the offset 418 | stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin); 419 | 420 | UInt32 ntHeadersSignature = reader.ReadUInt32(); 421 | fileHeader = FromBinaryReader(reader); 422 | if (this.Is32BitHeader) 423 | { 424 | optionalHeader32 = FromBinaryReader(reader); 425 | } 426 | else 427 | { 428 | optionalHeader64 = FromBinaryReader(reader); 429 | } 430 | 431 | imageSectionHeaders = new IMAGE_SECTION_HEADER[fileHeader.NumberOfSections]; 432 | for (int headerNo = 0; headerNo < imageSectionHeaders.Length; ++headerNo) 433 | { 434 | imageSectionHeaders[headerNo] = FromBinaryReader(reader); 435 | } 436 | } 437 | allBytes = File.ReadAllBytes(filePath); 438 | } 439 | 440 | 441 | public PeHeaderReader(byte[] fileBytes) 442 | { 443 | // Read in the DLL or EXE and get the timestamp 444 | using (MemoryStream stream = new MemoryStream(fileBytes, 0, fileBytes.Length)) 445 | { 446 | BinaryReader reader = new BinaryReader(stream); 447 | dosHeader = FromBinaryReader(reader); 448 | 449 | // Add 4 bytes to the offset 450 | stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin); 451 | 452 | UInt32 ntHeadersSignature = reader.ReadUInt32(); 453 | fileHeader = FromBinaryReader(reader); 454 | if (this.Is32BitHeader) 455 | { 456 | optionalHeader32 = FromBinaryReader(reader); 457 | } 458 | else 459 | { 460 | optionalHeader64 = FromBinaryReader(reader); 461 | } 462 | 463 | imageSectionHeaders = new IMAGE_SECTION_HEADER[fileHeader.NumberOfSections]; 464 | for (int headerNo = 0; headerNo < imageSectionHeaders.Length; ++headerNo) 465 | { 466 | imageSectionHeaders[headerNo] = FromBinaryReader(reader); 467 | } 468 | } 469 | 470 | } 471 | 472 | /// 473 | /// Gets the header of the .NET assembly that called this function 474 | /// 475 | /// 476 | public static PeHeaderReader GetCallingAssemblyHeader() 477 | { 478 | // Get the path to the calling assembly, which is the path to the 479 | // DLL or EXE that we want the time of 480 | string filePath = System.Reflection.Assembly.GetCallingAssembly().Location; 481 | 482 | // Get and return the timestamp 483 | return new PeHeaderReader(filePath); 484 | } 485 | 486 | /// 487 | /// Gets the header of the .NET assembly that called this function 488 | /// 489 | /// 490 | public static PeHeaderReader GetAssemblyHeader() 491 | { 492 | // Get the path to the calling assembly, which is the path to the 493 | // DLL or EXE that we want the time of 494 | string filePath = System.Reflection.Assembly.GetAssembly(typeof(PeHeaderReader)).Location; 495 | 496 | // Get and return the timestamp 497 | return new PeHeaderReader(filePath); 498 | } 499 | 500 | /// 501 | /// Reads in a block from a file and converts it to the struct 502 | /// type specified by the template parameter 503 | /// 504 | /// 505 | /// 506 | /// 507 | public static T FromBinaryReader(BinaryReader reader) 508 | { 509 | // Read in a byte array 510 | byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T))); 511 | 512 | // Pin the managed memory while, copy it out the data, then unpin it 513 | GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); 514 | T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 515 | handle.Free(); 516 | 517 | return theStructure; 518 | } 519 | 520 | #endregion Public Methods 521 | 522 | #region Properties 523 | 524 | /// 525 | /// Gets if the file header is 32 bit or not 526 | /// 527 | public bool Is32BitHeader 528 | { 529 | get 530 | { 531 | UInt16 IMAGE_FILE_32BIT_MACHINE = 0x0100; 532 | return (IMAGE_FILE_32BIT_MACHINE & FileHeader.Characteristics) == IMAGE_FILE_32BIT_MACHINE; 533 | } 534 | } 535 | 536 | /// 537 | /// Gets the file header 538 | /// 539 | public IMAGE_FILE_HEADER FileHeader 540 | { 541 | get 542 | { 543 | return fileHeader; 544 | } 545 | } 546 | 547 | /// 548 | /// Gets the optional header 549 | /// 550 | public IMAGE_OPTIONAL_HEADER32 OptionalHeader32 551 | { 552 | get 553 | { 554 | return optionalHeader32; 555 | } 556 | } 557 | 558 | /// 559 | /// Gets the optional header 560 | /// 561 | public IMAGE_OPTIONAL_HEADER64 OptionalHeader64 562 | { 563 | get 564 | { 565 | return optionalHeader64; 566 | } 567 | } 568 | 569 | public IMAGE_SECTION_HEADER[] ImageSectionHeaders 570 | { 571 | get 572 | { 573 | return imageSectionHeaders; 574 | } 575 | } 576 | 577 | /// 578 | /// Gets the timestamp from the file header 579 | /// 580 | public DateTime TimeStamp 581 | { 582 | get 583 | { 584 | // Timestamp is a date offset from 1970 585 | DateTime returnValue = new DateTime(1970, 1, 1, 0, 0, 0); 586 | 587 | // Add in the number of seconds since 1970/1/1 588 | returnValue = returnValue.AddSeconds(fileHeader.TimeDateStamp); 589 | // Adjust to local timezone 590 | returnValue += TimeZone.CurrentTimeZone.GetUtcOffset(returnValue); 591 | 592 | return returnValue; 593 | } 594 | } 595 | 596 | #endregion Properties 597 | } 598 | } 599 | -------------------------------------------------------------------------------- /detection/Program.cs: -------------------------------------------------------------------------------- 1 | //Author : Aden Chung Wee jing 2 | //Email : weejing789@gmail.com 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Management; 8 | using System.Collections; 9 | using System.Threading; 10 | using System.Collections.Generic; 11 | using System.Security.Cryptography; 12 | using System.IO; 13 | 14 | namespace detection 15 | { 16 | 17 | class catchAmsiBypass 18 | { 19 | static LinkedList listOfProcessId; 20 | static Semaphore semaphore; 21 | 22 | [StructLayout(LayoutKind.Sequential)] 23 | public struct MODULEINFO 24 | { 25 | public IntPtr lpBaseOfDll; 26 | public uint SizeOfImage; 27 | public IntPtr EntryPoint; 28 | } 29 | 30 | 31 | [DllImport("psapi.dll", SetLastError = true)] 32 | public static extern bool EnumProcessModules( 33 | IntPtr hProcess, 34 | [Out] IntPtr lphModule, 35 | UInt32 cb, 36 | [MarshalAs(UnmanagedType.U4)] out UInt32 lpcbNeeded); 37 | 38 | [DllImport("kernel32.dll")] 39 | public static extern IntPtr OpenProcess( 40 | int dwDesiredAccess, 41 | bool bInheritHandle, 42 | int dwProcessId); 43 | 44 | [DllImport("psapi.dll")] 45 | static extern uint GetModuleFileNameEx( 46 | IntPtr hProcess, 47 | IntPtr hModule, 48 | [Out] StringBuilder lpBaseName, 49 | [In] [MarshalAs(UnmanagedType.U4)] int nSize); 50 | 51 | 52 | [DllImport("kernel32.dll")] 53 | public static extern bool ReadProcessMemory( 54 | IntPtr hProcess, 55 | IntPtr lpBaseAddress, 56 | byte[] lpBuffer, 57 | int dwSize, 58 | ref int lpNumberOfBytesRead); 59 | 60 | [DllImport("psapi.dll", SetLastError = true)] 61 | public static extern bool GetModuleInformation( 62 | IntPtr hProcess, 63 | IntPtr hModule, 64 | out MODULEINFO lpmodinfo, 65 | uint cb); 66 | 67 | 68 | static void Main(string[] args) 69 | { 70 | 71 | PeHeaderReader amsiReader = new PeHeaderReader("C:/Windows/System32/amsi.dll"); 72 | byte[] amsiModule = amsiReader.allBytes; 73 | String onDiskAmsiCodehash = getSectionHeaderofAmsi(amsiReader, false, amsiModule); 74 | 75 | 76 | ManagementEventWatcher eventWatcher = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace")); 77 | eventWatcher.EventArrived += new EventArrivedEventHandler(newEvent); 78 | 79 | listOfProcessId = new LinkedList(); 80 | semaphore = new Semaphore(1, 1); 81 | eventWatcher.Start(); 82 | 83 | while (true) 84 | { 85 | Thread.Sleep(5000); 86 | semaphore.WaitOne(); 87 | LinkedListNode headPointer = listOfProcessId.First; 88 | 89 | if (headPointer == null) 90 | Console.WriteLine("Empty"); 91 | 92 | while(headPointer != null) 93 | { 94 | LinkedListNode nextPointer = headPointer.Next; 95 | checkForBypassEvidence(headPointer, onDiskAmsiCodehash); 96 | headPointer = nextPointer; 97 | } 98 | semaphore.Release(); 99 | } 100 | 101 | } 102 | 103 | // this method returns the hash of Amsi.dll code section. 104 | static String getSectionHeaderofAmsi(PeHeaderReader amsiReader, Boolean inMemory, byte[] amsiModule) 105 | { 106 | PeHeaderReader.IMAGE_SECTION_HEADER[] amsiSection = amsiReader.ImageSectionHeaders; 107 | int codeSectionPointer; 108 | for (int count = 0; count < amsiSection.Length; count++ ) 109 | { 110 | char[] sectionName = amsiSection[count].Name; 111 | if (sectionName[0] =='.' && sectionName[1] =='t' && sectionName[2] =='e' && sectionName[3] == 'x' && sectionName[4] =='t') 112 | { 113 | if (inMemory) 114 | codeSectionPointer = (int)amsiSection[count].VirtualAddress; 115 | else 116 | codeSectionPointer = (int)amsiSection[count].PointerToRawData; 117 | 118 | int SizeOfRawData = (int)amsiSection[count].SizeOfRawData; 119 | byte[] amsiCodeSection = new byte[SizeOfRawData]; 120 | Array.Copy(amsiModule, codeSectionPointer, amsiCodeSection, 0, SizeOfRawData); 121 | return calculateHash(amsiCodeSection); 122 | } 123 | } 124 | return "error"; 125 | } 126 | 127 | //this method returns the md5 hash of a file 128 | static String calculateHash(byte [] bytesToHash) 129 | { 130 | MD5 md5CheckSum = MD5.Create(); 131 | var hash = md5CheckSum.ComputeHash(bytesToHash); 132 | return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); 133 | } 134 | 135 | 136 | 137 | // this method checks for the creation of new PowerShell events 138 | static void newEvent(object sender, EventArrivedEventArgs e) 139 | { 140 | String processName = e.NewEvent.Properties["ProcessName"].Value.ToString(); 141 | bool match = string.Equals(processName, "powershell.exe"); 142 | if (match == true) 143 | { 144 | int processId = Int32.Parse(e.NewEvent.Properties["ProcessID"].Value.ToString()); 145 | semaphore.WaitOne(); 146 | listOfProcessId.AddFirst(processId); 147 | semaphore.Release(); 148 | } 149 | 150 | } 151 | 152 | // this method sets a handle on an identified process 153 | static void checkForBypassEvidence(LinkedListNode processId, string onDiskAmsiCodehash) 154 | { 155 | int PROCESS_VM_READ = (0x0010); 156 | int PROCESS_QUERY_INFORMATION = (0x0400); 157 | Console.WriteLine("Analysing process id: " + processId.Value); 158 | 159 | IntPtr processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, 160 | true, processId.Value); 161 | 162 | if (processHandle != null) 163 | { 164 | Boolean bypassFound = analyseAmsiDLL(processHandle, onDiskAmsiCodehash); 165 | if(bypassFound == true) 166 | { 167 | Console.WriteLine("Amsi tampering found in " + processId.Value); 168 | listOfProcessId.Remove(processId); 169 | } 170 | } 171 | else 172 | { 173 | listOfProcessId.Remove(processId); 174 | } 175 | } 176 | 177 | // this method sets a handle on the amsi DLL 178 | static Boolean analyseAmsiDLL(IntPtr processHandle, String onDiskAmsiCodehash) 179 | { 180 | IntPtr[] listOfModules = new IntPtr[1024]; 181 | GCHandle gch = GCHandle.Alloc(listOfModules, GCHandleType.Pinned); 182 | IntPtr modulePointer = gch.AddrOfPinnedObject(); 183 | 184 | uint uiSize = (uint)(Marshal.SizeOf(typeof(IntPtr)) * (listOfModules.Length)); 185 | uint cbNeeded = 0; 186 | 187 | if (EnumProcessModules(processHandle, modulePointer, uiSize, out cbNeeded)) 188 | { 189 | int numOfModules = (Int32)(cbNeeded / (Marshal.SizeOf(typeof(IntPtr)))); 190 | for (int count = 0; count <= numOfModules; count++) 191 | { 192 | StringBuilder moduleName = new StringBuilder(1024); 193 | GetModuleFileNameEx(processHandle, listOfModules[count], moduleName, (int)(moduleName.Capacity)); 194 | if (moduleName.ToString().Contains("amsi.dll")) 195 | { 196 | Boolean amsiIntegrityCheck = AmsiIntegrityCheck(processHandle, listOfModules[count], onDiskAmsiCodehash); 197 | //Boolean bypassFound = checkAmsiScanBufferBypass(processHandle, listOfModules[count]); 198 | gch.Free(); 199 | return amsiIntegrityCheck; 200 | } 201 | } 202 | } 203 | gch.Free(); 204 | return false; 205 | } 206 | 207 | // this method checks if any section of AmsiDll is tampered with by comparing the hash 208 | static Boolean AmsiIntegrityCheck(IntPtr processHandle, IntPtr amsiModuleHandle, String onDiskAmsiCodehash) 209 | { 210 | MODULEINFO amsiDLLInfo = new MODULEINFO(); 211 | GetModuleInformation(processHandle, amsiModuleHandle, out amsiDLLInfo, (uint)(Marshal.SizeOf(typeof(MODULEINFO)))); 212 | byte[] inMemoryAmsiDLL = new byte[amsiDLLInfo.SizeOfImage]; 213 | int bytesRead = 0; 214 | 215 | ReadProcessMemory(processHandle, amsiModuleHandle, inMemoryAmsiDLL, inMemoryAmsiDLL.Length, ref bytesRead); 216 | PeHeaderReader amsiReader = new PeHeaderReader(inMemoryAmsiDLL); 217 | 218 | String inMemoryAmsiCodehash = getSectionHeaderofAmsi(amsiReader, true, inMemoryAmsiDLL); 219 | 220 | if (inMemoryAmsiCodehash.Equals(onDiskAmsiCodehash)) 221 | { 222 | Console.WriteLine("hash matches"); 223 | return false; 224 | } 225 | else 226 | { 227 | Console.WriteLine("Hash does not match"); 228 | return true; 229 | } 230 | } 231 | 232 | // this method check if the AmsiScanBuffer is tampered with 233 | static Boolean checkAmsiScanBufferBypass(IntPtr processHandle, IntPtr amsiModuleHandle) 234 | { 235 | byte[] amsiBuffer = new byte[3]; 236 | int bytesRead = 0; 237 | ReadProcessMemory(processHandle, (amsiModuleHandle + 9248 + 27), amsiBuffer, amsiBuffer.Length, ref bytesRead); 238 | if (amsiBuffer[0] == 49 && amsiBuffer[1] == 255 && amsiBuffer[2] == 144 ) 239 | { 240 | return true; 241 | } 242 | if (amsiBuffer[0] !=65 && amsiBuffer[1] !=139 && amsiBuffer[2] !=248) 243 | { 244 | return true; 245 | } 246 | return false; 247 | } 248 | } 249 | } 250 | 251 | 252 | -------------------------------------------------------------------------------- /detection/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("detection")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("detection")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4c736f07-fb70-453a-9d8e-acb036b4038d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /detection/detection.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4C736F07-FB70-453A-9D8E-ACB036B4038D} 8 | Exe 9 | detection 10 | detection 11 | v4.6.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | bin\x64\Debug\ 39 | DEBUG;TRACE 40 | full 41 | x64 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | true 45 | 46 | 47 | bin\x64\Release\ 48 | TRACE 49 | true 50 | pdbonly 51 | x64 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | true 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | --------------------------------------------------------------------------------