using System; using System.Drawing; using System.IO; using System.Text; using System.Windows.Forms; using Microsoft.Win32; namespace WindowsKeyInfo { internal static class Program { [STAThread] private static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } } /// 레지스트리에서 윈도우 키 정보를 읽어오는 클래스 (원본 VBScript 로직 이식) internal static class KeyReader { private const string CurrentVersionPath = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; private const string ComputerNamePath = @"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName"; private const string NotFound = "없음"; public sealed class KeyInfo { public string ComputerName; public string ProductName; public string ProductId; public string InstalledKey; public string BackupKey; } public static KeyInfo Read() { var info = new KeyInfo(); // 64비트 윈도우에서는 반드시 64비트 레지스트리 뷰로 읽어야 DigitalProductId가 정상적으로 보임 RegistryView view = Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Default; using (RegistryKey hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view)) { using (RegistryKey cv = hklm.OpenSubKey(CurrentVersionPath)) { info.ProductName = GetString(cv, "ProductName"); info.ProductId = GetString(cv, "ProductId"); byte[] digitalId = cv == null ? null : cv.GetValue("DigitalProductId") as byte[]; info.InstalledKey = ConvertToKey(digitalId); } using (RegistryKey spp = hklm.OpenSubKey(CurrentVersionPath + @"\SoftwareProtectionPlatform")) { info.BackupKey = GetString(spp, "BackupProductKeyDefault"); } using (RegistryKey cn = hklm.OpenSubKey(ComputerNamePath)) { string name = GetString(cn, "ComputerName"); info.ComputerName = name == NotFound ? Environment.MachineName : name; } } return info; } private static string GetString(RegistryKey key, string valueName) { if (key == null) return NotFound; object value = key.GetValue(valueName); string text = value == null ? null : value.ToString(); return string.IsNullOrWhiteSpace(text) ? NotFound : text; } /// DigitalProductId(바이너리) → 25자리 제품 키 변환 public static string ConvertToKey(byte[] digitalId) { const int keyOffset = 52; const string maps = "BCDFGHJKMPQRTVWXY2346789"; if (digitalId == null || digitalId.Length < keyOffset + 15) return NotFound; byte[] key = (byte[])digitalId.Clone(); // 원본 배열 보존 // Windows 8 이상 형식인지 확인 int isWin8 = (key[66] / 6) & 1; key[66] = (byte)((key[66] & 0xF7) | ((isWin8 & 2) * 4)); string keyOutput = string.Empty; int last = 0; for (int i = 24; i >= 0; i--) { int current = 0; for (int j = 14; j >= 0; j--) { current = current * 256; current = key[j + keyOffset] + current; key[j + keyOffset] = (byte)(current / 24); current = current % 24; } keyOutput = maps[current] + keyOutput; last = current; } // Windows 8 이상 키는 'N' 문자 위치를 보정 if (isWin8 == 1) { string keypart1 = keyOutput.Substring(1, last); string keypart2 = keyOutput.Substring(last + 1); keyOutput = keypart1 + "N" + keypart2; } return keyOutput.Substring(0, 5) + "-" + keyOutput.Substring(5, 5) + "-" + keyOutput.Substring(10, 5) + "-" + keyOutput.Substring(15, 5) + "-" + keyOutput.Substring(20, 5); } } /// 메인 창 (WinForms) internal sealed class MainForm : Form { private readonly KeyReader.KeyInfo _info; private readonly TextBox _txtProductName; private readonly TextBox _txtProductId; private readonly TextBox _txtInstalledKey; private readonly TextBox _txtBackupKey; public MainForm() { Text = "Windows 제품 키 백업"; FormBorderStyle = FormBorderStyle.FixedDialog; MaximizeBox = false; StartPosition = FormStartPosition.CenterScreen; AutoScaleMode = AutoScaleMode.Dpi; Font = new Font("맑은 고딕", 9.5f); ClientSize = new Size(560, 344); var table = new TableLayoutPanel { Dock = DockStyle.Top, Height = 180, Padding = new Padding(14, 14, 14, 0), ColumnCount = 2, RowCount = 4 }; table.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 130)); table.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); for (int r = 0; r < 4; r++) table.RowStyles.Add(new RowStyle(SizeType.Percent, 25)); _txtProductName = AddRow(table, 0, "제품 이름", false); _txtProductId = AddRow(table, 1, "제품 ID", false); _txtInstalledKey = AddRow(table, 2, "설치된 키", true); _txtBackupKey = AddRow(table, 3, "백업 키", true); var buttons = new FlowLayoutPanel { Dock = DockStyle.Bottom, FlowDirection = FlowDirection.RightToLeft, Height = 56, Padding = new Padding(10, 10, 10, 10) }; var btnClose = MakeButton("닫기"); btnClose.Click += (s, e) => Close(); var btnSave = MakeButton("파일로 저장"); btnSave.Click += (s, e) => SaveToFile(); var btnCopy = MakeButton("복사"); btnCopy.Click += (s, e) => CopyToClipboard(); buttons.Controls.Add(btnClose); buttons.Controls.Add(btnSave); buttons.Controls.Add(btnCopy); // 화면 하단 바로가기 버튼 (기본 브라우저로 열기) var links = new FlowLayoutPanel { Dock = DockStyle.Bottom, FlowDirection = FlowDirection.LeftToRight, Height = 50, Padding = new Padding(14, 4, 10, 10) }; var lblLinks = new Label { Text = "바로가기:", AutoSize = true, Margin = new Padding(0, 9, 6, 0) }; var btnGetCidMe = MakeLinkButton("GETCID.ME", "https://getcid.me/"); var btnGetCidInfo = MakeLinkButton("GETCID.INFO", "https://getcid.info/"); var btnGetCidUs = MakeLinkButton("GETCID.US", "https://getcid.us/"); links.Controls.Add(lblLinks); links.Controls.Add(btnGetCidMe); links.Controls.Add(btnGetCidInfo); links.Controls.Add(btnGetCidUs); // 전화 인증 (slmgr 명령어 방식 — slui 4 창이 막혀 있어도 동작) var phone = new FlowLayoutPanel { Dock = DockStyle.Bottom, FlowDirection = FlowDirection.LeftToRight, Height = 44, Padding = new Padding(14, 4, 10, 0) }; var lblPhone = new Label { Text = "전화 인증:", AutoSize = true, Margin = new Padding(0, 9, 6, 0) }; var btnShowIid = MakeActionButton("① 설치 ID 보기"); btnShowIid.Click += (s, e) => ShowInstallationId(); var btnEnterCid = MakeActionButton("② 확인 ID 입력"); btnEnterCid.Click += (s, e) => EnterConfirmationId(); phone.Controls.Add(lblPhone); phone.Controls.Add(btnShowIid); phone.Controls.Add(btnEnterCid); var tip = new ToolTip(); tip.SetToolTip(btnShowIid, "설치 ID(Installation ID)를 확인합니다 (slmgr /dti)"); tip.SetToolTip(btnEnterCid, "받은 확인 ID(Confirmation ID)를 입력해 인증합니다 (slmgr /atp, 관리자 권한 필요)"); tip.SetToolTip(btnGetCidMe, "https://getcid.me/"); tip.SetToolTip(btnGetCidInfo, "https://getcid.info/"); tip.SetToolTip(btnGetCidUs, "https://getcid.us/"); // Dock=Bottom 은 나중에 추가한 것이 더 아래에 배치됨 Controls.Add(table); Controls.Add(buttons); Controls.Add(phone); Controls.Add(links); CancelButton = btnClose; try { _info = KeyReader.Read(); } catch (Exception ex) { MessageBox.Show("레지스트리에서 정보를 읽지 못했습니다.\n\n" + ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error); _info = new KeyReader.KeyInfo { ComputerName = Environment.MachineName, ProductName = "없음", ProductId = "없음", InstalledKey = "없음", BackupKey = "없음" }; } _txtProductName.Text = _info.ProductName; _txtProductId.Text = _info.ProductId; _txtInstalledKey.Text = _info.InstalledKey; _txtBackupKey.Text = _info.BackupKey; } private static TextBox AddRow(TableLayoutPanel table, int row, string caption, bool monospace) { var label = new Label { Text = caption, Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft }; var box = new TextBox { ReadOnly = true, Dock = DockStyle.Fill, Anchor = AnchorStyles.Left | AnchorStyles.Right, BackColor = SystemColors.Window }; if (monospace) box.Font = new Font("Consolas", 11f, FontStyle.Bold); table.Controls.Add(label, 0, row); table.Controls.Add(box, 1, row); return box; } private static Button MakeButton(string text) { return new Button { Text = text, Width = 110, Height = 32, Margin = new Padding(6, 0, 0, 0) }; } private Button MakeLinkButton(string text, string url) { var btn = new Button { Text = text, Width = 130, Height = 32, Margin = new Padding(0, 0, 8, 0), Cursor = Cursors.Hand }; btn.Click += (s, e) => OpenUrl(url); return btn; } private void OpenUrl(string url) { try { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(url) { UseShellExecute = true }); } catch (Exception ex) { MessageBox.Show("브라우저를 열지 못했습니다.\n\n" + url + "\n\n" + ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private static Button MakeActionButton(string text) { return new Button { Text = text, Width = 140, Height = 32, Margin = new Padding(0, 0, 8, 0), Cursor = Cursors.Hand }; } /// System32 경로 (32비트 프로세스면 Sysnative 사용) private static string SystemDir() { string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); string sysnative = Path.Combine(windir, "Sysnative"); if (!Environment.Is64BitProcess && Directory.Exists(sysnative)) return sysnative; return Path.Combine(windir, "System32"); } /// ① 설치 ID 보기: cscript slmgr.vbs /dti 실행 후 결과 표시 private void ShowInstallationId() { string sys = SystemDir(); string cscript = Path.Combine(sys, "cscript.exe"); string slmgr = Path.Combine(sys, "slmgr.vbs"); string output; Cursor = Cursors.WaitCursor; try { var psi = new System.Diagnostics.ProcessStartInfo(cscript, "//nologo \"" + slmgr + "\" /dti") { UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, StandardOutputEncoding = Encoding.GetEncoding( System.Globalization.CultureInfo.CurrentCulture.TextInfo.OEMCodePage) }; using (var p = System.Diagnostics.Process.Start(psi)) { output = p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd(); p.WaitForExit(60000); } } catch (Exception ex) { Cursor = Cursors.Default; MessageBox.Show("설치 ID를 가져오지 못했습니다.\n\n" + ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } Cursor = Cursors.Default; // 출력에서 가장 긴 숫자열(설치 ID, 보통 63자리)을 추출 string iid = null; foreach (System.Text.RegularExpressions.Match m in System.Text.RegularExpressions.Regex.Matches(output, @"\d{40,}")) { if (iid == null || m.Value.Length > iid.Length) iid = m.Value; } if (iid == null) { MessageBox.Show("설치 ID를 찾지 못했습니다. 윈도우가 보낸 메시지:\n\n" + output.Trim(), Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } using (var dlg = new InstallationIdDialog(iid)) { dlg.ShowDialog(this); } } /// ② 확인 ID 입력: 관리자 권한으로 slmgr.vbs /atp 확인ID 실행 private void EnterConfirmationId() { string cid; using (var dlg = new ConfirmationIdDialog()) { if (dlg.ShowDialog(this) != DialogResult.OK) return; cid = dlg.ConfirmationId; } string sys = SystemDir(); string wscript = Path.Combine(sys, "wscript.exe"); string slmgr = Path.Combine(sys, "slmgr.vbs"); try { // wscript로 실행하면 slmgr가 결과(성공/오류)를 자체 메시지 창으로 보여 줌 var psi = new System.Diagnostics.ProcessStartInfo(wscript, "\"" + slmgr + "\" /atp " + cid) { UseShellExecute = true, Verb = "runas" // 관리자 권한 요청 (UAC 창) }; System.Diagnostics.Process.Start(psi); } catch (System.ComponentModel.Win32Exception ex) when (ex.NativeErrorCode == 1223) { MessageBox.Show("관리자 권한 요청이 취소되었습니다.\n확인 ID 입력에는 관리자 권한이 필요합니다.", Text, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show("확인 ID 입력을 실행하지 못했습니다.\n\n" + ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// 원본 스크립트와 같은 형식의 텍스트 private string BuildText() { var sb = new StringBuilder(); sb.AppendLine("Product Name: " + _info.ProductName); sb.AppendLine("Product ID: " + _info.ProductId); sb.AppendLine("Orginal Installed Key: " + _info.InstalledKey); sb.AppendLine("Backup Installed Key: " + _info.BackupKey); return sb.ToString(); } private void CopyToClipboard() { try { Clipboard.SetText(BuildText()); MessageBox.Show("키 정보를 클립보드에 복사했습니다.", Text, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show("클립보드 복사에 실패했습니다.\n\n" + ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// exe가 있는 폴더에 WindowsKeyInfo_컴퓨터이름_사용자이름.txt 로 저장 private void SaveToFile() { string computer = (_info.ComputerName ?? Environment.MachineName).Replace(" ", ""); string user = Environment.UserName; string fileName = "WindowsKeyInfo_" + computer + "_" + user + ".txt"; string folder = AppDomain.CurrentDomain.BaseDirectory; string fullPath = Path.Combine(folder, fileName); try { File.WriteAllText(fullPath, BuildText(), new UTF8Encoding(true)); MessageBox.Show("저장했습니다.\n\n" + fullPath, Text, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (UnauthorizedAccessException) { MessageBox.Show("이 폴더에 쓸 권한이 없습니다.\n\n" + folder + "\n\nexe를 바탕화면이나 문서 폴더 등 쓰기 가능한 곳으로 옮긴 뒤 다시 시도해 주세요.", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); } catch (Exception ex) { MessageBox.Show("파일 저장에 실패했습니다.\n\n" + ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } } /// 설치 ID 표시 창 (7자리씩 9묶음) internal sealed class InstallationIdDialog : Form { public InstallationIdDialog(string iid) { Text = "설치 ID (Installation ID)"; FormBorderStyle = FormBorderStyle.FixedDialog; MaximizeBox = false; MinimizeBox = false; ShowInTaskbar = false; StartPosition = FormStartPosition.CenterParent; AutoScaleMode = AutoScaleMode.Dpi; Font = new Font("맑은 고딕", 9.5f); ClientSize = new Size(620, 190); var sbGrouped = new StringBuilder(); for (int i = 0; i < iid.Length; i += 7) { if (i > 0) sbGrouped.Append(' '); sbGrouped.Append(iid.Substring(i, Math.Min(7, iid.Length - i))); } string grouped = sbGrouped.ToString(); var lbl = new Label { Text = "아래 설치 ID를 전화 자동 응답에 입력하거나 확인 ID 발급처에 전달하세요.\n" + "받은 확인 ID는 메인 창의 [② 확인 ID 입력]에 넣으면 됩니다.", Location = new Point(14, 12), Size = new Size(592, 42) }; var box = new TextBox { Text = grouped, ReadOnly = true, Multiline = true, Location = new Point(14, 60), Size = new Size(592, 64), Font = new Font("Consolas", 12f, FontStyle.Bold), BackColor = SystemColors.Window }; var btnCopy = new Button { Text = "복사 (숫자만)", Location = new Point(366, 140), Size = new Size(120, 32) }; btnCopy.Click += (s, e) => { try { Clipboard.SetText(iid); MessageBox.Show("설치 ID를 클립보드에 복사했습니다.", Text, MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show("복사에 실패했습니다.\n\n" + ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } }; var btnClose = new Button { Text = "닫기", Location = new Point(494, 140), Size = new Size(112, 32), DialogResult = DialogResult.OK }; Controls.Add(lbl); Controls.Add(box); Controls.Add(btnCopy); Controls.Add(btnClose); AcceptButton = btnClose; CancelButton = btnClose; } } /// 확인 ID 입력 창 (48자리 숫자, 하이픈/공백 허용) internal sealed class ConfirmationIdDialog : Form { private readonly TextBox _box; public string ConfirmationId { get; private set; } public ConfirmationIdDialog() { Text = "확인 ID (Confirmation ID) 입력"; FormBorderStyle = FormBorderStyle.FixedDialog; MaximizeBox = false; MinimizeBox = false; ShowInTaskbar = false; StartPosition = FormStartPosition.CenterParent; AutoScaleMode = AutoScaleMode.Dpi; Font = new Font("맑은 고딕", 9.5f); ClientSize = new Size(620, 170); var lbl = new Label { Text = "받은 확인 ID 48자리를 입력하세요. 하이픈(-)이나 공백은 넣어도 자동으로 제거됩니다.\n" + "[인증]을 누르면 관리자 권한 확인 창이 뜨고, 결과는 윈도우 메시지 창으로 표시됩니다.", Location = new Point(14, 12), Size = new Size(592, 42) }; _box = new TextBox { Location = new Point(14, 62), Size = new Size(592, 30), Font = new Font("Consolas", 12f, FontStyle.Bold) }; var btnOk = new Button { Text = "인증", Location = new Point(366, 120), Size = new Size(120, 32) }; btnOk.Click += (s, e) => TryAccept(); var btnCancel = new Button { Text = "취소", Location = new Point(494, 120), Size = new Size(112, 32), DialogResult = DialogResult.Cancel }; Controls.Add(lbl); Controls.Add(_box); Controls.Add(btnOk); Controls.Add(btnCancel); AcceptButton = btnOk; CancelButton = btnCancel; } private void TryAccept() { var digits = new StringBuilder(); foreach (char c in _box.Text) { if (c >= '0' && c <= '9') digits.Append(c); } if (digits.Length != 48) { MessageBox.Show("확인 ID는 숫자 48자리여야 합니다.\n현재 입력된 숫자: " + digits.Length + "자리", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); _box.Focus(); return; } ConfirmationId = digits.ToString(); DialogResult = DialogResult.OK; Close(); } } }