WΛЯИING's blog

The deepest secrets of Visual Basic ...

C#: Adding many SortField to a CrystalReportViewer using reflection

April 9, 2009 22:16 by WΛЯИІNG

I'm currently working on a project for a client which consists in translating an important management application initially developed in VB6, into C# 3.5, by using decompilation since the client lost his source code. This is my first .Net related post.

The application uses many external components, including the powerful Crystal Report component.

During the development of that new version I faced a problem with the .Net implementation of Crystal Report.

Actually, with Visual Basic 6.0 it was an easy task to add one or many sort fields to the report document in order to sort the result by field (for example by "Lastname", "Firstname", and "ID").

It's not the case with .Net implementation of Crystal Report Document, on which you cannot dynamically add sort field to document. You must add the sort field when you're editing the document. It's not really convenient when you don't know how many fields must be sorted when creating the document.

The reason of this missing is not really clear since it's an existing feature on Visual Basic 6.0. We will learn here how to add many SortField to the ReportDocument of a CrystalReportViewer, using reflection.

Imagine you have to sort the request's result by "Firstname", "Lastname", and "ID" fields, using the Crystal report document with the variable "Filename" as the report document path, and the variable "srcDataSet" dataset as the data source:

string[] sortFieldNames = new string[] { "Firstname", "Lastname", "ID" };

ReportDocument
reportDocument = new ReportDocument();

reportDocument.FileName = String.Format("rassdk://{0}", Filename);

reportDocument.SetDataSource(srcDataSet.Tables[0]);

SortFields targetSortField = reportDocument.DataDefinition.SortFields;

Here we can modify the sort field (only if existing in the document):

DatabaseFieldDefinition fieldDef = reportDocument.Database.Tables["REPORTS"].Fields["FIRSTNAME"];

targetSortField[0].Field = fieldDef;

targetSortField[0].SortDirection = CrystalDecisions.Shared.SortDirection.AscendingOrder;

But impossible to add directly a new field to that SortFields collection because no method allows performing action:

pic1

Actually, it's possible to add a new sort field using the mother inherited class of each used component.

First we get the "SortFields" mother's class of type (CrystalDecisions.ReportAppServer.DataDefModel.SortsClass ) by using the RasSorts private accessor of the SortFields object:

MethodInfo getRasSorts = targetSortField.GetType().GetMethod("get_RasSorts", BindingFlags.NonPublic | BindingFlags.Instance);

object rasSorts = getRasSorts.Invoke(targetSortField, System.Type.EmptyTypes);

It return an object of type CrystalDecisions.ReportAppServer.DataDefModel.SortsClass.

Now we can access to the private "Add" method of the mother's class:

MethodInfo addSort = rasSorts.GetType().GetMethod("Add");

The description of that method show that it accept one parameter of type CrystalDecisions.ReportAppServer.DataDefModel.ISCRSort.

pic2 

ISCRSort is an interface, so we have to create a new instance of a class that implement the ISCRSort interface. That class is called "SortClass" and come from the "CrystalDecisions.ReportAppServer.DataDefModel" assembly.

Assembly rasAssembly = getRasSorts.ReturnType.Assembly;

ConstructorInfo ciRasSort = rasAssembly.GetType("CrystalDecisions.ReportAppServer.DataDefModel.SortClass").GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, System.Type.EmptyTypes, null);

object rasSort = ciRasSort.Invoke(System.Type.EmptyTypes);

Our "rasSort" object now instantiate the SortClass class, implementing ISCRSort.
Once the SortClass object is created, we must complete it with valid field, else it will return an error on execution.

The SortClass class uses the "RasField" accessor to define the field which will be sorted.

MethodInfo setSortField = rasSort.GetType().GetMethod("set_SortField", BindingFlags.Public | BindingFlags.Instance);

But this method only accepts an object implementing the ISCRField interface:
Void set_SortField(CrystalDecisions.ReportAppServer.DataDefModel.ISCRField);

So we have to extract the object of our field object (called fieldDef) that implement the ISCRField interface, it can be done with the "RasField" accessor of our DatabaseFieldDefinition object called "fieldDef":

MethodInfo getRasField = fieldDef.GetType().GetMethod("get_RasField", BindingFlags.NonPublic | BindingFlags.Instance);

object rasField = getRasField.Invoke(fieldDef, System.Type.EmptyTypes);

And now, the final touch, we add it to the collection:

addSort.Invoke(rasSorts, new object[] { rasSort });

Your new sort field has been adding to the SortFields collection, and you can now modify it:

int n = reportDocument.DataDefinition.SortFields.Count - 1;

reportDocument.DataDefinition.SortFields[n].SortDirection = CrystalDecisions.Shared.SortDirection.AscendingOrder;
reportDocument.DataDefinition.SortFields[n].Field = fieldDef;

That's all.


VBReFormer: CrackMe Sample #1

November 5, 2008 22:42 by WΛЯИІNG

Now that VBReFormer is a well advanced decompiler for Visual Basic application, I was searching for some unsolved crackmes in order to made sample of decompiling for learning purpose.

The website Crackmes.de contains an impressive number of crackmes applications, a perfect source of samples.

For the first sample of CrackMe solving with VBReFormer Professional I decided to take “Step 2” from yudi (more informations).

I will show you, step by step, how it’s simple to solve the yudi’s Step 2 using VBReFormer Professional.

  • Running the application:

yudi's Step 2 (CrackMe) / Screenshot 1

We can see that a serial is generated using the name of the user.

How the serial is generated? See the following step.

  • Now we just open the “Step 2.exe” file with VBReFormer Professional and getting the following result:

VBReFormer VB decompiler screenshot 1

  • We will now take a look to the first method loaded on Visual Basic application.

VBReFormer VB decompiler screenshot 2

We can see on this capture that the “Label4” visibility is set to False (not visible) at the beginning of the application.

Take a look to that control in the resource editor of VBReFormer and you will agree that it’s the control that show the message “Registered user!”

VBReFormer VB decompiler screenshot 3

We now need to know where the “Label4” control visibility is set to true, and what does the “Timer1” control.

  • The analysis of the Timer1 control is interesting but not very useful for the following of this tutorial.

VBReFormer VB decompiler screenshot 4

We can see here that the “Timer1_Timer” function is called every second by “Timer1” control in order to check that no debuggers, and if one is running, to close it.

We can note that it also close any MessageBox windows.

  • Now we are looking for the code under the “Try” button which check if the key match with the name.

That “Try” button is the “Command1” button in VBReFormer:

VBReFormer VB decompiler screenshot 5

Then just look to the Command1_Click() function in order to see the algorithm of key checking:

clip_image013

VBReFormer VB decompiler screenshot 6

The algorithm seems a little complicated for newbie, but complete and without any syntax and source code error from VBReFormer.

That’s a great thing for us; we will be able to test the application into the Visual Basic IDE later (to make a key generator for example).

By analyzing the code we can see the following:

Set var_pv2 = Me.Text1()
var_pv3 = var_pv2.Text()
var_pv10 = (var_pv3)
var_pv11 = (Date$) & (" ")
var_pv12 = (var_pv11) & (Time$)
var_pv13 = (var_pv12)

This part of code is showing us that the key is generated from the Name, but also with the Date and the Time !

That’s meaning it’s almost impossible to generate a key that does not expire the following second.

  • In order to made the Key Generator, save the project with VBReFormer, and open it with Visual Basic 6.

When it’s opened into the Visual Basic IDE, remove the debugger watching functions and just keep the following:

o Command1_Click

o Command2_Click

Now remove the following conditions block from Command1_Click function:

If (var_num8) Then
    var_pv6 = ("Hey")
    var_pv7 = ("need something")
    var_pv9 = MsgBox(var_pv7, 4160, var_pv6)
End If

These block are showing an alert when the “Name” field and when the “Key” field are empty, but it’s not usefull for a keygen.

At the end of the Command1_Click function we can see the serial check condition:

Set var_pv2 = Me.Text2()
var_pv3 = var_pv2.Text()
var_pv21 = (var_pv3)
var_pv22 = ((var_pv19 Like var_pv21))
If (((var_pv22) = (True))) Then
    Set var_pv2 = Me.Label4()
    var_pv2.Visible() = True
End If

That code is checking that the serial (stored in var_pv19 variable) generated from the name with the algorithm is the same than the one entered in the “Serial” field (Text2.Text).

To show the generated serial, we just need to replace that condition block by the following line of code:

VBReFormer VB decompiler screenshot 7

You must also remove the following line of code which remove the content of the both fields:

Set var_pv2 = Me.Text1()
var_pv2.Text() = ""
Set var_pv2 = Me.Text2()
var_pv2.Text() = ""
Set var_pv2 = Me.Text1()

After all change and simplifications, we have the following keygen code:

Private Sub Command1_Click()
    var_pv10 = Text1.Text
    var_pv13 = Date$ & " " & Time$
    For var_pv14 = 1 To Len(var_pv13) Step 1
        If IsNumeric(Mid$(var_pv13, CLng(var_pv14), 1)) Then
            var_pv15 = Asc(Mid$(var_pv13, CLng(var_pv14), 1))
            If var_pv14 <= Len(var_pv10) Then
                var_pv16 = Str(Asc(Mid$(var_pv10, CLng(var_pv14), 1)))
                var_pv16 = Right$(var_pv16, 1)
                var_pv16 = Val(var_pv16)
            End If

            var_pv18 = var_pv18 & Chr$(CLng(var_pv15 + 17 + var_pv16))
            var_pv18 = var_pv18 & Chr$(CLng(var_pv15 + 17 + var_pv16 * 2))
        End If
    Next var_pv14

    For var_pv14 = 1 To 24 Step 4
        var_pv19 = var_pv19 & Mid$(var_pv18, CLng(var_pv14), 4) & "-"
    Next var_pv14

    var_pv20 = Len(var_pv19) - 1
    var_pv19 = Mid$(var_pv19, 1, var_pv20)
    Text2.Text = var_pv19
End Sub

  • We now have to test our keygen:

yudi's Step 2 (CrackMe) / Screenshot 2

  • The first window is the windows of our KeyGen created from the original crackme, and the second window is the one of the original Crackme, with the key from the KeyGen.

The result is that our keygen work perfectly!

Just note that the use of date and time make your key valid for only 1 minute after having generated it.

Is it possible to bypass that limitation?

Yes it is ! In fact, to get the “Registered user!” message you even don’t need a key generator. By reading the code you can see that the operator used to perform a comparison between the both string key is the “Like” operator.

The “like” operator allows to comparate a string and a pattern…

Then you just can set “*” into the serial field and you will have a key which will be valid at anytime, with any name:

yudi's Step 2 (CrackMe) / Screenshot 3

 

Source code of the key generator can be downloaded here:

http://www.decompiler-vb.net/documentation/crackmes/step_2.zip

Enjoy it !


 
PageRank Actuel