• About Me
  • Blog
  • Home

Eric Hokanson

~ E's little space in cyberspace

Eric Hokanson

Tag Archives: ESET ChallengeME 2013

ESET CrackMe 2013 Challenge Solved!

18 Tuesday Feb 2014

Posted by Eric Hokanson in Cyber Security Research, Malware RE

≈ Leave a comment

Tags

ESET ChallengeME 2013, ESET CrackMe 2013 Challenge, Malware Analysis, Malware Reverse Engineering

my continuing adventures in reversing the eset 2013 crackme challenge…

In my last post, we found all the Easter eggs and set off looking for the solution to unlock this puzzle.  This is a challenge from an anti-malware company, so there should a “malicious-like” component to this puzzle and started examining other threads that were spawned before the WinMain function that displays the textboxes began.  We did find an interesting path, a path that led us to the process injection of Window’s userinit.exe.  That was our “malicious-like” behavior and when we traced the code to the CreateRemoteThread call:

Call_to_createRemoteThread

And stepped over that call (F8), we could observe our CrackMe code launching the newly injected userinit process:

userinit_in_task_mgruserinit_in_procmon

Unfortunately, the process goes immediately into a suspended state, which prevents OllyDbg from attaching to it.  After several failed attempts of trying to find a solution, I posed the question to the community for some help.  My thanks to the many members Reverse Engineering and Malware Group on LinkedIn for their suggestions.  The solution that worked was submitted by Mr. Mahmoudnia — he found another person’s solution to this ESET challenge.  The solution is rather clever.  Before we hit the second call to WriteProcessMemory:

before_2nd_call_to_WriteProcessMemory_0x403E70

Which is going to write the code at location 0x403E70 into userinit’s process space at 0xA0000.  We want to go to 0x403E70 and change the function prologue from “PUSH EBP” and “MOV EBP, ESP” to the opcode “EB FE”:

at_0x403E70

In Olly, right-click on the “PUSH EBP” instruction, select binary, then edit and uncheck the “keep size” box; now you can change the opcode to the value “EB FE”:

at_0x403E70_changed_to_EBFE

The opcode “EB FE” creates an infinite jump to the start of this function, i.e. to address 0x403E70.  Now we can step over the “WriteProcessMemoryCall” and proceed to step to the “CreateRemoteThread” call.  Now hit F8 to step over CreateRemoteThread and stay at location 0x4063B6:

step_over_call_to_CreateRemoteThread

Next, open a second OllyDbg instance, go to File -> Attach and scroll down the list of process until you find userinit.exe and click the “Attach” button:

attaching_to_userinit

Once attached and in the CPU window hit CTL-G and go to address 0xA0000 and change the “EB FE” back to the “PUSH EBP” and “MOV EBP, ESP” function prologue and set a break point.  This may take several attempts.  What consistently worked for me was to go to the theads window (the blue T button) and check to see if my thread was suspended.  If it was, I right clicked on the thread and selected “Resume”, then I hit F9 (Run).  If the RE gods are smiling upon us, we will hit the BP:

attached_to_userinit_goto_A0000

To see what the above routine does, I am going to single-step through this code line-by-line:

in_0xA0000_after_keygen_func

A little ways down, there is a call to a function at 0xA02F0.  I stepped inside that function and determined that this was some kind of key generation function.  Observe that ESI was set to the ASCII string “472631FB”.  Continuing on:

in_0xA0000_setting_up_Http_wget_over_port_1F90=8080

In the above chunk of code, we are making a call to a function in wininet.dll,  I used DLL Export Viewer to resolve the symbol name.  This is setting up a http network connection to localhost on port (highlighted as 0x1F90 in the stack window) 8080.  In my last post, we observed a wget POST call posting an index.php.  Continuing on, we can observe that this section of code is setting up all the parameters for an HttpOpenRequest call:

in_0xA0000_setting_up_HttpOpenRequest_index_php

Again, I fired up FakeNet and stepped over the call:

in_0xA0000_sends_index_php_over_port_8080

The wget POST with index.php is supposed to receive data but as you can clearly see, we don’t because EAX is set to 0.  The next instruction will test EAX and since it is 0, we will jump all the way down to 0xA0296.  At that location, there is code to terminate our thread.  I don’t want to end just yet.  There is an awful lot of code real estate that we are skipping and I want to see what it does.  So, I changed EAX from 0 to 1 so that the JE instruction is NOT taken in order to see what happens next:

in_0xA0000_compare_to_jules

In the above screen shot, we can see that what ever we were supposed to receive from our first wget POST is supposed to decrypt to the value of “Jules” — one of the main character’s (played by Samuel L. Jackson) in the movie “Pulp Fiction”.  So our Pulp Fiction theme continues.  Since we did not receive any data, the zero flag is not set and we make that long jump to 0xA0296 again.  So I set the ZF to 1 so the JNZ instruction is not taken.  Continuing on:

in_0xA0000_second_call_with_index_php

The code continues to set up a second HttpOpenRequest call.  This time instead of passing just index.php as one of the parameters, I thought I would change it to “index.php?key=280877F8”  This was the key that was generated in our first wget POST call.  I don’t know why I thought to try this, I was just curious and assumed that since we were getting a key, that there would be a second decryption routine.  Maybe I could coax the code to decrypted what ever is going to happen next…

in_0xA0000_second_call_index_php2

So we make the second wget POST call and and continue on to what I believe to be is a decryption routine…

in_0xA0000_decrypted_message_thats_all_congrats

Interesting!  We did get a message: “That’s all. Congratulations!”  We must be getting close to solving this thing.  So if we continue to single-step our way through this routine, eventually we wend our way to a USER32.MessageBox call:

in_0xA0000_call_to_final_messagebox

Let’s step over that call and see what message we get…

I_cracked_it_the_movie_is_over

W00t w00t — we did it!

This was a very fun RE challenge.  My thanks to ESET for creating such a fun puzzle and a special thanks to Mr. Mahmoudnia and others in the Reverse Engineering and Malware Research group on LinkedIn for their help and suggestions during the times I got stuck.  I learned some new tricks like that “EB FE” trick to attach to a suspended process.  I’ll have to remember that one if I ever encounter such a sneaky maneuver by malware in the future.

ESET Challenge Part 5

02 Sunday Feb 2014

Posted by Eric Hokanson in Cyber Security Research, Malware RE

≈ 1 Comment

Tags

ESET ChallengeME 2013, ESET CrackMe Challenge, Malware Analysis, Malware Reverse Engineering

In my last post we found all the Easter Eggs.  Each egg was another quote from the movie Pulp Fiction and when we found the most famous quote from the film:

all_easter_eggs_found

we knew we found all the eggs.

At the end of our hunt, I wondered if there might be more to this challenge.  There was a lot more code that I haven’t even analyzed yet and some of the code appeared obfuscated to hide its functionality.  And this is a crack me puzzle after all.  When we first discovered what to put in each stage box:

crack_me_textboxes_work_on_vm

we were alerted that we did not “do it.”  Since this challenge has an ongoing Pulp Fiction theme, and the Stage 2 text is not a quote from the movie, this may indicate that another Pulp Fiction quote might unlock the puzzle.  But which quote is the correct quote?  There are lots of memorable quotes from the film so a brute force approach is not likely practical.  Where to begin next?

After some thinking, I realized that the CrackMe text box above didn’t appear until the start (WinMain) function ran.  But, as we have seen in my previous posts, there is a lot of activity that happens before the start function runs.  There were several threads that ran before the main function.  I decided to put a break point on CreateThread and examine the corresponding call stack.  After setting the break point and hitting F9 to run, we encountered our first break:

first_BP_CreateThread

in the call stack window we can see the thread function starts at 0x404A60.  If we go to that address and put a break point there, we will be able to examine what that piece of code does.  But before I do that, I hit F9 (run) again to catch the next CreateThread call:

second_BP_CreateThread

this thread’s function starts at 0x404680.  If we go to that location:

threadfunct_404860_obfuscated

huh!  Olly is confused and is having a hard time analyzing this code; it thinks these are data bytes.  If we highlight all these data bytes to the nearest RETN instruction (at 0x404B1E), then right-click –> Analyze –> During next analysis, treat selection as –> Commands:

threadfunct_40486_revealed1

threadfunct_40486_revealed2

threadfunct_40486_revealed3

we can start to see that this is the part that sets up the named pipe server that we already analyzed in ESET CrackMe Challenge Part 3.  We can let this thread go; we already know what it does.

Lets return to the thread function from our CreateThread break at 0x405A60.

threadfunct_405A60_sleeps_until_textboxes_enabled

When this tread spawns, the process enters a sleep loop until the CrackMe text boxes are enabled and ready for input.  When that happens, the process sends us to a function at 0x405CD0.  So I put a break point at that function, let the program run (F9) and waited for a break to step into 0x405CD0.  Its a pretty big function but eventually we get to another CreateThread call:

threadfunct_405A6_takes_us_another_CreateThread_at_405D52

CreateThread_405D52_with_threadfunc_405CC0

this new thread has a function at 0x405CC0.  We set a break point at this new thread function to see what that does:

threadfunc_405CC0

it takes us to 0x405B10:

func_405B10

This is a huge function but the most interesting stuff happens here:

threadfunc_045CC0_with_textbox_proc_change

First, this function calls LoadLibrary and loads a number of user32.dll functions.  But as you can see in the Registers window, the function are not names but addresses (e.g., user32.7E41D60F).  This makes it harder for us to understand what is going on.  How can we resolve these address location calls in user32.dll to their actual function names?  There is a tool called DLL Export Viewer and it will load all of the Windows/System32 DLL’s mapping the addresses to function names.  I sorted on addresses and searched for what was being called at 0x405C7A and at 0x405C83:

DLL_Export_Viewer

In the example above, the CALL ESI is really a call to user32.7E41D60F, which is between the two addresses highlighted in the screen shot above.  So this call must be a call to a SetWindowLong function.  I did the same for the FindWindowsEx call that came before the SetWindowLong call.  After painstakingly stepping through this function and re-labeling all the user32.dll functions, I finally stumbled upon a key piece as illustrated in the following snippet of code:

Screen Shot 2014-02-01 at 5.54.04 PM

what this is doing is finding our CrackMe text box and setting the Stage 2 textbox to include this new procedure.  This means that the Stage 2 textbox will have two routines to check the input.  One will check for the “I did it!”, which will give us the “Nope. You didn’t!” dialog box, and the other checks for some other text — the correct text presumably.  We will need to set a break point at this new textbox procedure which is at:

new_textbox_proc_at_405F50

0x405F50

new_text_boxfunct_BP

Actually, through trial and error, I discovered that if we place a BP at 0x405F50, the CrackMe text boxes don’t finish rendering yet, and when you click on the CrackMe box, it breaks the program at 0x405F50.  I discovered that if we place a BP further down, say at 0x406023, the text box is fully rendered, and we can input text in the Stage 2 box, which will cause us to break at 0x406023.  If we do that, and place in any text other than the “I did it!”, we can trace this part of the code to try and figure out what the real message should be:

second_text_box_I_put_anything_to_hit_BP_at_406023

checks_my_imput_in_second_textbox_and_what_is_04041D0

We see that at 0x406023, ECX contains my bogus input and then a call is made to 0x4041D0.  What does that do?  If we go to 0x4041D0:

4041D0_is_obfuscated

Ack — we see that Olly hasn’t disassembled this piece correctly; it is fooled into believing that these are just data bytes.  So if we highlight all these data bytes until we get to the next RETN instruction (to 0x40429A), then right-click on the highlighted section, select Analysis –> During next analysis, treat selected as –> Commands:

4041D0_deobfuscated1

4041D0_deobfuscated2

The above two screen shots show the entire routine as supposedly correct commands.  This routine is rather complicated but my gut tells me that this is some kind of encryption routine.  I am rather lazy, so instead of analyzing the above mess, I am going to try and find an easier way by observing the behavior of stepping over this routine and see what the program does.  Back to our current breaking point:

comparing_my_imput_to_some_value_at_4227EC

when we return from the 0x4041D0 encryption routine, we note that some data at 0x4227EC is being loaded into a pointer to a data segment.  It looks like the program will compare that data with my encrypted input of “sfsf”.  If we follow 0x4227EC in the dump window (highlighted in grey in the bottom left window), we can see that this data looks encrypted.  Naturally, my input is not going to match whatever is in 0x4227EC:

comparison_not_same_back_to_new_textbox_proc

hence, the program will sit here waiting for the correct input from the Stage 2 textbox.  If we let the program resume (F9), we should be able to enter some other text into Stage 2.  It is likely that the encryption routine is symmetric since that is computationally cheaper than an asymmetric scheme.  To test my theory, I entered the word “cat” and hit the check button.  When we broke again, I stepped over the encryption routine and observed the encrypted result of “cat”.  I copied this encrypted “cat”, then resumed the program again (F9) and pasted the encrypted version of cat into the Stage 2 textbox and hit the check button to return to our current break point.  When I stepped over the encrypted routine again, I got back the word “cat”; this confirms my hunch.  So, we should be able to apply the same trick to the data at 0x4227EC.  Copy and paste that encrypted data into the Stage 2 textbox and let the program run again:

cut_and_paste_value_at_4227EC

cut_and_past_value_in_second_textbox

And…

Zed's_dead_baby1

Did you see it?  In the stack window??

Zed's_dead_baby2

“Zed’s dead, baby” — not only another quote from Pulp Fiction (uttered by Bruce Willis’ character Butch), but this must be the correct quote to unlock this puzzle!

After entering the correct input, it looks like the code will now take us to:

with_correct_input_we_goto_4060C0

0x4060C0:

base64_decoder_for_userinit

0x4060C0, like the previous thread routine, is loading a number of functions but this time from kernel32.dll; and like the previous thread, it is using address locations and not the names of the functions from kernel32.dll to hide the true functionality.  So again, I used the DLL Export Viewer tool to resolve the names.  We eventually come to an interesting part of the routine as observed in the above screen shot.  Another base64 encoded string.  The string is sent to the base64 decoder function and I already decoded it as noted above: it looks like this routine is going to do something with userinit.exe.  This must be the “malware” part of the CrackMe puzzle.  No legitimate program has any business hooking into userinit.exe; this is typical malware behavior.

Userinit, according to Microsoft, “Specifies the programs that Winlogon runs when a user logs on. By default, Winlogon runs Userinit.exe, which runs logon scripts, reestablishes network connections, and then starts Explorer.exe, the Windows user interface.”  Let us see what CrackMe intends to do with userinit:

allocating_new_memory_in_userinit_at_0x90000

at 0x406348, we see that in EAX, new memory was allocated in userinit.exe’s process space at address 0x90000.  This is writing some sort of data, I presume.  Then…

allocating_new_memory_in_userinit_at_0x0A0000

at 0x406380, more memory is allocated in userinit.exe’s process space at location 0xA0000.  Continuing a little further…

Call_to_createRemoteThread

we observe a call to CreateRemoteThread with EAX = 0xA0000.  So what was written at 0xA0000 must be code because CreateRemoteThread creates a thread that runs in the virtual address space of another process.

Before stepping over the CreateRemoteThread, I fired up FakeNet and ProcMon tools to capture any events that might happen:

after_stepping_over_CreateRemoteThread_a_post_index_php

this new process used wget to post an index.php on port 8080 localhost.  That is interesting, but here is where my trail (or thread) goes cold.  When I look for this process, I can not find it.  I don’t see this new process spawned by CreateRemoteThread listed in the active processes in OllyDbg even though it is clearly running somewhere:

crack_me_launches_userinit_and_is_running

There is a PID of 2276 but I don’t even see it in Process Explorer or the task manager.  This leaves me somewhat stumped as to how I can attach to this new process.  If I can find it, and attach, then I might be able to figure out what to do next.  I invite the community to share any hints or tips as to how I might attempt to locate this process.  I am so close to solving this thing — I can feel it.  I would expect that the patched userinit is supposed to generate my next dialog box after entering the correct quote, “Zed’s dead, baby” in the Stage 2 textbox.  But it must be missing a key piece to finish the job.  What was sent to localhost?

index_php_post

it looks like there is a key of some kind.  My guess is index.php must somehow use the code injected into userinit.exe at 0xA0000 to decrypt the data at 0x90000 (???) but I won’t know this for sure until I figure out how to find and attach to the process spawned at 0xA0000.

I will keep playing with it to see if I can figure it out.  If I come up with anything new, I will be sure to post those results here.  Stay tuned…

EDIT TO ADD February 18, 2014:  After several debug sessions, I did manage to find the userinit process in Windows Task Manager and Sysinternal’s Process Explorer.

userinit_in_task_mgruserinit_in_procmon

However, I still can not locate the userinit thread in OllyDbg to attach to it.  The problem seems to be that when the thread is spawned, it immediately goes into a suspended state, which would explain why I can’t find it in Olly’s active process window.  Hmmmmmm….  I will keep trying…

ESET Challenge Part 4

26 Sunday Jan 2014

Posted by Eric Hokanson in Cyber Security Research, Malware RE

≈ 2 Comments

Tags

ESET ChallengeME 2013, ESET CrakMe 2013 Challenge, Malware Analysis, Malware Reverse Engineering

My continuing adventures in the ESET 2013 CrackMe Challenge

Last time, we discovered that the crack_me_2.zip binary contains two BMP resources, and one of those resources were extracted and dropped into one of my temporary directories.  This “BMP” was actually an executable:

crack_me_2_dotnet_eastereggUpon double-clicking it displays a cute little photo.  We also learned that the executable to the left was not just any executable but a .NET executable and an obfuscated one at that.

crack_me_2_dotnet_decompiled

The strings all appear to be encrypted with some sort of byte array scheme. Classes and functions are all labeled with only the letters A through F in upper and lowercase.

However, even though this executable is obfuscated, we did see an indication that this rabbit bitmap is actually a pipe-server.  The pipe server that our crack_me_2 binary was searching for.  Upon first running the crack_me_2, we didn’t have a pipe-server running so the function returned a -1.  Crack_me_2 then proceeded to create the .tmp

crack_me_2_named_pipefile that resulted in the “Easter Egg” pipe-server .NET file.  Below is the disassembled code confirming our named pipe “Vincent_Vega” server:

easter_egg_pip_server

And the code that creates the pipe:

ester_egg_pipe2

To disassemble a .NET file you may use ILSpy or .NET Reflector.  I used both.  Started with ILSpy to statically study the Easter Egg and Reflector for debugging (dynamic analysis).

Upon re-starting crack_me_2 with this Easter Egg pipe server running, I set a break point on CreateFileW and instead of EAX = -1, EAX = 0x2C so we got a handle to our named pipe.  Now that a pipe server is running, the program skips all the instructions to create its .tmp Easter Egg file and jumps to here:

hamberger_messgae

We see another base 64 encoded string:

base64_royale_with_cheese

With the pipe-server running, the pipe sends the message “Le Big Mac”, and then compares that with our “Royale with Cheese”:

hamburger_message2

In the above screen shot, on the right, you can see the current state of the registers.  EAX contains the string “Le Big Mac”, while ESI holds “Royale with Cheese”.  These two registers are compared.  If they were equal, the Hamburger message box would appear with the message: “Hamburgers.  The cornerstone of any nutritious breakfast.”   The Pulp Fiction theme continues as the hamburger message is another quote delivered by Jules (Played by Samuel L. Jackson) in the film’s diner scene.  Since the messages do not match, no dialog box will be displayed.  However, we can hack the code, change our “Royale with Cheese” to “Le Big Mac” if we wanted to display the message.

Either way, hamburger message or no, when we continue to single-step, the program now takes us to:

loading_malcho_dll

Another set of base64 encoded strings.  They translate to:

base64_malcho_dll

base64_malcho_dll_function

So crack_me_2 is going to attempt to load a MALCHO.DLL that exports a base64 encoded function that gets a password.

It looks as though, once MALCHO.DLL is loaded, crack_me_2 will extract a password from the Easter Egg pipe server.  This password will then be used to in the following:

gets_decrypts_resource_133

The function call at 0x404A71 is going to extract the second BMP resource in crack_me_2’s resource section. The parameter of 0x85 = 133 in base 10 and we see thanks to PE Explorer that the 133 is the second BMP’s resource ID.

crack_me_2_resources_BMPs

All that is missing is the first parameter — the password that this so-called MALCHO.DLL is supposed to retrieve for us.  But where is this dll?  I have to admit that this stymied me for a few days.  I searched up and down the code looking for this MALCHO.DLL.  I even tried googling for it.  No hits for the dll, but there were links to reports that contained the name, Juraj Malcho, head of — you guessed it — ESET’s Virus Laboratory.  Could he be the author???  I was wondering about the dll’s name because I did not see any reference to a Malcho in Pulp Fiction.

After a couple of days searching to no avail, I decided to ask the Reverse Engineering Malware Research group on linkedIn (my thanks to Mr. Mahmoudnia) for some hints.  It turns out that there is no MALCHO.DLL; I have to make my own MALCHO.DLL.  They key is in the .NET Easter Egg binary.

To help me better understand this .NET Easter Egg, I used a tool called DeReactor to deobfuscate this .NET file.

After setting up the named pipe-server, most of the action appears to be here:

deobfuscated_dynamic_routine

deobfuscated_dynamic_routine_part2

The deobfuscated file calls it method_8() in Class_06; its called Method A of Class D in the obfuscated file.  This took me a while to figure out what was going on here.  It turns out that this Easter Egg .Net creates threads that dynamically — and it is one of three routines that does this (the whole chunk of code is below; feel free to scroll past all that).

protected void A([In] List<byte> obj0)
{
Encoding.ASCII.GetString(obj0.ToArray(), 1, obj0.Count - 1);
}

protected void B()
{
string str1 = string.Format(.d(), (object) null, (object) null);
string str2 = string.Format(.E(), (object) null, (object) null);
string str3;
lock (D.A)
str3 = !this.a ? str2 : str1;
this.A.A(str3);
}

private void b()
{
this.A.Add((object) new G((object) OpCodes.Ldc_I4, (object) (this.A & (int) byte.MaxValue), (object) .e()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4, (object) (this.a & (int) byte.MaxValue), (object) .F()));
this.A.Add((object) new G((object) OpCodes.Xor, (object) null, (object) .f()));
}

private void A([In] object obj0)
{
try
{
Thread thread = obj0 as Thread;
if (thread == null)
{
thread = new Thread(new ParameterizedThreadStart(this.A));
thread.IsBackground = true;
thread.Start((object) Thread.CurrentThread);
this.a = this.A(MethodBase.GetCurrentMethod());
this.a.Set();
Thread.Sleep(500);
}
while (!this.A)
{
if (Debugger.IsAttached || Debugger.IsLogging())
this.a();
if (!thread.IsAlive)
this.a();
Thread.Sleep(1000);
}
lock (D.B)
{
if (--this.B != 0)
return;
this.B.WaitOne();
this.A.A.WaitOne();
this.A.Set();
}
}
catch (Exception ex)
{
}
}

private int A([In] MethodBase obj0)
{
byte[] ilAsByteArray = obj0.GetMethodBody().GetILAsByteArray();
int num1 = 0;
foreach (byte num2 in ilAsByteArray)
num1 += (int) num2;
return num1;
}

protected void C()
{
this.A.Add((object) new G((object) OpCodes.Ldarg_1, (object) null, (object) .G()));
this.A.Add((object) new G((object) OpCodes.Ldloc_0, (object) null, (object) .g()));
this.A.Add((object) new G((object) OpCodes.Ldloc_2, (object) null, (object) .H()));
this.A.Add((object) new G((object) OpCodes.Ldarg_0, (object) null, (object) .h()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .I()));
this.A.Add((object) new G((object) OpCodes.Ldloc_2, (object) null, (object) .i()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_1, (object) null, (object) .J()));
this.A.Add((object) new G((object) OpCodes.Add, (object) null, (object) .j()));
this.A.Add((object) new G((object) OpCodes.Stloc_2, (object) null, (object) .K()));
this.A.Add((object) new G((object) OpCodes.Ldlen, (object) null, (object) .k()));
this.A.Add((object) new G((object) OpCodes.Conv_I4, (object) null, (object) .L()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .l()));
this.A.Add((object) new G((object) OpCodes.Ldarg_0, (object) null, (object) .M()));
this.A.Add((object) new G((object) OpCodes.Ldloc_2, (object) null, (object) .m()));
this.A.Add((object) new G((object) OpCodes.Ldelem_U1, (object) null, (object) .N()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .n()));
this.A.Add((object) new G((object) OpCodes.Stloc_3, (object) null, (object) .O()));
this.A.Add((object) new G((object) OpCodes.Ldloc_1, (object) null, (object) .o()));
this.A.Add((object) new G((object) OpCodes.Ldloc_0, (object) null, (object) .P()));
this.A.Add((object) new G((object) OpCodes.Sub, (object) null, (object) .p()));
this.A.Add((object) new G((object) OpCodes.Add, (object) null, (object) .Q()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .q()));
this.A.Add((object) new G((object) OpCodes.Stloc_0, (object) null, (object) .R()));
this.A.Add((object) new G((object) OpCodes.Stloc_0, (object) null, (object) .r()));
this.A.Add((object) new G((object) OpCodes.Ldloc_0, (object) null, (object) .S()));
this.A.Add((object) new G((object) OpCodes.Ldarg_1, (object) null, (object) .s()));
this.A.Add((object) new G((object) OpCodes.Ldlen, (object) null, (object) .T()));
this.A.Add((object) new G((object) OpCodes.Conv_I4, (object) null, (object) .t()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_1, (object) null, (object) .U()));
this.A.Add((object) new G((object) OpCodes.Sub, (object) null, (object) .u()));
this.A.Add((object) new G((object) OpCodes.Ceq, (object) null, (object) .V()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .v()));
this.A.Add((object) new G((object) OpCodes.Ceq, (object) null, (object) .W()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_1, (object) null, (object) .w()));
this.A.Add((object) new G((object) OpCodes.Stloc_3, (object) null, (object) .X()));
this.A.Add((object) new G((object) OpCodes.Stloc_S, (object) 4, (object) .x()));
this.A.Add((object) new G((object) OpCodes.Ldloc_S, (object) 4, (object) .Y()));
this.A.Add((object) new G((object) OpCodes.Clt, (object) null, (object) .y()));
this.A.Add((object) new G((object) OpCodes.Stloc_S, (object) 4, (object) .Z()));
this.A.Add((object) new G((object) OpCodes.Ldloc_S, (object) 4, (object) .z()));
this.A.Add((object) new G((object) OpCodes.Ldelem_U1, (object) null, (object) .aA()));
this.A.Add((object) new G((object) OpCodes.Ceq, (object) null, (object) .aa()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .aB()));
this.A.Add((object) new G((object) OpCodes.Ceq, (object) null, (object) .ab()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .aC()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .ac()));
this.A.Add((object) new G((object) OpCodes.Stloc_0, (object) null, (object) .aD()));
this.A.Add((object) new G((object) OpCodes.Stloc_1, (object) null, (object) .ad()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .aE()));
this.A.Add((object) new G((object) OpCodes.Stloc_2, (object) null, (object) .ae()));
this.A.Add((object) new G((object) OpCodes.Stloc_S, (object) 4, (object) .aF()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .af()));
this.A.Add((object) new G((object) OpCodes.Ldloc_S, (object) 4, (object) .aG()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .ag()));
this.b();
this.A.Add((object) new G((object) OpCodes.Ldloc_0, (object) null, (object) .aH()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_1, (object) null, (object) .ah()));
}

protected void c()
{
this.A = new DynamicMethod(.aI(), typeof (bool), new Type[2]
{
typeof (byte[]),
typeof (byte[])
}, typeof (D).Module);
ILGenerator ilGenerator = this.A.GetILGenerator();
ilGenerator.DeclareLocal(typeof (int), true);
ilGenerator.DeclareLocal(typeof (int), true);
ilGenerator.DeclareLocal(typeof (int), true);
ilGenerator.DeclareLocal(typeof (bool), true);
ilGenerator.DeclareLocal(typeof (bool), true);
Label label1 = ilGenerator.DefineLabel();
Label label2 = ilGenerator.DefineLabel();
Label label3 = ilGenerator.DefineLabel();
Label label4 = ilGenerator.DefineLabel();
Label label5 = ilGenerator.DefineLabel();
Label label6 = ilGenerator.DefineLabel();
this.A.Add((object) new G((object) OpCodes.Brtrue_S, (object) label2, (object) .ai()));
this.A.Add((object) new G((object) OpCodes.Br_S, (object) label1, (object) .aJ()));
this.A.Add((object) new G((object) label6, (object) null, (object) .aj()));
this.A.Add((object) new G((object) OpCodes.Brtrue_S, (object) label3, (object) .aK()));
this.A.Add((object) new G((object) OpCodes.Br_S, (object) label4, (object) .ak()));
this.A.Add((object) new G((object) label3, (object) null, (object) .aL()));
this.A.Add((object) new G((object) OpCodes.Br_S, (object) label5, (object) .al()));
this.A.Add((object) new G((object) label2, (object) null, (object) .aM()));
this.A.Add((object) new G((object) label5, (object) null, (object) .am()));
this.A.Add((object) new G((object) label1, (object) null, (object) .aN()));
this.A.Add((object) new G((object) OpCodes.Brtrue_S, (object) label6, (object) .an()));
this.A.Add((object) new G((object) OpCodes.Br_S, (object) label4, (object) .aO()));
this.A.Add((object) new G((object) label4, (object) null, (object) .ao()));
this.A.Add((object) new G((object) OpCodes.Ldloc_3, (object) null, (object) .aP()));
this.A.Add((object) new G((object) OpCodes.Xor, (object) null, (object) .ap()));
this.A.Add((object) new G((object) OpCodes.Ret, (object) null, (object) .aQ()));
this.A.Sort();
foreach (G g in this.A)
{
if (g.A is OpCode)
{
if (g.a != null)
{
if (g.a is byte)
ilGenerator.Emit((OpCode) g.A, (byte) g.a);
else if (g.a is int)
{
ilGenerator.Emit((OpCode) g.A, (int) g.a);
}
else
{
if (!(g.a is Label))
throw new Exception(.aq());
ilGenerator.Emit((OpCode) g.A, (Label) g.a);
}
}
else
ilGenerator.Emit((OpCode) g.A);
}
else if (g.A is Label)
ilGenerator.MarkLabel((Label) g.A);
}
this.A = (D.e<bool, byte[], byte[]>) this.A.CreateDelegate(typeof (D.e<bool, byte[], byte[]>));
}

To make analyzing this Easter Egg a challenge, these methods are generating MSIL opcodes on the fly and in a “randomish” order.  Note the this.A.Sort() after all those this.A.Add(…) statements.  To better understand what is going on, we will need to run it in Reflector’s Visual Studio debugger plugin.  But first we will have to deal with:

dotNet_anti_debugging

Its checking if we are running the Easter Egg program in a debugger or if we are running any logging tools, if so the program basically does nothing.  So I used Reflector to edit out that snippet, “re-compile” it and set a break point after the this.A.Sort() routine.  Then I dumped out the IL (Intermediate Language) evaluation stack and here is what I got (I put in the comments):

L_000000000: Nop
L_000000010: Ldc.I4.0
L_000000020: Stloc.0
L_000000030: Ldc.I4,88 	//Load value 88 
L_000000031: Ldc.I4,254 //Load value 254 
L_000000032: Xor  	// Xor: 88 xor 254 = 166
L_000000080: Stloc.1	// store the value 166
L_000000090: Ldc.I4.0
L_0000000A0: Stloc.2
L_0000000B0: br.s label
L_0000000CF: label6
L_0000000D0: Nop
L_0000000E0: Ldarg.0	//Load the byte[] array
L_0000000F0: Ldloc.2 	//Load local2 
L_000000100: Ldelem.U1	//Take one byte from byte[] array with index at 
			//local2
L_000000110: Ldloc.1 	//
L_000000120: Ldloc.0
L_000000130: Sub	//local1 - local2
L_000000140: xor	//(local1 - local2) Xor byte[]
L_000000150: Ldarg.1
L_000000160: Ldloc.0
L_000000170: Ldelem.U1
L_000000180: Ceq
L_0000001A0: Ldc.I4.0
L_0000001B0: Ceq
L_0000001D0: Stloc.S,4
L_0000001F0: Ldloc.S,4
L_000000210: brtrue.s label2
L_000000230: Nop
L_000000240: ldloc.0
L_000000250: ldc.i4.1
L_000000260: Add
L_000000270: Stloc.0
L_000000280: Ldloc.0
L_000000290: Ldarg.1
L_0000002A0: Ldlen
L_0000002B0: Conv.I4
L_0000002C0: Ldc.I4.1
L_0000002D0: Sub
L_0000002E0: Ceq
L_000000300: Ldc.I4.0
L_000000310: Ceq
L_000000330: Stloc.S,4
L_000000350: Ldloc.S,4
L_000000370: brtrue.s label3
L_000000390: Ldc_I4.1
L_0000003A0: Stloc.3
L_0000003B0: Br.s label4
L_0000003CF: label3
L_0000003D0: Nop
L_0000003E0: br.s label5
L_0000003FF: label2
L_000000400: Ldc_I4.0
L_000000410: Stloc.0
L_000000420: Nop
L_000000430: Ldloc.2
L_000000440: Ldc_I4.1
L_000000450: Add
L_000000460: Stloc.2
L_000000470: Ldloc.2
L_000000480: Ldarg.0
L_000000490: Ldlen
L_0000004A0: Conv.I4
L_00000041F: label5
L_00000046F: label
L_0000004B0: Clt
L_0000004D0: Stloc_S,4
L_0000004F0: Ldloc_S,4
L_000000510: brtrue.s label6
L_000000530: Ldc_I4_0
L_000000540: Stloc_3
L_000000550: br.s label4
L_00000056F: label4
L_000000570: ldloc.3
L_000000580: Ret

It looks like this is part of a decryption routine.  In C, it would look something like:

for(int i = 0; i < byteArray.length; i++)
{
    byteArray[i] = (byteArray[i] ^ (166 - i));
}

And what is the byte array? If we examine the original obfuscated start of our routine, we see that there is one byteArray passed in as an argument:

byte_array_screen_shot

and the only byte[] member in Class D contains:

byte_array_contents

which if you run in our psuedo decryption algorithm above, transforms those numbers in the byte array to the ASCII codes that produces the string:

/ download/bmV4dF9maWxl/cGEkJHdk.txt

That looks like a URL. Now this is starting to make sense! In the following:

enumerating_windows_screenshot

My guess is that the above chunk enumerates searching for Internet Explorer and sends the decrypted URL.  If we append our decrypted URL to ESET’s URL:

http://www.joineset.com/download/bmV4dF9maWxl/cGEkJHdk.txt

then click on the link, the site contains:

Jules:You read the bible, Ringo?

So this must be the password that our MALCHO.DLL must retrieve for us to use as the decryption key to the second BMP resource. So now its off to Visual Studio to create a simple MALCHO.DLL:

malcho_dll_code

It simply returns the password as a string.  Also I changed the function name since the base64 encoded name had an equals sign in it.  At first I used the base64 name but the compiler freaked out.  I forgot that you can’t use an ‘=’ in variable names or function names.  The ‘=’ sign is an assignment operator and it confuses the compiler.  So after we load our MALCHO.DLL, we can simply change the function name to getPassord in OllyDbg before we allow the program to call GetProcAddress:

loading_malcho_dll

changing_malcho_function_name

And it loaded!

getPassword_worked

Continuing to step through the program:

gets_decrypts_resource_133

crack_me_2 has now extracted and decrypt the resource 133 BMP and is going to drop it as 2B0.tmp.  I let the Olly run the program to completion and grabbed our new file.  I tried changing the extension to .exe to run it but that did not work.  I tried loading it in PE Explorer but it told me that 2B0 was not a legitimate PE file so I loaded 2B0 into HexWorkshop:

2B0_tmp_is_a_java_class_file

I recognized the magic bytes: CAFEBABE.  This is a java class file.  A java class file called Bible.  And you can see the print statement strings containing the most famous quote in the movie Pulp Fiction:

all_easter_eggs_found

So it seems that we have found all the Easter eggs.  Is that all there is?  I don’t know for sure.  There are still parts of the crack_me_2 code that I have not examined in complete detail.  I did see calls to some registry querying and other registry functions.  Additionally, I found some other anti-debugging checks later in the code, and some of the code doesn’t disassemble correctly; Olly thinks its a bunch of character bytes.  If there is nothing more than why would someone go to all the bother of calling Windows registry functions, add and occlude more code if not to have it do something.  I speak from experience that programmers are generally lazy.  I don’t mean that as a derogatory remark.  Programmers purposely do not add any functionality to a program that will never be used.  Suffice it to say, there may be more to this saga.  After all, I have not tried to put any other Pulp Fiction quotes other than “I did it!” in the second textbox of the starting dialog box.  We could attempt to try all possible Pulp Fiction quotes but there are a lot of them and I could spend the rest of my natural life trying them all.  And since I am lazy I am not going to do that.

Whenever I have some spare time, I will continue to poke around.  If I find anything new, I will post those results here.

ESET Challenge Part 2

10 Friday Jan 2014

Posted by Eric Hokanson in Malware RE

≈ 4 Comments

Tags

ESET ChallengeME 2013, Malware Analysis, Malware Reverse Engineering, Reverse Engineering, Reverse Engineering Puzzles

In part one of the ESET CrackMe 2013 challenge, we:

  • Did some initial triaging and string analysis in order to try and deduce what the sample may do.
  • We discovered that it was packed using a UPX packer.
  • We unpacked the sample by hand using OllyDbg and ImportRec.
  • Then analyzed the strings of our unpacked sample to see if we could now determine this samples potential capabilities.

Today, we are going to test our theory by actually running and debugging the EsteCrackMe3013 sample.  Remember from part one, we did observe some indications of anti-debugging in our string analysis, so it is likely that we will have to try to circumvent those.

First, let’s load our unpacked CrackMe sample into IDA Pro and let it take us to the starting point of the program.  A little ways down you can see a series of MOV instructions.  This is a long block of move instructions, starting at 0x404679 and ending at 0x407A63:

Screen Shot 1

These look like ASCII characters and if we highlight each character in IDA and hit the ‘r’ key, it will reveal the actual character:

Screen Shot 2

This looks like it might be the message we see printed to the console that we see when we first run the program:

crackme_firs_run

I see an asterisk, some characters, and lots and lots of spaces.  I could be hitting the ‘r’ key all day, and it appears that these characters are being loaded “randomly” into random locations.  Perhaps they are scrambling the start up message to make static analysis harder.  If we set a break point in IDA after all these MOV instructions, then highlight on of the move instructions, right click and jump in a new hex window, we can see:

Screen Shot 3

Screen Shot 4

And that is exactly what all those MOV instructions are doing.  Moving the “scrambled” start up message into memory.  It also explains why we were seeing lots of spaces.

At the end of the MOV instructions, if scroll down, we see calls to SetConsoleCursorPositon, SetConsoleTitle, and WriteConsole.  This is the part of the code that retrieves the start up message lines from memory and prints them to the screen.  Once we scroll past the routines that print the start message to the console, that is where things start getting interesting:

Screen Shot 5

After writing the start up message to the console, the program immediately calls a subroutine located at 0x402998.  This routine checks if we hit the ‘Enter’ key to continue.  Once we hit the enter key, the program calls the subroutine on the next line.  At first, I thought this was a TLS callback because I saw evidence of TlsIndex, and calls to TlsGetValue.  There appears to be a place for a callback function.  However, after hitting CTL-E in IDA, the only starting point I saw was the program’s starting point.  I did not see evidence of a TlsCallbackEntry point.  I did not spend too much time investigating this subroutine, but, whether a callback is called or not, it appears that the program makes calls to non-returnable functions.  These functions eventually cause the program to wend its way to a FreeLibrary call and then eventually to an ExitProcess call.  My guess is, that during the normal flow of the program, we call this routine and eventually terminate the program.  This subroutine, however, does not prevent us from stepping over this function during a debug session.  But if we do, notice that the program loads some value from location 0x40810C, and then calls OutPutDebugString.  What do you bet, EAX contains that ‘s%s%s%s%s%s%s…” string sequence we saw in our string analysis in part one?

Screen Shot 6

Sure enough, that is exactly what EAX contains.  So, if we step over subroutine sub_40256C, and get to location 0x407BD92, then the program knows it must be in a debugger.  It attempts to leverage an old OllyDbg vulnerability that will crash Olly.  We can change EAX to 0 to get past that issue, or use a newer version of Olly, or IDA’s debugger.

After surviving our first anti-debugging trick, we now see that there are calls to LoadLibrary, but we don’t know the library’s name because it is disguised at offset dword_407F18:

Screen Shot 7

I suspect, the authors are trying to hide the name of the library loaded.  If we highlight, and hit the ‘a’ key, IDA will resolve the above array of numbers to an ASCII string:

Screen Shot 8

We do this to all the obfuscated library names, and we can begin to see clearly what is being loaded:

Screen Shot 2014-01-10 at 11.56.24 AM

URLMON and WININET libraries, functions for checking for Internet connections, and to download a file from some location.  We also see strings for IsDebuggerPresent and scrolling down a little further we also calls to GetTickCount, and checks to the file segment fs:18h.  These are all known techniques for checking to see if the program is in a debugger.  If it is, the program can simply terminate the process leaving us high and dry.

Screen Shot 2014-01-10 at 12.00.39 PM

We will need to deal with these anti-debugging techniques.  Before we start debugging, lets continue our static analysis and scroll down to find some other interesting things such as:

Screen Shot 2014-01-10 at 12.05.07 PM

I am not sure what that is.  Junk code?  The hexidecimal digits look like ASCII symbols.  If we highlight and press the ‘r’ key:

Screen Shot 2014-01-10 at 12.10.16 PM

Interesting!  A key of some sort?  Scrolling further down, there are more checks for debugging and calls to GetTickCount, and jumps to ExitProcess if a debugger is detected.  But eventually, we arrive to a series of code blocks that call WriteConsole.  What other messages are there?  Our next goal is to start the debugger, and when ever we come to a check for a debugger, change the jump value in the control flags to avoid ending the process.  Lets see if we can get to these WriteConsole calls and see what these messages are.

After a couple of attempts, I finally get to the first WriteConsole call and we see the message:

Screen Shot 2014-01-10 at 12.44.42 PM

Continuing, stepping through to the next WriteConsole call, we get our second message:

Screen Shot 2014-01-10 at 12.48.02 PM

We continue stepping through to the third call to WriteConsole, and our third hidden message is revealed:

Screen Shot 2014-01-10 at 12.52.21 PM

Looks like there is a second leg to our quest!  Ok.  We got pretty far today.  It may be a good idea to re-run this program in our debugger, just like we did above, but this time run procmon and fakenet to see if this program did anything else behind the scenes such as write to the registry, and download any files.

This was a lot of fun and I learned some new tricks in IDA Pro that will be useful in my new position as a malware analyst and researcher.  The next step is to visit the site, and get the second part of the challenge, and repeat the process.  But that we will have to do another day.

ESET 2013 Challenge

08 Wednesday Jan 2014

Posted by Eric Hokanson in Malware RE, Uncategorized

≈ 3 Comments

Tags

ESET ChallengeME 2013, Malware Analysis, Malware Reverse Engineering

UPDATE 20160114:  The links to the ESET ChallengeMe 2013 sample are now outdated.  I have attached the samples to this post and you can get them here: Crackmes.  Password: eset.  Enjoy!

E

==============================

I was introduced to ESET’s ChallengeMe 2013 by Yashar Mahmoudnia, a member of the Reverse Engineering Malware Research group on LinkedIn.   You can download the challenge here. Before reading the solution, I wanted to try my hand at the challenge and brush up on my skills.  Then I can compare my results to see how I did.   I am half-way through the challenge and here is what I have learned so far.

Step one:  Triage analysis

When you first run the program you get:

crackme_firs_run

When you hit enter it quits.   Next, I ran the sample through a strings program:

crackme_strings

Not very many human readable strings.  Looks like a bunch of garbage.  This is an indication of packing or encryption.  I used a tool called PEiD to try to identify the packer.  At first under normal scan, PEiD said it found nothing. So I tried the deepscan:

crackme_peid

Packed with UPX.

step two: unpacking

We could try the program upx -d to unpack, however, I am going to unpack it by hand.  I am going to use OllyDbg to load the program, step (F7) past the PUSHAD (you may want to make sure you have the anti-debug plugins installed to hide the debugger in case the sample uses anti-debugging techniques) instruction and then set a hardware breakpoint on the ESP register on access.   Then run the program (F9) and when the breakpoint is hit, you should be close to the POPAD instruction.  Close by should be the programs Original Entry Point (OEP):

crackme_unpack_OEP

We just paused after the POPAD and a few lines down we see a really far, unconditional jump from our present location at 0x4122E6 to 0x40463C.  That is our OEP.  Set the cursor to that JMP instruction and hit F4 to run to position.  Then hit F7 to make the jump to the OEP and voila — this looks like source code:

crackme_unpacked

Leave the program paused here for now.  This won’t run as is.  We have to first rebuild the imports with ImportRec.   We will need the Relative Virtual Address (RVA): in our OEP address of 0x40463C that is the 463C part.  Next, we need the RVA of the import table.  In Olly, scroll down until you see something that looks like:

crackme_import_table

The table starts at address 0x40C074 and if you keep scrolling down to the end of the table, you will find that it ends at 0x40C150.  The RVA of the table is C074 and if we subtract that from the C150 we get the size of the table: 0xDC (220 bytes).  This is all the information we will need to use ImportRec.

Attach to the running process with ImportRec, the right click the big middle, white box and go to “Advanced Commands”, then “Select Code Sections”, and accept the defaults.  Now press the full dump button to dump the process from memory.  Click “Ok” to close the dialog boxes.

In ImportRec’s “IAT Infos needed” section type the 463C in the OEP box, C074 in the RVA box, and DC in the size box.  Click on the “IAT Autosearch” button.  If we did this right, it should find something:

crackme_importrec1

Click OK, then click Get Imports.  The big middle white box should have no errors.  Next click on the Fix Dump button and click on the dump file that ImportRec created for us earlier.  ImportRec will create a esetcrackme2013_dump_.exe file.  You should be able to run that file and it should work just like the original packed sample, displaying the same start up message, and exiting when you hit the enter key.

Now we should be able to examine the binary’s strings to get a first impression of what this program might do:

crackme_unpacked_strings

I see some Windows Registry key strings HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run, and HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\SharedAccess.  This sample may set these keys to persist between power cycles.  There are strings to URLMON.DLL functions that check an Internet connection and to download a file.  I see an URL to http://www.eicar.org, so this thing may be trying to download an eicar.com file.  Also note the long string sequence highlighted in the string snapshot above.  That looks like the string sequence used to leverage an old OllyDbg vulnerability.  I see other possible anti-debugging strings such as GetTickCount, and IsDeguggerPresent.

Upon first impression, it looks like this program might be some sort of downloader or dropper, but the only way to know for sure is to attempt to debug it.  See if we can coax the thing to continue even after we hit the enter key in order to observe what its true intent is.  I will write up those results and post them in a few days.

EDIT TO ADD 20140109: Yashar posted his teammate’s write up of the solution.

EDIT TO CORRECT 20140109: Corrected the spelling of Yashar’s name.

Subscribe

  • Entries (RSS)
  • Comments (RSS)

Archives

  • May 2016
  • May 2015
  • April 2015
  • March 2015
  • September 2014
  • August 2014
  • June 2014
  • May 2014
  • April 2014
  • March 2014
  • February 2014
  • January 2014
  • December 2013
  • November 2013

Categories

  • Alan Turing
  • Algorithms
  • Apollo 17
  • C Programming
  • Christmas
  • Computer Programming
  • Computer Science
  • Computer Security
  • Current Events
  • Cyber Security Research
  • Education
  • Freedom of choice
  • Freewill
  • Hacking
  • Holidaze
  • Learning
  • Malware RE
  • Math
  • NASA
  • Pen-testing
  • Philosophy
  • Pi Day
  • procrastination
  • Programming
  • Python
  • Quine programs
  • Quotes
  • Random Stuff
  • Research
  • Reverse Engineering
  • Shopping
  • Smithsonian National Air and Space Museum
  • Software Development
  • Star Wars
  • Success
  • Uncategorized

Meta

  • Register
  • Log in

Blog at WordPress.com.

  • Follow Following
    • Eric Hokanson
    • Join 44 other followers
    • Already have a WordPress.com account? Log in now.
    • Eric Hokanson
    • Customize
    • Follow Following
    • Sign up
    • Log in
    • Report this content
    • View site in Reader
    • Manage subscriptions
    • Collapse this bar
 

Loading Comments...