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();
}
}
}