vrijdag 21 november 2008

Random thoughts…

  • Ik heb een nieuwe versie van "map2dbg" gemaakt. Hiermee kun je weer de .map bestanden van Delphi en C++Builder 2007, 2009 (en verder?) omzetten naar een .dbg (Microsoft debug) bestand. Deze kun je weer gebruiken met Proces Explorer (kijken waarom je app. hangt of waar hij mee bezig is etc) en met MS WinDbg (analyseren van minidumps/memorydumps).
    http://asmprofiler.googlecode.com/files/map2dbg.zip

  • Eerste ervaringen met de gratis Turbo C++ Builder:
    De sources van map2dbg waren gelukkig openbaar, dus ik kon met de gratis versie van Turbo C++ een fix en een nieuwe build maken. Mijn eerste ervaring met C++Builder ("wat een boel compiler opties! die wil ik ook in Delphi!") en na een lange tijd weer met C++. Brrr, wat kun je met C++ lelijke code maken zeg... :-)
    http://turboexplorer.com/downloads

dinsdag 4 november 2008

Delphi Prism (.Net) beter dan C#?

Er is een interessant interview met Marc Hoffman over Delphi Prism:
http://www.bitwisemag.com/2/Delphi-Prism-Visual-Studio-Pascal
Screencast:
http://www.delphi.org/screencasts/3-DelphiPrismVideo1.html

Onder andere over verbeteringen van Delphi Prism ten opzichte van C#:

Makkelijker werken met null values en nullable types:
http://wiki.remobjects.com/wiki/Colon_Operator
http://wiki.remobjects.com/wiki/Nullable_Types

Makkelijker "Parallel Framework" / PFX features gebruiken:
http://wiki.remobjects.com/wiki/Future_(keyword)
http://wiki.remobjects.com/wiki/Parallel_Loops
http://wiki.remobjects.com/wiki/Asynchronous_Statements

Overig: locking, uitgebreide "for each", support:
http://wiki.remobjects.com/wiki/Locked_(keyword)
http://wiki.remobjects.com/wiki/For_(keyword)#Shortcut_syntax_.22for_each.22.2B.22from.22
Support voor "Mono on Linux" en "Cocoa# on MaxOS X"

Overzicht van alle features:
http://wiki.remobjects.com/wiki/Oxygene
http://wiki.remobjects.com/wiki/Language_Features_3.0_(Oxyg%C3%A8ne)

donderdag 9 oktober 2008

SQL Server: nested transacties dmv workaround

Oa voor unit testen wilde ik nested transacties gebruiken bij MS Sql Server 2005. Tot mijn verbazing is dit standaard niet mogelijk via ADO! Het is wel mogelijk via SQL, maar dan nogal omslachtig… Uiteindelijk is het me gelukt…

Je kunt namelijk handmatig transacties starten en committen via SQL met “BEGIN TRANSACTION” en “COMMIT TRANSACTION” statements. Dit kan ook nested, waarbij je transacties een naam kunt geven, bijv: “BEGIN TRANSACTION level1″.

Echter, een rollback wordt ALTIJD op de buitenste transactie gedaan! Dus niet netjes nested zoals met een commit…
Na wat zoekwerk vond ik de oplossing: savepoints gebruiken. Na elke “BEGIN TRANSACTION” doe ik een “SAVE TRANSACTION ”. Deze savepoint kunt je weer ongedaan maken via “ROLLBACK TRANSACTION ”. Echter, de transactie bestaat nog steeds, dus na de rollback moet je nog een commit (!) doen op de transactie zelf. Verre van netjes, maar het werkt…

Bijvoorbeeld:
In het onderstaande voorbeeld worden 2 transacties gebruikt, waarbij de 2e en geneste transactie weer ongedaan gemaakt wordt. De 1e transactie wordt wel gecommit. Het resultaat is dat “something 1″ wel opgeslagen is, maar dat “something 2″ niet doorgevoerd is in de database.

BEGIN TRANSACTION level1
SAVE TRANSACTION save1

BEGIN TRANSACTION level2
SAVE TRANSACTION save2

ROLLBACK TRANSACTION save2
COMMIT TRANSACTION level2

COMMIT TRANSACTION level1

dinsdag 23 september 2008

Dll Import Table van executable aanpassen

In mijn vorige blogpost over de TC hack, had ik beloofd uit te leggen hoe je de hack (dll) vast in kunt bouwen, zodat het elke keer direct bij het starten van het programma actief wordt.

Dit is onder andere mogelijke door de "DLL Import Table" van de executable aan te passen. Deze table bevat namelijk de statisch gelinkte dlls en hun procedures (dus niet wat het programma zelf runtime dynamisch kan laden!). Deze tabel wordt door Windows in gelezen en alle dll's worden automatisch geladen. Door nu deze tabel aan te passen, kun je dus je eigen dll laden!

Na wat zoek en prutswerk kwam ik de volgende (werkende) tool tegen:
"Explorer Suite" (http://www.ntcore.com/exsuite.php ).
Een van de onderdelen van deze suite is de "CFF Explorer". Dit is een resource explorer, waarmee allerlei "resources" van een exe/dll bekeken en aangepast kan worden (icons, resourcestrings, etc). Een van de dingen die aangepast kan worden is de Import Table.

Hiermee heb ik met succes de "TCMain.exe" (exe van TC) aangepast, zodat het altijd mijn extra dll laadt. Bij het laden van de dll wordt direct de hook toegepast (zie vorige post).
Een simpel stappenplan:

  1. Bestand laden (TCMain.exe in dit geval)
  2. "Import Adder" selecteren
  3. Add knop en dll selecteren (TCHook.dll in dit geval)
  4. Een willekeurige functie van de dll selecteren (deze wordt toch niet aangeroepen, maar je hebt nu eenmaal 1 nodig)
    Vervolgens op "Import by Name" knop drukken
  5. Import table opnieuw bouwen
  6. En de aangepaste exe opslaan

Dat was alles!

maandag 1 september 2008

Team Coherence hack -> 10x sneller!

Op mijn huidige detacheringsplek gebruiken ze "Team Coherence" (http://www.teamcoherence.com) voor versie beheer en bug/issue tracking. Ze gebruiken dit pakket heel strict: bijvoorbeeld elke check-in moet een tracker melding hebben, een tracker heeft aantal stadia (waaronder controle door de aanvrager of indiener), versie labels, promotion levels, etc.

Op zich zijn ze tevreden over dit pakket. Het pakket heeft wel een poos een dip gehad, waarbij het niet meer onderhouden werd door de makers, maar nu wordt weer af en toe een update uitgebracht. Het grootste probleem echter wat ze hadden, was dat het pakket steeds trager werd: opstarten duurde maar liefst anderhalve minuut! Ook wisselen van views etc duurde dik een minuut.

Toen ik even niets te doen had (wachten op specs etc), heb ik even gekeken met Proces Explorer naar wat de oorzaak kon zijn. Nu blijkt dat hij veel geheugen acties doet (van 10Mb oplopend naar 65Mb en dan weer 36Mb), en veel kernel/system (rood) tijd gebruikt. Zie onder (er is trouwens getest op een HT systeem, vandaar niet meer dan 50% CPU):


1 thead (main thread) is druk bezig. Zie onder:



Als we nu de stack trace opvragen (en dit een paar keer doen), dan blijkt dat hij steeds bezig is met "GlobalRealloc".
Zie onder:


Ik weet dat het een Delphi 5 programma is, en Delphi gebruikt maar op 1 plek Global memory: TMemorystream.SetSize. Waarschijnlijk wordt steeds een paar bytes geschreven (stream.Write(s) oid), zodat het geheugenblok steeds met paar bytes vergroot moet worden. Nu doet Windows dit niet efficiënt: zelfs als na het huidige blok genoeg ruimte is, hij kopieert altijd naar een nieuw blok geheugen. En dit kost tijd, vooral als het heel vaak gebeurt. Vandaar de "rode" systeem CPU tijd.

Nu heb ik al wat ervaring met hacken gekregen met mijn profiler (http://code.google.com/p/asmprofiler) en detouring, dus ik heb ik een kleine dll geschreven die de GlobalRealloc overruled, en 1Mb extra reserveert. Als de nieuwe ruimte nog past in het oude, dan doet het niets, anders vraagt het door middel van de originele GlobalRealloc nieuw geheugen aan (met weer 1Mb extra):

function HookedGlobalReAlloc(hMem: HGLOBAL; dwBytes: SIZE_T; uFlags: UINT): HGLOBAL; stdcall;
begin
if GlobalSize(hMem) < id="pcex4"> Result := trampoline_GlobalReAlloc(hMem, dwBytes + (1000*1024), uFlags) //reserve 1Mb extra mem
else
Result := hMem; //do not realloc if needed size fits in current size, GlobalRealloc ALWAYS does a resize, even when it is not needed!
end;

Door middel van dll injection heb ik dit in Team Coherence geladen (zowel server als client), en nu duurt het opstarten maar 10 seconden! Dus 10x sneller!

P.S. ik heb dit ook aan de makers gestuurd, nog geen reactie tot nu toe...
P.S.2 de volgende keer zal ik demonstreren hoe ik de executable aangepast heb, zodat altijd mijn extra dll geladen wordt (dll injection dus niet meer nodig elke keer).

dinsdag 19 augustus 2008

ADO = Traag, kan 2x sneller!

Ik had wel eens eerder gehoord dat ADO traag zou zijn, maar ik had met ADO nog geen ervaring.
(trouwens, deze traagheid geldt voor ADO Win32, ADO.net weet ik niet, maar als ik SQL Server Enterprise Manager 2003 (win32)
vergelijk met SQL Server Management Studio 2005 (.net) dan is de .Net versie VELE malen trager!)

Geen ervaring, tot nu toe, want bij mijn huidige opdracht hebben ze een aantal database performance problemen.
De performance problemen komen oa door een slecht ontwerp (elke cell in een grid/lijst is een object
met allerlei properties -> veel overhead), maar ook voor een deel door ADO. De data wordt namelijk
via ADO geladen in hun eigen data objecten.

Nu verloopt bij ADO alles via OLE interfaces. Elke call moet worden geconverteerd, gecontrolleerd, foutafhandeling ,etc.
Dit geeft veel overhead. En bij het laden van data gebeurt dit bij elke cell! Als je veel records met veel velden hebt, gaat het dus hard...
Om deze overhead te minimaliseren kun je zelf ook gebruik maken van de interfaces ipv bijv. de standaard ADO dataset van Delphi.
Dit kan het ongeveer 2x sneller maken! Vooral bij veel enkelvoudige queries (detail record van een ID ophalen).

Voorbeeld:

//eerst data laden
procedure LoadData(aSQL: string);
var
connection : TADOConnection;
recordset : _Recordset;
dataset : TADODataSet;
begin
//recordset bevat data van SQL statement (select * from bla)
recordset := connection.Execute( aSQL );

//of van (bestaande) dataset
dataset.CommandText := aSQL;
dataset.Open;
recordset := dataset.RecordSet;
end;

//dan data vullen in een data object oid
procedure FillData;
var
field : TField;
adofield : ADOInt.Field;
recordset : ADOInt._Recordset
i : integer;
olerows : OleVariant;
begin
for i := 0 to recordset.Fields.Count-1 do
begin
//store adofield reference
adofield := recordset.Fields[i];
//get field object by name
f := data.FieldByName( adofield.Name );

//get value
f.Value := adofield.Value;

//of
//vanaf ADO 2.5 30% sneller?
f.Value := recordset.Collect[i];

end;

//of: alle waarden van record (of meerdere regels) in een array:
//get all values of current row
olerows := recordset.GetRows(1, 0, EmptyParam);

for i := 0 to recordset.Fields.Count-1 do
//niet sneller maar iets trager?
f.Value := olerows[i, 0];
end;

PS: eventueel de waarde van OleVariant nog omzetten naar juiste type Variant dmv "VarAsType", want met
OLE kan bijvoorbeeld een datum of een decimaal als een intern type opgeslagen zijn.

vrijdag 13 juni 2008

Reflector + Reflexil: Verander de code van een assembly (!)

".Net Reflector" zullen de meeste .Net'ers wel kennen (.Net assemblies details bekijken, analyseren, decompilen, etc):
http://www.aisto.com/roeder/dotnet/Download.aspx?File=Reflector
Voor dit mooie programma zijn ook allerlei "add ins" beschikbaar:
http://www.codeplex.com/reflectoraddins

1 ervan is "Reflexil":
http://sebastien.lebreton.free.fr/reflexil/
Hiermee kan een assembly ook bewerkt worden! Bestaande code kan aangepast worden, of geheel vervangen worden door nieuwe code. Ik heb het zelf even getest met een Delphi.Net programma, en het werkt super.

Het is gebasseerd op Mono.Cecil, wat onderdeel is van het open source "Mono" project:
http://www.mono-project.com/Cecil

Een aantal voorbeelden hoe je een assembly kunt aanpassen:
http://www.codeproject.com/KB/msil/reflexil.aspx
http://blog.cumps.be/reverse-engineering-with-reflector-and-reflexil/

Wat wel even belangrijk is als je het zelf probeert: als je iets aangepast hebt, moet je in de tree van Reflector de assembly zelf selecteren. Dan pas kun je de wijzigingen opslaan :-).

Nu kan het een en ander natuurlijk moeilijker gemaakt worden dmv "obfuscation". Hiermee worden de originele namen vervangen door "abc", code structuur iets aangepast (geen mooie "if then else" constructies, etc):
http://blog.cumps.be/obfuscation-making-reverse-engineering-harder/

Ook kun je "Code Signing" gebruiken om het nog moeilijker te maken, maar het is en blijft vrij eenvoudig mogelijk om een assembly aan te passen:
http://blog.cumps.be/code-signing-as-reverse-engineering-protection/

Veel succes ermee! :-)