Press "Enter" to skip to content

ILSPY – SZPIEGUJEMY SZPIEGA

Hej,

Z góry przepraszam że mnie wcięło, pociągi mi bokiem wychodzą, jak i ta cała praca. Nie tak dawno temu minął mi rok, z czego się bardzo cieszę. Jeszcze tylko rok i milestone przepracować pierwsze dwa lata życia będzie zaliczony.

Kiedy akurat nie czytam książki, nie śpię lub nie pracuję, przeglądam inne blogi. Siedzę a to na dotnetomaniaku, a to u któregoś ze slackowych (ircowym coś się nie chce blogować ostatnio) znajomych odpalę arta czy dwa, no i okazało się, że procent robi fajny konkurs. Tzn nie tyle konkurs fajny (acz nagroda przednia), co propozycje tematów. Art nie pasuje do żadnej z kategorii, ale nawiązuje tematycznie do całego wydarzenia. No może trochę. A z pewnością stanowiło inspirację. Tak czy inaczej, zapraszam do lektury.

Jest sobie ILSpy. Taaka zabawka, bierzesz binarkę, podglądasz kod. Zwykle jest on podobny do tego, jaki się napisze. No właśnie, zwykle. Żeby nie powiedzieć tylko wtedy, kiedy trzymasz się z dala od generyków, dynamiców i całej masy innego ciekawego śmiecia. Ok, tak naprawdę, to jest w stanie zgadnąć w miarę dokładnie wyłącznie dla prostych przypadków. To znaczy skompilowany kod wypluty przez ILSpy nadal będzie działać tak jak oczekujemy, ale już jego utrzymanie nie byłoby tak prostą rzeczą, więc jeśli ktoś planuje podkradać kod z binarek przy użyciu ILSpy (pamiętacie kalkulatorek?) to przynajmniej nie jeździ po bogu ducha winnej istocie, że brzydki kod pisze.

[tu miało być kilka screenów z VS, ale ten niestety postanowił się na mnie zezłościć i jeszcze mu nie przeszło- nadal jest fioletowy. Także posłużę się plaintextem].

Weźmy sobie taki oto kodzik.

using System;

namespace Simple_gettype
{
    class Program
    {
        static void Main(string[] args)
        {
            string stringvariable = "Jestem stringiem";
            Double doublevariable = 12;
            Console.WriteLine(stringvariable.GetType().ToString());
            Console.WriteLine(doublevariable.GetType().ToString());
            Console.ReadKey();
        }
    }
}

No niby nic, generalnie całość polega na tym, że printujemy typ danej zmiennej. Całe 5kb binarki, do tego 12kb symboli;).
Aby kompilator nie robił za dużo optymalizacji, kompilujemy nasz kodzik w debugmodzie, a następnie próbujemy podejrzeć go w ilspy.

No i ctrl+O, ścieżka do projektu, ciach. Przełączam podgląd kodu na c# i …

namespace Simple_gettype
{
	internal class Program
	{
		private static void Main(string[] args)
		{
			string stringvariable = "Jestem stringiem";
			double doublevariable = 12.0;
			Console.WriteLine(stringvariable.GetType().ToString());
			Console.WriteLine(doublevariable.GetType().ToString());
			Console.ReadKey();
		}
	}
}

Hmm… poradził sobie całkiem nieźle, poza .0 przy 12 dla double kod jest znak w znak taki sam.
No to zadanie „w szkole” wykonane prawidłowo.

Pójdźmy sobie w takim układzie kroczek dalej. Zadanie „na sprawdzianie”. Poprośmy o podanie typu, którego kompilator nie zna. Też 2 razy. Aby tego dokonać użyjemy typu dynamicznego i jeszcze (by sobie nie zgadł optymalizując) dołączymy delikatny czynnik losowości. No to jak?

Kod dla tego przykładu będzie taki:

using System;


namespace lessSimpleGettype
{
    class Program
    {
        static void Main(string[] args)
        {
            Random rand = new Random();
            bool startfromstring = false;
            int r = rand.Next() % 2;
            if (r == 1) startfromstring = true;
            if (r == 0) startfromstring = false;
            dynamic first;
            dynamic second;
            string stringvariable = "Jestem stringiem";
            Double doublevariable = 12;
            if (startfromstring == true)
            {
                first = stringvariable;
                second = doublevariable;
            }
            else
            {
                first = doublevariable;
                second = stringvariable;
            }
            Console.WriteLine(first.GetType().ToString());
            Console.WriteLine(second.GetType().ToString());
            Console.ReadKey();

        }
    }
}

Binarka tym razem ma 6KB, symbole 14. A co na to Ilspy? No dobrze, że mam tu zawijaczkę kodu.

using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Runtime.CompilerServices;

namespace lessSimpleGettype
{
	internal class Program
	{
		[CompilerGenerated]
		private static class <>o__0
		{
			public static CallSite> <>p__0;

			public static CallSite> <>p__1;

			public static CallSite> <>p__2;

			public static CallSite> <>p__3;

			public static CallSite> <>p__4;

			public static CallSite> <>p__5;
		}

		private static void Main(string[] args)
		{
			Random rand = new Random();
			bool startfromstring = false;
			int r = rand.Next() % 2;
			bool flag = r == 1;
			if (flag)
			{
				startfromstring = true;
			}
			bool flag2 = r == 0;
			if (flag2)
			{
				startfromstring = false;
			}
			string stringvariable = "Jestem stringiem";
			double doublevariable = 12.0;
			bool flag3 = startfromstring;
			object first;
			object second;
			if (flag3)
			{
				first = stringvariable;
				second = doublevariable;
			}
			else
			{
				first = doublevariable;
				second = stringvariable;
			}
			if (Program.<>o__0.<>p__2 == null)
			{
				Program.<>o__0.<>p__2 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "WriteLine", null, typeof(Program), new CSharpArgumentInfo[]
				{
					CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
					CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
				}));
			}
			Action arg_15D_0 = Program.<>o__0.<>p__2.Target;
			CallSite arg_15D_1 = Program.<>o__0.<>p__2;
			Type arg_15D_2 = typeof(Console);
			if (Program.<>o__0.<>p__1 == null)
			{
				Program.<>o__0.<>p__1 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "ToString", null, typeof(Program), new CSharpArgumentInfo[]
				{
					CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
				}));
			}
			Func arg_158_0 = Program.<>o__0.<>p__1.Target;
			CallSite arg_158_1 = Program.<>o__0.<>p__1;
			if (Program.<>o__0.<>p__0 == null)
			{
				Program.<>o__0.<>p__0 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "GetType", null, typeof(Program), new CSharpArgumentInfo[]
				{
					CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
				}));
			}
			arg_15D_0(arg_15D_1, arg_15D_2, arg_158_0(arg_158_1, Program.<>o__0.<>p__0.Target(Program.<>o__0.<>p__0, first)));
			if (Program.<>o__0.<>p__5 == null)
			{
				Program.<>o__0.<>p__5 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "WriteLine", null, typeof(Program), new CSharpArgumentInfo[]
				{
					CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
					CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
				}));
			}
			Action arg_260_0 = Program.<>o__0.<>p__5.Target;
			CallSite arg_260_1 = Program.<>o__0.<>p__5;
			Type arg_260_2 = typeof(Console);
			if (Program.<>o__0.<>p__4 == null)
			{
				Program.<>o__0.<>p__4 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "ToString", null, typeof(Program), new CSharpArgumentInfo[]
				{
					CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
				}));
			}
			Func arg_25B_0 = Program.<>o__0.<>p__4.Target;
			CallSite arg_25B_1 = Program.<>o__0.<>p__4;
			if (Program.<>o__0.<>p__3 == null)
			{
				Program.<>o__0.<>p__3 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "GetType", null, typeof(Program), new CSharpArgumentInfo[]
				{
					CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
				}));
			}
			arg_260_0(arg_260_1, arg_260_2, arg_25B_0(arg_25B_1, Program.<>o__0.<>p__3.Target(Program.<>o__0.<>p__3, second)));
			Console.ReadKey();
		}
	}
}

Tak paczem i widzem…
Ooo, nie ma pod spodem czegoś takiego jak dynamic. To zwykły object:). Ooo kompilator cwanie sobie wygenerował wszystkie możliwe przypadki i się będzie przełączał między nimi. Także cała dynamiczność jest ściemniana. Poza tym to OMG, ale mi się w oczkach pieprzy od tych dzióbków. Tak czy inaczej ten kod nie przypomina ani trochę kodu, jaki ręcznie napisałam. Pomijając, że bez zmiany nazw niedozwolonych na coś bardziej ludzkiego to się nawet nie będzie chciało skompilować;).

Mogłabym kombinować dalej i pokazać jaka sieka jest jak np. kompilator nie wie od razu jaką funkcję wywołać, ale to wam zostawiam jako zadanie domowe. Mogę tylko podpowiedzieć, że losową z nich przypisujemy do delegaty i nią wywołujemy.

Także nie, ILSpy to żadna Mata Hari nie jest. Trzymcie się i do poczytania.

One Comment

  1. ja ja

    PS ostatnią linijkę mi samo dodało próbując „zamykać tagi” nie należała do kodu.

Comments are closed.