BUILDING CUSTOM CONTROLS in C# – PART 1 :

cover 1

ABSTRACT

Custom UI Controls require a good grasp of GDI+, collections, delegates and events and smart use of multithreading facilities provided by the C# language. Here we focus on 2D graphics that incorporates the System.Drawing and System.Drawing.Drawing2D namespaces. You will build a custom control as an exercise.

Custom controls are different from components in C# lingo in that they have a user interface which can interact with the user who intends to use it. Therefore the use of a keyboard, mouse, joystick and pen has to be supported. Think of a network visualizer screen or a custom mixer in a music software package, or a 3D mesh control or a simple textbox with zooming feature added – the possibilities are endless and very simple as well once you get the hang of it.

Finally the visualizer looks like this, drag and drop any file on the main form. The title bar set the filename of the input file, the entropy score is shown in big font and the histogram distribution is shown at the right. In this picture we see a sosex_64.zip package which is a compressed file and its entropy value and byte distribution/histogram.

clip_image002

PREREQUISITES

Software Requirements : Visual Studio 2005 and above.

Elementary knowledge of C#.

Skill Level : Intermediate

 

DESIGN CONCEPTS

We will build an entropy visualizer and understand the construction steps.

We will also take a look at building a parallel axis graph control later.

Basically to implement any sort of UI interactivity as graphics you need to set boundaries in the visual area in terms of basic shapes or a series of points. Both of these can be generated in runtime or pre-calculated to your liking. Each of these bounded areas (series of closed points/shapes) has something called a region that is a data structure that encapsulates the area and the possible set of points that fall in that area. Thus any sort of visual interaction done by the user is recorded and processed as the points that have been interacted with onscreen. Every mouse movement can be traced as a set of coordinates on the visual area of the control and thus a pre-demarcated or generated set of point based boundaries immediately resolve the proximity awareness part of handling visual boundaries. To illustrate, each figure in the sample UI windows form has its own set of area points and a clear boundary. A straight line will still occupy a set of points that enable its representation. This set of points will be both its boundary and its constituent set of points. Thus tracking movement within the control visual area and incorporating logic becomes a simple issue as long as we know what is the current point being pointed to by the user and what part of the visual area contains that point. This is the fundamental concept to understand before you build your next Rodin in C#.

To construct a simple entropy visualizer we first need to understand what entropy is and find the best way to implement it in C# code. To summarize academic definitions it is the – degree of chaos in a system. It is also the number of bits required to represent information. If you look at compressed files or packed code, the entropy is high – in a range of 0 – 8 (bits per byte), the entropy is 6.XX – 7.XX, where XX signifies decimal digits as the value of entropy is calculated from the histogram of the whole data set, whereby the probability is calculated of each byte over the entire data set size and finally calibrated to accommodate a logarithmic scale.

The main formula is (in words)–

ENTROPY = SUMMATION OF i= (0 TO N) [-PROBABILITY OF BYTE [i] * LOG BASE 2 (PROBABILITY OF BYTE[i])]

… where the probability is just the count of occurrence of each byte (histogram) divided by the total data set size, which gives us an idea of the distribution.

The reasons why entropy is important are many in information theory, but it has mainly to do with how we humans process data and patterns and the natural occurrence of chaotic systems that maintain harmony with each other– say we take language as an example. Humans have an innate ability to gauge and recognise patterns in most things. We are creatures of habit and it comes from this. We like to categorise things and predict events.Written language is not different in that it always contains a set of elements called the alphabet from which words are constructed. These words are woven into sentences and sentences are spun to describe everything we see and feel around us. This whole process seems iterative and repeatable. The most used letter in the English language is ‘e’. It’s a vowel and thus is very important in giving most words its forming sound. Other languages also have a set number of highly used words and sounds. It follows that in a set of sentences that have a structure wherein the subject, verb and object are well placed – the entropy or the disorder is minimal in this context. Yet if every sentence in another paragraph is not made of carefully constructed words and semantic sentences but rather a rather random set of characters (including punctuation) that don’t conform to that language’s rules and idioms – what you are looking at is a chaotic representation of that language’s parameters. Thus in this case you won’t find the recurrence of a specific letter or a histogram that gives any indication that these sentences are constructed in the language’s order. Neither the character frequency, nor word count or sentence semantics make sense. In fact the histogram is going to be quite uniform even though the individual counts might be high according to the size of the total data. This is entropy in action. Thus randomly sampling a stream of characters within a minimum window size (which can be a buffer of characters/words/sentences – any logical unit) will not give any meaningful result. This also means that more set of characters are needed to represent anything resembling information. This measure in numeric after calibration for the dataset parameters will be the entropy value for this data set.

CONSTRUCTION

Open Visual Studio and create a new Windows Forms solution. Name it ENTROPY. Right click the project pane and create a new user control class. You see a blank area by default. It’s your job from here on to get it working.

clip_image004

clip_image006

clip_image008

The first thing to do is to approximately size up the area to something you can work with – just an estimate as we will use code that can resize if needed(fixed size is also possible).

clip_image010

Let us create an entropy class that can modularise the functionality of calculating from any source. This is my implementation of the Shannon entropy, it works just fine.

namespace ENTROPY

{

class EntropyC{

public static string GetEntropy(byte[] c)

{

int[] numArray = new int[0x100];

byte[] buffer = c;

for (int i = 0; i < 0x100; i++)//initialize each element to zero

{

numArray[i] = 0;

}

for (int j = 0; j < (buffer.Length – 1); j++) // histogram of each byte

{

int index = buffer[j];

numArray[index]++;

}

int length = buffer.Length;

float entropy = 0f;

for (int k = 0; k < 0x100; k++)

{

if ((numArray[k] != 0) && (k != 0))

{

entropy += (-float.Parse(numArray[k].ToString()) / float.Parse(length.ToString())) * float.Parse(Math.Log((double) (float.Parse(numArray[k].ToString()) / float.Parse(length.ToString())), 2.0).ToString());

}

}

return entropy.ToString();

}

}

}

THE VISUALIZER

The bulk of the code for the visualizer involves running in the paint loop and using the Graphics object instance of the current display surface and calling methods from it that can perform various screen drawing functions.

I used Photoshop to give the bar its gradient background by exporting a .png file and setting the user control background to it. This is open to artistic license.

clip_image012

Once the visualizer is compiled you will see a new control in the toolbox. Hereafter you can drag and drop it in the main form and arrange it as you want.

The visualizer code is just elementary geometry stuff really. The setBuffer() method takes in a byte array and proceeds to take a histogram or distribution of the byte array. After which it draws rectangles for each byte histogram value. The mouse events are handled for mouseLeave and mouseMove. For each probability value which is the totalCount of a particular value/Total Size of the input byte array (the input filesize in bytes), multiplication by 100 results in a percentage value, right? This value is used to calibrate the height of each horizontal bar in the entropy display for each byte value.

The main drawing classes that are used to emulate pen and paper – LinearGradientBrush/Font/SolidColor

DrawString() method takes in a string and prints it in the screen at a specified location.

DrawRectangle() is used to draw a thin rectangle to simulate a line.

Rectangle areas are stored in a data list generated in runtime to keep an in memory record of the rectangle areas so that the mouse hover interactions – called brushing can be responded with appropriate data from the same data set.

Point structures are used as is provided by the framework.

Invalidate() refreshes the screen and fires the paint event whenever a value is changed.

Another part of the display logic involves moving the byte count text in accordance with the cursor position in the bar, for better interactivity. A simple set of arithmetic adjustments so that the histogram feels more responsive.

using System;

using System.ComponentModel;

using System.Drawing;

using System.Windows.Forms;

using System.Collections.Generic;

using System.Collections;

using System.Drawing.Drawing2D;

namespace ENTROPY

{

/// <summary>

/// Description of EntropyViewer.

/// </summary>

public partial class EntropyViewer : UserControl

{

public EntropyViewer()

{

//

// The InitializeComponent() call is required for Windows Forms designer support.

//

InitializeComponent();

//

// TODO: Add constructor code after the InitializeComponent() call.

//

}

bool running = false;

List<int> distribution = new List<int>();

List<int> distributionReal = new List<int>();

public void setBuffer(byte[] c)

{

byte[] buffer;

int[] counter = new int[256];

distribution.Clear();

buffer = c;

for (int i = 0; i < 256; i++)

{

counter[i] = 0;

}

for (int y = 0; y < buffer.Length; y++)

{

int tmp = (int)buffer[y];

counter[tmp]++;

}

int great = counter[0];

for (int h = 1; h < 256; h++)

{

if (great < counter[h])

{

great = counter[h];

}

}

for (int k = 0; k < 256; k++)

{

distribution.Add((counter[k] * 100) / (great));

}

for (int k = 0; k < 256; k++)

{

distributionReal.Add((counter[k] * 100) / (c.Length));

}

running = true;

Invalidate();

}

float graphDiv; float w = 0;

List<distList> _entRect = new List<distList>();

//List<distList> _drawList=new List<distList>();

struct distList

{

int distributionPercentage;

RectangleF r;

string Val;

PointF A;

PointF B;

internal distList(int a, RectangleF b, string c, PointF d, PointF e)

{

distributionPercentage = a;

r = b;

Val = c;

A = d;

B = e;

}

public int DistList

{

get { return distributionPercentage; }

}

public RectangleF Rectangle

{

get { return r; }

}

public String ByteVal

{

get { return Val; }

}

public PointF LineStart

{

get { return A; }

}

public PointF LineEnd

{

get { return B; }

}

}

public void LineWidth(int a)

{

lWd = a; Invalidate();

}

public void fontSize(int a)

{

fWd = a; Invalidate();

}

int fWd = 9; //default

int lWd = 2; //default

PointF k; PointF ksub;

void EntropyViewerPaint(object sender, PaintEventArgs e)

{

if (running == true)

{

_entRect.Clear(); //_drawList.Clear();

graphDiv = ((float)this.Height – 1) / (float)256;

int cnt = 0;

LinearGradientBrush lb = new LinearGradientBrush(new Point(0, 0), new Point(0, 5), Color.Teal, Color.White);

lb.SetBlendTriangularShape(0.4f, 0.5f);

for (float h = 0; h < (float)this.Height; h += graphDiv)

{

//e.Graphics.DrawLine(new Pen(new SolidBrush(Color.SeaGreen),2),new PointF(h,(float)(this.Height-1)), new PointF(h,(float)this.Height-(distribution[cnt]*this.Height)/100));

e.Graphics.DrawLine(new Pen(lb, lWd), new PointF((float)(this.Width – 1), h), new PointF(((float)this.Width – (distribution[cnt] * this.Width) / 100), h));

RectangleF r = new RectangleF(new PointF(0, h), new SizeF((float)(this.Width), h));

//RectangleF r2=new RectangleF(new PointF(0,h),new SizeF((float)(this.Width),lWd));

_entRect.Add(new distList(distribution[cnt], r, (cnt.ToString(“X2”)), new PointF((float)(this.Width – 1), h), new PointF(((float)this.Width – (distribution[cnt] * this.Width) / 100), h)));

//_drawList.Add(new distList(distribution[cnt],r2,(cnt.ToString(“X2”)),

//new PointF((float)(this.Width-1),h),new PointF(((float)this.Width-(distribution[cnt]*this.Width)/100),h)

//));

if (cnt < 255) { cnt++; } else { break; }

}

if (OnDisp == true)

{ //for brushing info

if ((tmp.Height) <= (float)(this.Height / 2))

{

k = new PointF(1, tmp.Height + 30);

ksub = new PointF(1, tmp.Height + 43);

}

else if ((tmp.Height) > ((float)((3 / 4) * this.Height)) && tmp.Height < this.Height – 50)

{

k = new PointF(1, tmp.Height – 20);

ksub = new PointF(1, tmp.Height – 7);

}

else if ((tmp.Height > (this.Height – 30) && tmp.Height < (this.Height – 1)))

{

k = new PointF(1, this.Height – 50);

ksub = new PointF(1, this.Height – 37);

}

e.Graphics.DrawString(displayValue.Split(‘,’)[0], new Font(“Arial”, 8), new SolidBrush(Color.White), k);

e.Graphics.DrawString(displayValue.Split(‘,’)[1], new Font(“Arial”, 8), new SolidBrush(Color.White), ksub);

//e.Graphics.DrawRectangle(new Pen(new SolidBrush(Color.Yellow),lWd),new Rectangle(new Point((int)tmp2.Location.X,(int)tmp2.Location.Y),new Size((int)tmp2.Width,(int)tmp2.Height)));

LinearGradientBrush lb2 = new LinearGradientBrush(new Point(0, 0), new Point(0, 5), Color.Yellow, Color.Yellow);

lb.SetBlendTriangularShape(0.4f, 0.5f);

e.Graphics.DrawLine(new Pen(lb2, 2), LineSta, EndSta);

//lb.Dispose();

}

//e.Graphics.DrawRectangle(new Pen(new SolidBrush(Color.Gray)),new Rectangle(new Point(0,0),new Size(this.ClientRectangle.Width-1,this.ClientRectangle.Height-1)));

e.Graphics.DrawString(“0”, new Font(“Arial”, fWd), new SolidBrush(Color.White), new Point(0, 0));

SizeF s = e.Graphics.MeasureString(“255”, new Font(“Arial”, 9));

e.Graphics.DrawString(“255”, new Font(“Arial”, fWd), new SolidBrush(Color.White), new Point(0, this.Height – (int)s.Height – 1));

}

else

{ //e.Graphics.DrawRectangle(new Pen(new SolidBrush(Color.Gray)),new Rectangle(new Point(0,0),new Size(this.ClientRectangle.Width-1,this.ClientRectangle.Height-1)));

e.Graphics.DrawString(“0”, new Font(“Arial”, 9), new SolidBrush(Color.White), new Point(0, 0));

SizeF s = e.Graphics.MeasureString(“255”, new Font(“Arial”, 9));

e.Graphics.DrawString(“255”, new Font(“Arial”, 9), new SolidBrush(Color.White), new Point(0, this.Height – (int)s.Height – 1));//this.Width-(int)s.Width-1

}

//e.Dispose();

}

PointF mm; string displayValue = String.Empty; bool OnDisp = false; RectangleF tmp; PointF LineSta; PointF EndSta;

void EntropyViewerMouseMove(object sender, MouseEventArgs e)

{

string val = String.Empty;

mm = new PointF((float)e.X, (float)e.Y);

foreach (distList j in _entRect)

{

if (j.Rectangle.Contains(mm) == true)

{ //chk for point in var md hence required

tmp = j.Rectangle; LineSta = j.LineStart; EndSta = j.LineEnd;

//val = j.DistList.ToString();

displayValue = j.DistList.ToString() + ” % ” + “,” + j.ByteVal;

OnDisp = true;

}

Invalidate();

}

}

void EntropyViewerMouseLeave(object sender, EventArgs e)

{

OnDisp = false;

Invalidate();

}

}

}

THE MAIN FORM :

This is the container form for putting the control above on a windows form. We could have directly used this form for the display as the above, but that would defeat the purpose of building a modular control as the code here would be cumbersome to transfer to another form or project when required. Keeping the essential features only, we enable drag and drop code so that any file can be checked for entropy and byte distribution. This involves 3 steps – set AllowDrop property in the property page for this form to true. Set the DragEnter and DragDrop events from the events pane. Handle these events using code. Here filedrop is a DataFormat type and that is queried and converted to a string array. The first index – 0 is used to access the file path of the input file from the drag and drop operation. Finally DrawString Method is used to draw the entropy value from the byte buffer passed as an argument to the entropy class GetEntropyC() method which returns a string representation of the floating point value, The length of the value is checked for > 4 and only the substring is extracted from a longer floating point string for display purposes.

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.IO;

using System.Drawing.Drawing2D;

namespace ENTROPY

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

this.Paint += new PaintEventHandler(Form1_Paint);

}

void Form1_Paint(object sender, PaintEventArgs e)

{

e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

e.Graphics.DrawString(EntropyValue, new Font(“Arial”, 44), new SolidBrush(Color.Blue), new Point(25, this.Height/2 – 50));

}

private void Form1_DragEnter(object sender, DragEventArgs e)

{

if (e.Data.GetDataPresent(DataFormats.FileDrop))

{

e.Effect = DragDropEffects.All;

}

else { e.Effect = DragDropEffects.None; }

}

string EntropyValue = “0.0”;

private void Form1_DragDrop(object sender, DragEventArgs e)

{

string[] f = (string[])e.Data.GetData(DataFormats.FileDrop);

byte[] b = File.ReadAllBytes(f[0]);

this.Text =Path.GetFileName(f[0]);

entropyViewer2.setBuffer(b);

EntropyValue = EntropyC.GetEntropy(b);

if (EntropyValue.Length > 4)

{

EntropyValue = EntropyValue.Substring(0, 4);

}

Invalidate();

}

private void Form1_DragEnter_1(object sender, DragEventArgs e)

{

Form1_DragEnter(sender, e);

}

private void Form1_DragDrop_1(object sender, DragEventArgs e)

{

Form1_DragDrop(sender,e);

}

}

}

CONCLUSION :

We have seen how to quickly code and compile a custom user control for our daily programming purposes, be it security research quick fixes or the next CAD environment. The simplexity of C# can handle any graphics code with ease while incorporating user interactivity. The simple APIs and functions exposed in .NET under GDI+ involve wrapping a lot of complex interfaces (native API, WIN32 API, GDI32.DLL, USER32.DLL, WIN32K.SYS, GDI (old)/CLR Layer) in a user friendly programming API. From here you could build a directory entropy scanner, enabled with filetype detectors and their custom format parsers so that each type is searched for entropy reporting in a specific manner, for example – sections in a PE file or encryption keys in a malware set. This could be further embellished with more graphs and algorithm choices. The sky is the limit. In the next part we build a little more involved graphing utility.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s