diff --git a/v2rayN/v2rayN/Base/StringEx.cs b/v2rayN/v2rayN/Base/StringEx.cs
index 1e150a48..98fed4d8 100644
--- a/v2rayN/v2rayN/Base/StringEx.cs
+++ b/v2rayN/v2rayN/Base/StringEx.cs
@@ -46,5 +46,29 @@ namespace v2rayN.Base
         {
             return value == null ? string.Empty : value.Trim();
         }
+
+        public static string RemovePrefix(this string value, char prefix)
+        {
+            if (value.StartsWith(prefix))
+            {
+                return value.Substring(1);
+            }
+            else
+            {
+                return value;
+            }
+        }
+
+        public static string RemovePrefix(this string value, string prefix)
+        {
+            if (value.StartsWith(prefix))
+            {
+                return value.Substring(prefix.Length);
+            }
+            else
+            {
+                return value;
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/v2rayN/v2rayN/Handler/UpdateHandle.cs b/v2rayN/v2rayN/Handler/UpdateHandle.cs
index 7e90e6ca..f9de0b39 100644
--- a/v2rayN/v2rayN/Handler/UpdateHandle.cs
+++ b/v2rayN/v2rayN/Handler/UpdateHandle.cs
@@ -9,6 +9,7 @@ using System.Windows;
 using v2rayN.Base;
 using v2rayN.Mode;
 using v2rayN.Resx;
+using v2rayN.Tool;
 
 namespace v2rayN.Handler
 {
@@ -331,7 +332,7 @@ namespace v2rayN.Handler
         /// 
         /// 获取V2RayCore版本
         /// 
-        private string getCoreVersion(ECoreType type)
+        private SemanticVersion getCoreVersion(ECoreType type)
         {
             try
             {
@@ -352,7 +353,7 @@ namespace v2rayN.Handler
                 {
                     string msg = string.Format(ResUI.NotFoundCore, @"", "", "");
                     //ShowMsg(true, msg);
-                    return "";
+                    return new SemanticVersion("");
                 }
 
                 using Process p = new();
@@ -385,13 +386,13 @@ namespace v2rayN.Handler
                         version = Regex.Match(echo, $"([0-9.]+)").Groups[1].Value;
                         break;
                 }
-                return version;
+                return new SemanticVersion(version);
             }
             catch (Exception ex)
             {
                 Utils.SaveLog(ex.Message, ex);
                 _updateFunc(false, ex.Message);
-                return "";
+                return new SemanticVersion("");
             }
         }
 
@@ -400,18 +401,18 @@ namespace v2rayN.Handler
             try
             {
                 var gitHubReleases = Utils.FromJson>(gitHubReleaseApi);
-                string version;
+                SemanticVersion version;
                 if (preRelease)
                 {
-                    version = gitHubReleases!.First().TagName;
+                    version = new SemanticVersion(gitHubReleases!.First().TagName);
                 }
                 else
                 {
-                    version = gitHubReleases!.First(r => r.Prerelease == false).TagName;
+                    version = new SemanticVersion(gitHubReleases!.First(r => r.Prerelease == false).TagName);
                 }
                 var coreInfo = LazyConfig.Instance.GetCoreInfo(type);
 
-                string curVersion;
+                SemanticVersion curVersion;
                 string message;
                 string url;
                 switch (type)
@@ -421,8 +422,8 @@ namespace v2rayN.Handler
                     case ECoreType.Xray:
                     case ECoreType.v2fly_v5:
                         {
-                            curVersion = "v" + getCoreVersion(type);
-                            message = string.Format(ResUI.IsLatestCore, curVersion);
+                            curVersion = getCoreVersion(type);
+                            message = string.Format(ResUI.IsLatestCore, curVersion.ToVersionString("v"));
                             string osBit = "64";
                             switch (RuntimeInformation.ProcessArchitecture)
                             {
@@ -439,7 +440,7 @@ namespace v2rayN.Handler
                                     break;
                             }
 
-                            url = string.Format(coreInfo.coreDownloadUrl64, version, osBit);
+                            url = string.Format(coreInfo.coreDownloadUrl64, version.ToVersionString("v"), osBit);
                             break;
                         }
                     case ECoreType.clash:
@@ -466,8 +467,8 @@ namespace v2rayN.Handler
                         }
                     case ECoreType.sing_box:
                         {
-                            curVersion = "v" + getCoreVersion(type);
-                            message = string.Format(ResUI.IsLatestCore, curVersion);
+                            curVersion = getCoreVersion(type);
+                            message = string.Format(ResUI.IsLatestCore, curVersion.ToVersionString("v"));
                             switch (RuntimeInformation.ProcessArchitecture)
                             {
                                 case Architecture.Arm64:
@@ -482,12 +483,12 @@ namespace v2rayN.Handler
                                     url = coreInfo.coreDownloadUrl64;
                                     break;
                             }
-                            url = string.Format(url, version, version.Replace("v", ""));
+                            url = string.Format(url, version.ToVersionString("v"), version);
                             break;
                         }
                     case ECoreType.v2rayN:
                         {
-                            curVersion = FileVersionInfo.GetVersionInfo(Utils.GetExePath()).FileVersion.ToString();
+                            curVersion = new SemanticVersion(FileVersionInfo.GetVersionInfo(Utils.GetExePath()).FileVersion.ToString());
                             message = string.Format(ResUI.IsLatestN, curVersion);
                             switch (RuntimeInformation.ProcessArchitecture)
                             {
@@ -511,16 +512,15 @@ namespace v2rayN.Handler
 
                 if (type == ECoreType.v2rayN)
                 {
-                    decimal.TryParse(curVersion, out decimal decCur);
-                    decimal.TryParse(version, out decimal dec);
+                    decimal.TryParse(curVersion.ToString(), out decimal decCur);
+                    decimal.TryParse(version.ToString(), out decimal dec);
                     if (decCur >= dec)
                     {
                         AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message));
                         return;
                     }
                 }
-
-                if (curVersion == version)
+                if (curVersion >= version)
                 {
                     AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message));
                     return;
diff --git a/v2rayN/v2rayN/Tool/SemanticVersion.cs b/v2rayN/v2rayN/Tool/SemanticVersion.cs
new file mode 100644
index 00000000..a79a3fbc
--- /dev/null
+++ b/v2rayN/v2rayN/Tool/SemanticVersion.cs
@@ -0,0 +1,173 @@
+using v2rayN.Base;
+
+namespace v2rayN.Tool
+{
+    public class SemanticVersion
+    {
+        private int major;
+        private int minor;
+        private int patch;
+        private string version;
+
+        public SemanticVersion(int major, int minor, int patch)
+        {
+            this.major = major;
+            this.minor = minor;
+            this.patch = patch;
+            this.version = $"{major}.{minor}.{patch}";
+        }
+
+        public SemanticVersion(string version)
+        {
+            this.version = version.RemovePrefix('v');
+            try
+            {
+                string[] parts = this.version.Split('.');
+                if (parts.Length == 2)
+                {
+                    this.major = int.Parse(parts[0]);
+                    this.minor = int.Parse(parts[1]);
+                    this.patch = 0;
+                }
+                else if (parts.Length == 3)
+                {
+                    this.major = int.Parse(parts[0]);
+                    this.minor = int.Parse(parts[1]);
+                    this.patch = int.Parse(parts[2]);
+                }
+                else
+                {
+                    throw new ArgumentException("Invalid version string");
+                }
+            }
+            catch
+            {
+                this.major = 0;
+                this.minor = 0;
+                this.patch = 0;
+                this.version = "0.0.0";
+            }
+        }
+
+        public override bool Equals(object? obj)
+        {
+            if (obj is SemanticVersion other)
+            {
+                return this.major == other.major && this.minor == other.minor && this.patch == other.patch;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        public override int GetHashCode()
+        {
+            return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode();
+        }
+
+        /// 
+        /// Use ToVersionString(string? prefix) instead if possible.
+        /// 
+        /// major.minor.patch
+        public override string ToString()
+        {
+            return this.version;
+        }
+
+        public string ToVersionString(string? prefix = null)
+        {
+            if (prefix == null)
+            {
+                return this.version;
+            }
+            else
+            {
+                return $"{prefix}{this.version}";
+            }
+        }
+
+        public static bool operator ==(SemanticVersion v1, SemanticVersion v2) { return v1.Equals(v2); }
+        public static bool operator !=(SemanticVersion v1, SemanticVersion v2) { return !v1.Equals(v2); }
+        public static bool operator >=(SemanticVersion v1, SemanticVersion v2) { return v1.GreaterEquals(v2); }
+        public static bool operator <=(SemanticVersion v1, SemanticVersion v2) { return v1.LessEquals(v2); }
+
+        #region Private
+        private bool GreaterEquals(SemanticVersion other)
+        {
+            if (this.major < other.major)
+            {
+                return false;
+            }
+            else if (this.major > other.major)
+            {
+                return true;
+            }
+            else
+            {
+                if (this.minor < other.minor)
+                {
+                    return false;
+                }
+                else if (this.minor > other.minor)
+                {
+                    return true;
+                }
+                else
+                {
+                    if (this.patch < other.patch)
+                    {
+                        return false;
+                    }
+                    else if (this.patch > other.patch)
+                    {
+                        return true;
+                    }
+                    else
+                    {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        private bool LessEquals(SemanticVersion other)
+        {
+            if (this.major < other.major)
+            {
+                return true;
+            }
+            else if (this.major > other.major)
+            {
+                return false;
+            }
+            else
+            {
+                if (this.minor < other.minor)
+                {
+                    return true;
+                }
+                else if (this.minor > other.minor)
+                {
+                    return false;
+                }
+                else
+                {
+                    if (this.patch < other.patch)
+                    {
+                        return true;
+                    }
+                    else if (this.patch > other.patch)
+                    {
+                        return false;
+                    }
+                    else
+                    {
+                        return true;
+                    }
+                }
+            }
+        }
+        #endregion Private
+    }
+}