I have been ask to give a poster session at this years ISC2
Security Congress this year in September in Philadelphia. My
employer has graciously agreed to pay for my expense, plus I will be there for
the whole conference to attend some great classes.
Discussing security with emphasis on privacy, cloud, social media, NIST 800 reports, .Net Security, Secure Coding.
Sunday, August 26, 2012
Sunday, July 22, 2012
Code Obfuscation
Languages like C# and Java can be easily be de-compiled and
now with more web applications using JavaScript we don’t even need to
de-compile the code to see it.
Because of this, code obfuscators are on the rise. One of the newest kids on the block is JScrambler
(https://jscrambler.com/) made by
AuditMark (http://auditmark.com/).
Code obfuscator’s work but do they provide any value? Do
they add any value, for security, protecting our intellectual property, etc.?
Obfuscated code is modified on purpose to make it harder to read
and making it more difficult to analysis what the code really does. A key point here is this goal is a two way
street. It can be for someone trying to
protect their intellectual property or for malware trying to prevent its detection
by anti virus/malware scanners. So our
first point is obfuscated code can be used for and against us.
That brings up an excellent point. If your web site uses
something like jscrambler should I have the right to inspect the JavaScript
used by your web application to make sure it is not doing something evil?
Remember the JavaScript is not running on the server it’s running on your box.
Code obfuscated makers say obfuscation provides the
following benefits to its users…
- Protect Intellectual property (e.g. algorithms) contained in JavaScript.
- Prevent code reutilization.
- Prevent code modification.
- Prevent unauthorized code execution (piracy).
- Add another layer of security, by making it harder for attackers to interfere with your Web Application.
This last point is the most discussed point; does obfuscated
code really provide a benefit as an added layer of security? Remember code
obfuscation is NOT encryption.
Yes, obfuscated code does provide extra security. We know it
can be broken/reverse engineered but the time and effort it takes does provide
a temporarily determent to the causal
hacker. In the long-run it will not protect your code.
Before using code obfuscation I think one of the first
things we need to ask ourselves is “What are we trying to accomplish with code
obfuscation?” with that question in mind here are a few guidelines.
- Never put sensitive information on the client (passwords, 'hidden" URLs, validation routines, encryption routines, etc).
- Understand that anything on a client can be compromised because you no longer have control.
- Any obfuscation of client-side code has to be un-done in order for the framework to process it... thus only providing only a marginal security improvement.
My personal
take on this is JavaScript code obfuscation provides less transparency on the
Web. Open transparency creates better privacy in that everyone can see what everyone
else is doing in an open environment. The temporary time value of security gained
by using obfuscation is not enough to overcome the potential downfall of a less
open transparent environment.
A few other
resources:
https://developers.google.com/closure/compiler/docs/api-tutorial3 Compression provides obfuscation as a
side-effect.
Saturday, June 30, 2012
Multifarious Security June 2012
In this blog post I am going to talk on various interesting
items on security. I am going to try to do this at least once a month on various tidbits
that I hope everyone finds interesting.
- Learn about web tracking and Panopticlick. https://panopticlick.eff.org/
- Security disclosers are still an
issue. We need a Federal mandate to make companies tell employees, customers
and investors if PI data has been breached. I wrote about legislation in a past
blog post that congress is trying to get passed into a law. It doesn’t look
like congress can do it. http://www.huffingtonpost.com/2012/06/29/cybercrime-disclosures-ra_0_n_1637008.html?utm_hp_ref=tw
- Check out http://www.openwall.com/ they have the John
the Ripper project plus other projects. John the Ripper was just upgraded to
use GPU support integrated.
- I have blogged about Microsoft’s
Azure Cloud on security. Microsoft has a series of videos on Azure security.
Everyone should view this series first before diving into the Azure How-to
tutorials. http://blogs.technet.com/b/trustworthycomputing/archive/2012/05/22/cloud-fundamentals-video-series-data-center-security.aspx
- PGP Creator Phil Zimmermann has a
new company. What he did for text and emails he is trying to do for IPhone and
IPad users. https://silentcircle.com/
- Sad, funny, or both? You decide. http://instagram.com/p/MG2hHRMkSO/
- An interesting paper on selling
0-day vulnerabilities. http://securityevaluators.com/files/papers/0daymarket.pdf
And https://www.owasp.org/images/b/b7/OWASP_BeNeLux_Day_2011_-_T._Zoller_-_Rise_of_the_Vulnerability_Market.pdf
A few posts back I talked about how much information a
system is willing to give up. One of the items was the chip set. Knowing the
chip set gives you two distinct advantages. It allows you to date the system.
This may help you to decide which vulnerabilities that can be tried first.
Second it may help you determine what OS is running on the chip. Well now you
have another vulnerability that you can use knowing the chip set. This vulnerability will give you escalated privileges. http://www.kb.cert.org/vuls/id/649219
Saturday, June 23, 2012
.Net Agile Cryptographic Function
A lot has recently been written about the recent LinkedIn hack. I am not going to delve into the details of the hack but I do want to show how you can have an agile cryptographic function for creating hash values. By agile I mean that the code we can use to create our hash value is not hard coded within the program.
MD5 is the weakest hashing algorithm we have in the .Net framework. MD5 might be strong enough when our first application was written but now we want to implement a stronger hashing function. We can re-code our application to use windows default hashing function. SHA-1 is the current default hashing algorithm but SHA-1 still doesn’t provide us with the strongest algorithm we can use. So now our boss has told us about the Linkedin hack and has asked us to use SHA512. Using an agile approach we can say “Ok, I can have that done in about 1 hour” without deploying any code. All we have to do is change our app or machine confg file.
We can accomplish this by using the following lines of code.
Line 1 let's us get our hashing algorithm we are going to use from the config file. If we use the machine config file our implementation would be server wide instead of application specific.
MD5 is the weakest hashing algorithm we have in the .Net framework. MD5 might be strong enough when our first application was written but now we want to implement a stronger hashing function. We can re-code our application to use windows default hashing function. SHA-1 is the current default hashing algorithm but SHA-1 still doesn’t provide us with the strongest algorithm we can use. So now our boss has told us about the Linkedin hack and has asked us to use SHA512. Using an agile approach we can say “Ok, I can have that done in about 1 hour” without deploying any code. All we have to do is change our app or machine confg file.
We can accomplish this by using the following lines of code.
App Code File:
<add key="HashMethod" value="SHA512"/>
C# Code:
1: preferredHash = HashAlgorithm.Create((string)ConfigurationManager.AppSettings["HashMethod"]);
2:
3: hash = computeHash(preferredHash, testString);
4:
5: private string computeHash(HashAlgorithm myHash, string input) {
6: byte[] data;
7: data = myHash.ComputeHash(Encoding.UTF8.GetBytes(input));
8: sb = new StringBuilder();
9: for (int i = 0; i < data.Length; i++) {
10: sb.Append(data[i].ToString("x2"));
11: }
12: return sb.ToString();
13: }
Line 1 let's us get our hashing algorithm we are going to use from the config file. If we use the machine config file our implementation would be server wide instead of application specific.
The
drawback to this method is key size. I would suggest of giving yourself twice
the size of the largest key of hashing algorithm you could possible use to store hash values.
This means we need a varchar of 1024 if we are going to store our hash value in
the database.
I will
leave it up to the reader to take the hash values through Rainbow tables to see
how quickly Raindow tables can return the value of the hash.
Demo Code:
using System; using System.Text; using System.Security.Cryptography; using System.Configuration; using System.Collections; namespace HashDemo { class HashTest { private MD5 md5Hash; private HashAlgorithm cryptoDefaultHash; private HashAlgorithm preferredHash; private Random random; private string testString = string.Empty; private string hash; private StringBuilder sb; private int minSaltSize = 8; private int maxSaltSize = 24; private int saltSize; public HashTest() { testString = "MySup3r#Secur3&Pa()sw0rd*"; // My Super Secure Password md5Hash = MD5.Create(); // Application hard codes Hash Algorithm. Not good must change code to use a newer Crypto Algorithm. cryptoDefaultHash = HashAlgorithm.Create(); // A little better, but uses windows default SHA-1 Crypto Algorithm, but SHA-1 is weak /// preferredHash = HashAlgorithm.Create((string)ConfigurationManager.AppSettings["HashMethod"]); // Best way is to use app or machine config file to set preferred Crypto Algorithm. /// Console.WriteLine("The crypto function is: " + md5Hash.GetType() + "."); Console.WriteLine("The crypto function is: " + cryptoDefaultHash.GetType() + "."); Console.WriteLine("The crypto function is: " + preferredHash.GetType() + "."); random = new Random(); saltSize = random.Next(minSaltSize, maxSaltSize); } public void test() { hash = computeMd5Hash(testString); Console.WriteLine("MD5 - The hash is: " + hash + "."); hash = computeMd5HashWithSalt(testString); Console.WriteLine("MD5 with Salt - The hash is: " + hash + "."); hash = computeDefaultHash(cryptoDefaultHash, testString); Console.WriteLine("Default hash is: " + hash + "."); hash = computeDefaultHashWithSalt(cryptoDefaultHash, testString); Console.WriteLine("Default hash with Salt is: " + hash + "."); hash = computeHash(preferredHash, testString); Console.WriteLine("Preferred hash with Salt is: " + hash + "."); hash = computeHashWithSalt(preferredHash, testString); Console.WriteLine("Preferred hash with Salt is: " + hash + "."); } private string computeMd5Hash(string input) { byte[] data; data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input)); sb = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sb.Append(data[i].ToString("x2")); } return sb.ToString(); } private string computeMd5HashWithSalt(string input) { byte[] data; data = md5Hash.ComputeHash(GetSalt(input)); sb = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sb.Append(data[i].ToString("x2")); } return sb.ToString(); } private string computeDefaultHash(HashAlgorithm myHash, string input) { byte[] data; data = myHash.ComputeHash(Encoding.UTF8.GetBytes(input)); sb = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sb.Append(data[i].ToString("x2")); } return sb.ToString(); } private string computeDefaultHashWithSalt(HashAlgorithm myHash, string input) { byte[] data; data = myHash.ComputeHash(GetSalt(input)); sb = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sb.Append(data[i].ToString("x2")); } return sb.ToString(); } private string computeHash(HashAlgorithm myHash, string input) { byte[] data; data = myHash.ComputeHash(Encoding.UTF8.GetBytes(input)); sb = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sb.Append(data[i].ToString("x2")); } return sb.ToString(); } private string computeHashWithSalt(HashAlgorithm myHash, string input) { byte[] data; data = myHash.ComputeHash(GetSalt(input)); sb = new StringBuilder(); for (int i = 0; i < data.Length; i++) { sb.Append(data[i].ToString("x2")); } return sb.ToString(); } private byte[] GetSalt(string input) { byte[] data; byte[] saltBytes; RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); saltBytes = new byte[saltSize]; rng.GetNonZeroBytes(saltBytes); data = Encoding.UTF8.GetBytes(input); byte[] dataWithSaltBytes = new byte[data.Length + saltBytes.Length]; for (int i = 0; i < data.Length; i++) dataWithSaltBytes[i] = data[i]; for (int i = 0; i < saltBytes.Length; i++) dataWithSaltBytes[data.Length + i] = saltBytes[i]; return dataWithSaltBytes; } ~HashTest() { // dtor preferredHash.Clear(); cryptoDefaultHash.Clear(); md5Hash.Clear(); } } // end of class } // end of namespace
Program Output:
The crypto
function is: System.Security.Cryptography.MD5CryptoServiceProvider.
The crypto
function is: System.Security.Cryptography.SHA1CryptoServiceProvider.
The crypto
function is: System.Security.Cryptography.SHA512Managed.
MD5 - The
hash is: 6c27f9f54f905b815e01298ccef39420.
MD5 with
Salt - The hash is: 60148ef74c3dbce6ab11c5897f110167.
Default hash
is: 636f54fe86c4518308f2599e5f5d8adc72cdc79c.
Default hash
with Salt is: 2b223ced9cb488dfe842f3acc32c192c14f0606b.
Preferred
hash with Salt is: 48ccbc77e2f280e277ebd871af9871b624c915559f677ba5c16
2d9156e70c46aa5fcc539dec4cc51155101c4e638d226746cc4a7b821bbc5501e104fbeee5eb3.
Preferred
hash with Salt is: 6cd578abf2d9b65d621d42fb23e57aede34b401507a4d57d3a7
00d2dbd4f2376b4632a3add3e44c71249e1ed5a4ee293ddfe45efd9e2d0102909d10b74b64483.
Sunday, June 10, 2012
VeraCode Infographic on Web Security
Sunday, June 3, 2012
Obtaining Windows security/audit information
We have good reasons to obtain information from our servers
for security and audit information. Cyber criminals also have good reasons in
needing the same information. Security professionals and code developers also
need to understand what information is easily available for the asking.
Remember we can’t protect our systems unless we know what to protect. We have
to protect our servers, data and applications from unauthorized access. We also
have to protect our assets from users trying to escalate their access beyond what has
been granted to them.
The
following information can be accessed with a simple program. What information
would help a user gain further unauthorized access?
System Environment Information:
COMPUTERNAME - WIN-39OLKRF40NJ
VisualStudioDir -
\\vmware-host\Shared Folders\Documents\Visual Studio 2008
HOMEPATH - \Users\User_Name
LOCALAPPDATA -
C:\Users\User_Name\AppData\Local
PSModulePath -
C:\Windows\system32\WindowsPowerShell\v1.0\Modules\
PROCESSOR_ARCHITECTURE - AMD64
CommonProgramW6432
- C:\Program Files\Common Files
CommonProgramFiles(x86) - C:\Program Files (x86)\Common Files
ProgramFiles(x86) -
C:\Program Files (x86)
PROCESSOR_LEVEL - 6
LOGONSERVER - \\WIN-39OLKRF40NJ
USERNAME - User_Name
HOMEDRIVE - C:
USERPROFILE -
C:\Users\User_Name
SystemRoot -
C:\Windows
TEMP - C:\Users\USERN~1\AppData\Local\Temp
PUBLIC -
C:\Users\Public
ALLUSERSPROFILE -
C:\ProgramData
FP_NO_HOST_CHECK -
NO
APPDATA - C:\Users\User_Name\AppData\Roaming
ProgramData - C:\ProgramData
PATHEXT -
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
OS - Windows_NT
CommonProgramFiles
- C:\Program Files\Common Files
PROCESSOR_IDENTIFIER - Intel64 Family 6 Model 23 Stepping 10, GenuineIntel
ComSpec -
C:\Windows\system32\cmd.exe
SESSIONNAME -
Console
SystemDrive - C:
PROCESSOR_REVISION
- 170a
ProgramFiles -
C:\Program Files
NUMBER_OF_PROCESSORS - 1
VS90COMNTOOLS -
C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools\
WecVersionForRosebud.268 - 2
TMP - C:\Users\USERN~1\AppData\Local\Temp
ProgramW6432 -
C:\Program Files
USERDOMAIN - WIN-39OLKRF40NJ
windir -
C:\WindowsEnvironment.SpecialFolder: C:\Windows\system32
LogicalDrives: A:\,
C:\, D:\, Z:\
System Path:
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
c:\Program Files
(x86)\Microsoft SQL Server\100\Tools\Binn\
c:\Program Files
(x86)\Microsoft SQL Server\100\DTS\Binn\
Chipset information in Win Registry
GenuineIntel running at 2522 MHz.
Oem Id: 9
Page Size: 4096
Minimum Application
Address: 65536
Maximum Application
Address: 0
ProcessorMask:
4294901759
Number Of
Processors: 2047
Processor Type: 1
Allocation
Granularity: 0
Processor
Level: 1
Current Windows
user log in: WIN-39OLKRF40NJ\User_Name
-----------------------------------Win32_UserAccount instance
Caption: WIN-39OLKRF40NJ\Administrator
Description:
Built-in account for administering the computer/domain
Domain: WIN-39OLKRF40NJ
FullName:
LocalAccount: True
Lockout: False
Name: Administrator
PasswordChangeable:
True
PasswordExpires:
False
PasswordRequired:
True
Caption: WIN-39OLKRF40NJ\Guest
Description:
Built-in account for guest access to the computer/domain
Domain: WIN-39OLKRF40NJ
FullName:
LocalAccount: True
Lockout: False
Name: Guest
PasswordChangeable:
False
PasswordExpires:
False
PasswordRequired:
False
Caption: WIN-39OLKRF40NJ\User_Name
Description:
Domain: WIN-39OLKRF40NJ
FullName:
LocalAccount: True
Lockout: False
Name: User_Name
PasswordChangeable:
True
PasswordExpires:
True
PasswordRequired:
True
-----------------------------------ACL Registry Info - Current access
rules:
User: CREATOR OWNER
Type: Allow
Rights: 268435456
Inheritance: ContainerInherit
Propagation:
InheritOnly
Inherited? False
User: NT AUTHORITY\SYSTEM
Type: Allow
Rights: 268435456
Inheritance:
ContainerInherit
Propagation:
InheritOnly
Inherited? False
User: NT AUTHORITY\SYSTEM
Type: Allow
Rights: FullControl
Inheritance: None
Propagation: None
Inherited? False
User: BUILTIN\Administrators
Type: Allow
Rights: 268435456
Inheritance:
ContainerInherit
Propagation:
InheritOnly
Inherited? False
User: BUILTIN\Administrators
Type: Allow
Rights: FullControl
Inheritance: None
Propagation: None
Inherited? False
User: BUILTIN\Users
Type: Allow
Rights: -2147483648
Inheritance:
ContainerInherit
Propagation:
InheritOnly
Inherited? False
User: BUILTIN\Users
Type: Allow
Rights: ReadKey
Inheritance: None
Propagation: None
Inherited? False
using System; using System.Collections; using Microsoft.Win32; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Security.Principal; using System.Management; using System.Security.AccessControl; namespace env { public class SystemEnvironement : System.Windows.Forms.Form { private System.Windows.Forms.TextBox textBox1; WindowsPrincipal wp; String DomainName; [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public uint dwOemId; public uint dwPageSize; public uint lpMinimumApplicationAddress; public uint lpMaximumApplicationAddress; public uint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public uint dwProcessorLevel; public uint dwProcessorRevision; } [DllImport("kernel32")] static extern void GetSystemInfo(ref SYSTEM_INFO pSI); public SystemEnvironement() { this.SuspendLayout(); InitForm(); SystemInformation(); } private void InitForm() { this.textBox1 = new System.Windows.Forms.TextBox(); this.textBox1.Location = new System.Drawing.Point(12, 12); this.textBox1.Multiline = true; this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBox1.Size = new System.Drawing.Size(560, 740); this.textBox1.TabIndex = 1; this.ClientSize = new System.Drawing.Size(584, 757); this.Controls.Add(this.textBox1); } private void SystemInformation() { string[] pathparts; textBox1.Text += "System Environment Information: \r\n"; IDictionary environmentVariables = Environment.GetEnvironmentVariables(); foreach (DictionaryEntry de in environmentVariables) { if (!de.Key.Equals("Path")) { textBox1.Text += "\r\n " + de.Key + " - " + de.Value; } if (de.Key.Equals("USERDOMAIN")) { DomainName = de.Value.ToString(); } } textBox1.Text += "Environment.SpecialFolder: " + Environment.GetFolderPath(Environment.SpecialFolder.System); String[] drives = Environment.GetLogicalDrives(); textBox1.Text += "\r\n LogicalDrives: " + String.Join(", ", drives); textBox1.Text += "\r\n\r\n System Path: \r\n"; pathparts = Environment.GetEnvironmentVariable("PATH").Split(new[] { System.IO.Path.PathSeparator }); int max = pathparts.Length - 1; for (int i = 0; i < max; i++) { textBox1.Text += "\r\n " + pathparts[i]; } RegistryKey RegKey = Registry.LocalMachine; RegKey = RegKey.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"); Object cpuSpeed = RegKey.GetValue("~MHz"); Object cpuType = RegKey.GetValue("VendorIdentifier"); textBox1.Text += "\r\n\r\n Chipset information in Win Registry " + cpuType + " running at " + cpuSpeed + " MHz."; textBox1.Text += "\r\n "; try { SYSTEM_INFO sysInfo = new SYSTEM_INFO(); GetSystemInfo(ref sysInfo); textBox1.Text += "\r\n " + "Oem Id: " + sysInfo.dwOemId; textBox1.Text += "\r\n " + "Page Size: " + sysInfo.dwPageSize; textBox1.Text += "\r\n " + "Minimum Application Address: " + sysInfo.lpMinimumApplicationAddress; textBox1.Text += "\r\n " + "Maximum Application Address: " + sysInfo.lpMaximumApplicationAddress; textBox1.Text += "\r\n " + "ProcessorMask: " + sysInfo.dwActiveProcessorMask; textBox1.Text += "\r\n " + "Number Of Processors: " + sysInfo.dwNumberOfProcessors; textBox1.Text += "\r\n " + "Processor Type: " + sysInfo.dwProcessorType; textBox1.Text += "\r\n " + "Allocation Granularity: " + sysInfo.dwAllocationGranularity; textBox1.Text += "\r\n " + "Processor Level: " + sysInfo.dwProcessorLevel; } catch (Exception e) { Console.WriteLine(e.ToString()); } textBox1.Text += "\r\n "; textBox1.ReadOnly = true; wp = new WindowsPrincipal(WindowsIdentity.GetCurrent()); textBox1.Text += "\r\n " + "Current Windows user log in: " + wp.Identity.Name; textBox1.Text += "\r\n "; ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("root\\CIMV2","SELECT * FROM Win32_UserAccount"); try { textBox1.Text += "-----------------------------------"; textBox1.Text += "Win32_UserAccount instance"; foreach (ManagementObject queryObj in mSearcher.Get()) { textBox1.Text += "\r\n " + "Caption: " + queryObj["Caption"].ToString(); textBox1.Text += "\r\n " + "Description: " + queryObj["Description"].ToString(); textBox1.Text += "\r\n " + "Domain: " + queryObj["Domain"].ToString(); textBox1.Text += "\r\n " + "FullName: " + queryObj["FullName"].ToString(); textBox1.Text += "\r\n " + "LocalAccount: " + queryObj["LocalAccount"].ToString(); textBox1.Text += "\r\n " + "Lockout: " + queryObj["Lockout"].ToString(); textBox1.Text += "\r\n " + "Name: " + queryObj["Name"].ToString(); textBox1.Text += "\r\n " + "PasswordChangeable: " + queryObj["PasswordChangeable"].ToString(); textBox1.Text += "\r\n " + "PasswordExpires: " + queryObj["PasswordExpires"].ToString(); textBox1.Text += "\r\n " + "PasswordRequired: " + queryObj["PasswordRequired"].ToString(); textBox1.Text += "\r\n "; } textBox1.Text += "\r\n "; } catch (Exception ex) { Console.WriteLine(ex.ToString()); } textBox1.Text += "-----------------------------------"; textBox1.Text += "ACL Registry Info - Current access rules:"; RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\"); RegistrySecurity rSec = rk.GetAccessControl(); foreach (RegistryAccessRule ar in rSec.GetAccessRules(true, true, typeof(NTAccount))) { textBox1.Text += "\r\n " + "User: " + ar.IdentityReference; textBox1.Text += "\r\n " + "Type: " + ar.AccessControlType; textBox1.Text += "\r\n " + "Rights: " + ar.RegistryRights; textBox1.Text += "\r\n " + "Inheritance: " + ar.InheritanceFlags; textBox1.Text += "\r\n " + "Propagation: " + ar.PropagationFlags; textBox1.Text += "\r\n " + "Inherited? " + ar.IsInherited; textBox1.Text += "\r\n "; } } static void Main(string[] args) { Application.Run(new SystemEnvironement()); } // end of main } // end of class } // end of namespace
Subscribe to:
Posts (Atom)