Join the discord

HackThisSite.org - Application challenges

19 Nov, 2017 14:13
This is something I was planning for a long time.
THIS ARTICLE IS A GIANT SPOILER!
If you haven't completed the achievements already but plan to do it some day, you should stop reading it.

There - you've been warned.

I solved these challenges long ago. In matter of fact I solved them so long ago, I totally forgot some of them and basically did them as it was my first time.
One thing I should mention is that really tried to stay on point without falling into too much details. Still, some of the challenges got a bit chunky but oh well, you know me.

For all of the challenges I've used the following approach:

- PE forensic to determine the compiler/linker and pick the right tool for further analysis
- Look for the "Invalid password" message
- Look around the imported functions for StringCompare, strcmp, memcmp, etc.
- Real time debugging

To accomplish this task, I've used the following tools:

- PEiD v0.95 - Discontinued development, but still do the job done
- Detect it Easy v1.01 - Or simply DiE is the natural successor of PEiD
- TrID v2.24 - File Identifier
- x86dbg v25 - the better ollydbg
- HxD v1.7.7.0 - my choice of a HEX editor
- IDA v6.6 - Interactive disassembler
- VBReFormer v5.0 - it's what I got
- whatever I forgot to mention







Application Challenge #1 (Windows)


Target: app1win.exe
Requirement: Find the password
Difficulty: easy

Here's what I have here:


According to DiE, the application is linked with "Microsoft Linker(3.0)[EXE32]", but no compiler is detected:


PEiD wasn't helpful at all:


However, TrID nailed it:


I intentionally wrote an article about reverse engineering REALbasic applications some time ago.
Now I can use the IDAPython script to rebuild it and analyse it further in IDA. You can refer to the article if something is unclear.

Moving on.
After loading app1win.exe in IDA, running the rebuilder script, and using IDA's decompiler, I'm here:
OVERLAY entry point, IDA decompileint main_overlay() {
    RuntimeInit();
    sub_570C9C();
    RuntimeRegisterFileType(&prm_0002, &prm_0001, &prm_0000, 0);
    sub_57083F();
    sub_570B19();
    RuntimeRun();
    var_0001 = 0;
    RuntimeUnlockObject(0);
    RuntimeExit();
    return 0;
}

Now, the named functions like RuntimeInit(), RuntimeRegisterFileType(), etc. are part of REALbasic's Framework API, so I'll use them as milestones, without tracing inside them.
The parameters you see around, starting with "prm_" is user defined data, and I'll watch them closer. Also, the procedures starting with "sub_" are part of the user code, so I'll trace these too.

So, let's see what we have here.
Starting with RuntimeInit(), then RuntimeRegisterFileType(), some unknown procedures, RuntimeRun() and RuntimeExit() in the end.
Logically I should be interested in everything before RuntimeRun() and in the current case this is sub_570B19():
OVERLAY_EP->>sub_570B19(), IDA decompileint sub_570B19() {
    // trimmed

    RuntimeStackCheck();
    RuntimeUnlockObject(0);
    v2 = sub_56C5E7();
    if ( !v2 )
        RuntimeNilObject();
    WindowForceUpdateFrontBuffer(v2);
    RuntimeUnlockObject(v2);
    return 0;
}

Here, we have RuntimeUnlockObject(), some unknown procedure sub_56C5E7() and WindowForceUpdateFrontBuffer(), so we go inside sub_56C5E7():
OVERLAY_EP->sub_570B19()->sub_56C5E7(), IDA decompileint sub_56C5E7() {
    // trimmed

    RuntimeStackCheck();
    RuntimeUnlockObject(0);
    RuntimeLockUnlockObjects(0, 0);
    RuntimeUnlockObject(0);
    v0 = sub_562716(&prm_0139);
    v6 = RuntimeNewObject(v0);
    WindowDefaultConstructor(v6);
    RuntimeLockUnlockObjects(v6, 0);
    RuntimeUnlockObject(0);
    v1 = sub_562716(v4);
    v5 = RuntimeCheckCast(v6, v1);
    RuntimeLockObject(v5);
    RuntimeUnlockObject(v6);
    RuntimeUnlockObject(0);
    RuntimeUnlockObject(v6);
    RuntimeUnlockObject(v5);
    return v5;
}

Again, lost of framework calls, and a unknown call sub_562716(), taking prm_0139 as parameter.
Checking what prm_0139 holds shows I'm on the right track:
prm_0139, IDA disassemblyOVERLAY:00571541 prm_0139        struc_PRM <1, offset str_0139, 9, 7, 8000100h>
OVERLAY:00571555 str_0139        db 7,'Window1',0,0,0,0

This is obviously the window constructor procedure, so I'm going in:
OVERLAY_EP->sub_570B19()->sub_56C5E7()->sub_562716(), IDA decompileint sub_562716() {
// trimmed

    RuntimeStackCheck();
    if (!var_0032) {
        v0 = RuntimeLookupClass(&prm_0079);
        var_0032 = RuntimeNewClass(v0, 14, 0, 21);
        *(_DWORD *)(var_0032 + 4) = &prm_0139;
        RuntimeLockString(&prm_0139);
        v1 = var_0032;
        *(_DWORD *)(var_0032 + 16) = 0;
        *(_DWORD *)(v1 + 12) = 140;
        *(_DWORD *)(v1 + 20) = &unk_5626D8;
        *(_DWORD *)(v1 + 24) = &unk_5626F7;
        v2 = *(_DWORD *)(v1 + 32);
        *(_DWORD *)v2 = &unk_562CE7;
        *(_DWORD *)(v2 + 4) = &prm_0081;
        RuntimeLockString(&prm_0081);
        *(_DWORD *)(v2 + 8) = aUlBL;
        *(_DWORD *)(v2 + 12) = &prm_0082;
        RuntimeLockString(&prm_0082);
        *(_DWORD *)(v2 + 16) = &unk_563AFF;
        *(_DWORD *)(v2 + 20) = &prm_0083;
        RuntimeLockString(&prm_0083);
        *(_DWORD *)(v2 + 24) = &unk_563DD6;
        *(_DWORD *)(v2 + 28) = &prm_0084;
        RuntimeLockString(&prm_0084);
        *(_DWORD *)(v2 + 32) = &unk_5640AD;
        *(_DWORD *)(v2 + 36) = &prm_0085;
        RuntimeLockString(&prm_0085);
        *(_DWORD *)(v2 + 40) = &unk_564384;
        *(_DWORD *)(v2 + 44) = &prm_0086;
        RuntimeLockString(&prm_0086);
        *(_DWORD *)(v2 + 48) = &unk_56465B;
        *(_DWORD *)(v2 + 52) = &prm_0087;
        RuntimeLockString(&prm_0087);
        *(_DWORD *)(v2 + 56) = &unk_56AD50;
        *(_DWORD *)(v2 + 60) = &prm_0088;
        RuntimeLockString(&prm_0088);
        *(_DWORD *)(v2 + 64) = &unk_56B027;
        *(_DWORD *)(v2 + 68) = &prm_0089;
        RuntimeLockString(&prm_0089);
        *(_DWORD *)(v2 + 72) = &unk_56B3C7;
        *(_DWORD *)(v2 + 76) = &prm_0090;
        RuntimeLockString(&prm_0090);
        *(_DWORD *)(v2 + 80) = &unk_56B767;
        *(_DWORD *)(v2 + 84) = &prm_0091;
        RuntimeLockString(&prm_0091);
        *(_DWORD *)(v2 + 88) = &unk_56BB07;
        *(_DWORD *)(v2 + 92) = &prm_0092;
        RuntimeLockString(&prm_0092);
        *(_DWORD *)(v2 + 96) = &unk_56BEA7;
        *(_DWORD *)(v2 + 100) = &prm_0093;
        RuntimeLockString(&prm_0093);
        *(_DWORD *)(v2 + 104) = &unk_56C247;
        *(_DWORD *)(v2 + 108) = &prm_0094;
        RuntimeLockString(&prm_0094);
        v3 = *(_DWORD *)(var_0032 + 40);
        *(_DWORD *)v3 = &unk_562CE7;
        *(_DWORD *)(v3 + 4) = &prm_0095;
        RuntimeLockString(&prm_0095);
        *(_DWORD *)(v3 + 8) = aUlBL;
        *(_DWORD *)(v3 + 12) = &prm_0096;
        RuntimeLockString(&prm_0096);
    }
    return var_0032;
}
This looks promising.

Because it's the Window constructor procedure, I expect to find things like creating window controls, static texts, menus, buttons, button actions and so on.
Since the application consists of a text field and a button, i'm interested in anything related to grabbing data from a text field and button actions.
Also, note that these variables starting with "unk_" are procedures that weren't resolved automatically by IDA, so I'll basically have to go to the said address and press 'C' to convert it into code.

The logic in the above code is pretty simple.
Basically first we crate a Class using RuntimeNewClass(), then assign procedures and parameters to it like this:
Procedures assignment, IDA decompile*(_DWORD *)v2 = &unk_562CE7;           // Procedure 1
*(_DWORD *)(v2 + 4) = &prm_0081;       // Procedure 1 parameter
RuntimeLockString(&prm_0081);
*(_DWORD *)(v2 + 8) = aUlBL;           // Procedure 2
*(_DWORD *)(v2 + 12) = &prm_0082;      // Procedure 1 parameter
RuntimeLockString(&prm_0082);
*(_DWORD *)(v2 + 16) = &unk_563AFF;    // Procedure 3
*(_DWORD *)(v2 + 20) = &prm_0083;      // Procedure 3 parameter
RuntimeLockString(&prm_0083);

Now i can take a look at every "prm_" and take a look at the interesting ones.
The complete list of parameters here is this:
Rarameters list, IDA disassemblyOVERLAY:00571525 prm_0079        struc_PRM <1, offset str_0079, 8, 6, 600h>
OVERLAY:00571539 str_0079        db 6,'Window',0
OVERLAY:00571541 prm_0139        struc_PRM <1, offset str_0139, 9, 7, 8000100h>
OVERLAY:00571555 str_0139        db 7,'Window1',0,0,0,0
OVERLAY:00571561 prm_0081        struc_PRM <1, offset str_0081, 15h, 13h, 8000100h>
OVERLAY:00571575 str_0081        db 19,'#Layout%%o<Window1>',0,0,0,0
OVERLAY:0057158D prm_0082        struc_PRM <1, offset str_0082, 1Dh, 1Bh, 8000100h>
OVERLAY:005715A1 str_0082        db 27,'#CreateControls%%o<Window1>',0,0,0,0
OVERLAY:005715C1 prm_0083        struc_PRM <1, offset str_0083, 26h, 24h, 8000100h>
OVERLAY:005715D5 str_0083        db 36,'StaticText1%o<StaticText>%o<Window1>',0,0,0
OVERLAY:005715FD prm_0084        struc_PRM <1, offset str_0084, 1Dh, 1Bh, 8000100h>             // / create the EDIT field
OVERLAY:00571611 str_0084        db 27,'key%o<EditField>%o<Window1>',0,0,0,0                    // \
OVERLAY:00571631 prm_0085        struc_PRM <1, offset str_0085, 26h, 24h, 8000100h>
OVERLAY:00571645 str_0085        db 36,'StaticText2%o<StaticText>%o<Window1>',0,0,0
OVERLAY:0057166D prm_0086        struc_PRM <1, offset str_0086, 26h, 24h, 8000100h>             // / create the BUTTON
OVERLAY:00571681 str_0086        db 36,'PushButton1%o<PushButton>%o<Window1>',0,0,0             // \
OVERLAY:005716A9 prm_0087        struc_PRM <1, offset str_0087, 2Dh, 2Bh, 8000100h>             // / assign action to the BUTTON
OVERLAY:005716BD str_0087        db 43,'PushButton1_Action%%o<Window1>o<PushButton>',0,0,0,0    // \
OVERLAY:005716ED prm_0088        struc_PRM <1, offset str_0088, 26h, 24h, 8000100h>
OVERLAY:00571701 str_0088        db 36,'StaticText3%o<StaticText>%o<Window1>',0,0,0
OVERLAY:00571729 prm_0089        struc_PRM <1, offset str_0089, 21h, 1Fh, 8000100h>
OVERLAY:0057173D str_0089        db 31,'EditUndo%o<MenuItem>%o<Window1>',0,0,0,0
OVERLAY:00571761 prm_0090        struc_PRM <1, offset str_0090, 20h, 1Eh, 8000100h>
OVERLAY:00571775 str_0090        db 30,'EditCut%o<MenuItem>%o<Window1>',0
OVERLAY:00571795 prm_0091        struc_PRM <1, offset str_0091, 21h, 1Fh, 8000100h>
OVERLAY:005717A9 str_0091        db 31,'EditCopy%o<MenuItem>%o<Window1>',0,0,0,0
OVERLAY:005717CD prm_0092        struc_PRM <1, offset str_0092, 22h, 20h, 8000100h>
OVERLAY:005717E1 str_0092        db 32,'EditPaste%o<MenuItem>%o<Window1>',0,0,0
OVERLAY:00571805 prm_0093        struc_PRM <1, offset str_0093, 22h, 20h, 8000100h>
OVERLAY:00571819 str_0093        db 32,'EditClear%o<MenuItem>%o<Window1>',0,0,0
OVERLAY:0057183D prm_0094        struc_PRM <1, offset str_0094, 25h, 23h, 8000100h>
OVERLAY:00571851 str_0094        db 35,'FileQuit%o<QuitMenuItem>%o<Window1>',0,0,0,0
OVERLAY:00571879 prm_0095        struc_PRM <1, offset str_0095, 8, 6, 8000100h>
OVERLAY:0057188D str_0095        db 6,'Layout',0
OVERLAY:00571895 prm_0096        struc_PRM <1, offset str_0096, 10h, 0Eh, 8000100h>
OVERLAY:005718A9 str_0096        db 14,'CreateControls',0

So, I'm interested in prm_0087 associated procedure:
sub_56465B(prm_0087), IDA decompileint __cdecl sub_56465B(int a1, int a2) {
// trimmed

    RuntimeLockObject(a1);
    RuntimeLockObject(a2);
    RuntimeStackCheck();
    RuntimeUnlockString(0);
    RuntimeUnlockObject(0);
    v167 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 24))(a1);
    if ( !v167 )
        RuntimeNilObject();
    v166 = editTextGetter(v167);                                          // Get the user entered code
    RuntimeLockUnlockStrings(v166, 0);
    if ( RuntimeStringCompare(v166, &prm_0106) ) {                        // Compare the user code with whatever prm_0106 holds
        if ( RuntimeStringCompare(v166, &prm_0109) ) {                    // Compare the user code with whatever prm_0109 holds
            if ( RuntimeStringCompare(v166, &prm_0112) ) {                // Compare the user code with whatever prm_0112 holds
                if ( RuntimeStringCompare(v166, &prm_0115) ) {            // Compare the user code with whatever prm_0115 holds
                    if ( RuntimeStringCompare(v166, &prm_0118) ) {        // Compare the user code with whatever prm_0118 holds
                        if ( RuntimeStringCompare(v166, &prm_0121) ) {    // Compare the user code with whatever prm_0121 holds
                            RuntimeMsgBox(&prm_0124);                     // Throw message from prm_0124
                        } else {

// trimmed

Those RuntimeStringCompare() calls look interesting, so let's see their params:
List of RuntimeStringCompare parameters, IDA disassemblyOVERLAY:005718E9 prm_0106        struc_PRM <1, offset str_0106, 15h, 13h, 8000100h>
OVERLAY:005718FD str_0106        db 19,'0130-1414-5624-1341',0,0,0,0
;...
OVERLAY:00571975 prm_0109        struc_PRM <1, offset str_0109, 15h, 13h, 8000100h>
OVERLAY:00571989 str_0109        db 19,'3810-1941-5861-5351',0,0,0,0
;...
OVERLAY:005719A1 prm_0112        struc_PRM <1, offset str_0112, 15h, 13h, 8000100h>
OVERLAY:005719B5 str_0112        db 19,'8361-9811-5511-8134',0,0,0,0
;...
OVERLAY:005719CD prm_0115        struc_PRM <1, offset str_0115, 15h, 13h, 8000100h>
OVERLAY:005719E1 str_0115        db 19,'9484-2341-5696-5321',0,0,0,0
;...
OVERLAY:005719F9 prm_0118        struc_PRM <1, offset str_0118, 15h, 13h, 8000100h>
OVERLAY:00571A0D str_0118        db 19,'0138-4411-5902-2411',0,0,0,0
;...
OVERLAY:00571A25 prm_0121        struc_PRM <1, offset str_0121, 15h, 13h, 8000100h>
OVERLAY:00571A39 str_0121        db 19,'7692-3349-1914-4567',0,0,0,0

Trying any of these gives me this result:


Nice. I've got what I need, but let's see where that "smashthestate" came from.

The prm_0124 holds the "Sorry, you entered an incorrect serial number. Please re-enter", so that's the bad password message.
In the else statement the code goes like this:
Site code generation, IDA decompile// trimmed multiple calls to RuntimeUnlockString(0);
v30 = StringDBCSChr(115);                 // 's'
v29 = RuntimeAddString(&prm_0122, v30);   // "Contratulations! The password to this level is \'" (the typo is not mine)
RuntimeUnlockString(0);
v28 = StringDBCSChr(109);                 // 'm'
v27 = RuntimeAddString(v29, v28);
RuntimeUnlockString(0);
v26 = StringDBCSChr(97);                  // 'a'
v25 = RuntimeAddString(v27, v26);
RuntimeUnlockString(0);
v24 = StringDBCSChr(115);                 // 's'
v23 = RuntimeAddString(v25, v24);
RuntimeUnlockString(0);
v22 = StringDBCSChr(104);                 // 'h'
v21 = RuntimeAddString(v23, v22);
RuntimeUnlockString(0);
v20 = StringDBCSChr(116);                 // 't'
v19 = RuntimeAddString(v21, v20);
RuntimeUnlockString(0);
v18 = StringDBCSChr(104);                 // 'h'
v17 = RuntimeAddString(v19, v18);
RuntimeUnlockString(0);
v16 = StringDBCSChr(101);                 // 'e'
v15 = RuntimeAddString(v17, v16);
RuntimeUnlockString(0);
v14 = StringDBCSChr(115);                 // 's'
v13 = RuntimeAddString(v15, v14);
RuntimeUnlockString(0);
v12 = StringDBCSChr(116);                 // 't'
v11 = RuntimeAddString(v13, v12);
RuntimeUnlockString(0);
v10 = StringDBCSChr(97);                  // 'a'
v9 = RuntimeAddString(v11, v10);
RuntimeUnlockString(0);
v8 = StringDBCSChr(116);                  // 't'
v7 = RuntimeAddString(v9, v8);
RuntimeUnlockString(0);
v6 = StringDBCSChr(101);                  // 'e'
v5 = RuntimeAddString(v7, v6);
v4 = RuntimeAddString(v5, &prm_0123);     // "\'"
RuntimeMsgBox(v4);                        // Throws the complete message

So the valid site password is constructed character by character and appended to the "Congratulations" message.

Application Challenge #1 (UNIX)


Target: app1.unix.bin
Requirement: Find the password
Difficulty: easy

The UNIX version is compiled for FreeBSD which is... odd.

I just throw it in IDA and use the decompiler and here's what I got:
UNIX version, entry point, IDA disassemblyint __cdecl main(int argc, const char **argv, const char **envp) {
// trimmed
    
    v9 = &argc;
    std::string::string((std::string *)&v8);
    std::string::string((std::string *)&v7);
    std::string::operator=(&v7, "moo");
    std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v8);
    s2 = (char *)std::string::c_str((std::string *)&v7);
    v4 = (const char *)std::string::c_str((std::string *)&v8);
    if ( strcmp(v4, s2) == 0 ) {
        std::operator<<<std::char_traits<char>>(&std::cout, "IVannaHackHTS");
        v6 = 0;
    } else {
        v6 = 77;
    }
    std::string::~string((std::string *)&v7);
    std::string::~string((std::string *)&v8);
    return v6;
}

Couldn't be simpler than that: if the user entered code is "moo", it prints the site password "IVannaHackHTS"

Application Challenge #1 (Mac)


Target: app1mac
Requirement: Find the password
Difficulty: easy

I tried literally everything, including trying it on a genuine Mac, and yet I wasn't even able to extract the .sit file.
That being said - fuck Mac and fuck Apple.

Application Challenge #2 (Windows)


Target: app2win.exe
Requirement: Find the Password
Difficulty: easy

The second challenge looks pretty much like the first one:


Again it's a executable written in REALbasic, so you can refer to the first solution for more details.
Using the Python script in IDA, I'll fast forward to here:
OVERLAY entry point, IDA decompileint main_overlay() {
    RuntimeInit();
    sub_56F4E0();
    RuntimeRegisterFileType(&prm_0002, &prm_0001, &prm_0000, 0);
    sub_56F083();
    sub_56F35D();
    RuntimeRun();
    var_0001 = 0;
    RuntimeUnlockObject(0);
    RuntimeExit();
    return 0;
}

Same construction as before, and again I'm interested in the procedure right before RuntimeRun() - sub_56F35D():
OVERLAY_EP->sub_56F35D(), IDA decompileint sub_56F35D() {
// trimmed

    RuntimeStackCheck();
    RuntimeUnlockObject(0);
    v2 = sub_56AE2B();
    if ( !v2 )
        RuntimeNilObject();
    WindowForceUpdateFrontBuffer(v2);
    RuntimeUnlockObject(v2);
    return 0;
}

Continuing to sub_56AE2B():
OVERLAY_EP->sub_56F35D()->sub_56AE2B(), IDA decompileint sub_56AE2B() {
// trimmed

    RuntimeStackCheck();
    RuntimeUnlockObject(0);
    RuntimeLockUnlockObjects(0, 0);
    RuntimeUnlockObject(0);
    v0 = sub_5647EB(&prm_0169);
    v6 = RuntimeNewObject(v0);
    WindowDefaultConstructor(v6);
    RuntimeLockUnlockObjects(v6, 0);
    RuntimeUnlockObject(0);
    v1 = sub_5647EB(v4);
    v5 = RuntimeCheckCast(v6, v1);
    RuntimeLockObject(v5);
    RuntimeUnlockObject(v6);
    RuntimeUnlockObject(0);
    RuntimeUnlockObject(v6);
    RuntimeUnlockObject(v5);
    return v5;
}

and finally sub_5647EB():
OVERLAY_EP->sub_56F35D()->sub_56AE2B()->sub_5647EB(), IDA decompileint sub_5647EB() {
// trimmed

    RuntimeStackCheck();
    if (!var_0043) {
        v0 = RuntimeLookupClass(&prm_0110);           // "Window"
        var_0043 = RuntimeNewClass(v0, 18, 0, 21);
        *(_DWORD *)(var_0043 + 4) = &prm_0169;        // "Window1"
        RuntimeLockString(&prm_0169);
        v1 = var_0043;
        *(_DWORD *)(var_0043 + 16) = 0;
        *(_DWORD *)(v1 + 12) = 140;
        *(_DWORD *)(v1 + 20) = &unk_5647AD;
        *(_DWORD *)(v1 + 24) = &unk_5647CC;
        v2 = *(_DWORD *)(v1 + 32);
        *(_DWORD *)v2 = &unk_564ED8;
        *(_DWORD *)(v2 + 4) = &prm_0112;              // "#Layout%%o<Window1>"
        RuntimeLockString(&prm_0112);
        *(_DWORD *)(v2 + 8) = aUlBF;
        *(_DWORD *)(v2 + 12) = &prm_0113;             // "#CreateControls%%o<Window1>"
        RuntimeLockString(&prm_0113);
        *(_DWORD *)(v2 + 16) = &unk_56606E;
        *(_DWORD *)(v2 + 20) = &prm_0114;             // "StaticText1%o<StaticText>%o<Window1>"
        RuntimeLockString(&prm_0114);
        *(_DWORD *)(v2 + 24) = &unk_566345;           // / The serial number EDIT control
        *(_DWORD *)(v2 + 28) = &prm_0115;             // \ "key%o<EditField>%o<Window1>"
        RuntimeLockString(&prm_0115);
        *(_DWORD *)(v2 + 32) = &unk_56661C;
        *(_DWORD *)(v2 + 36) = &prm_0116;             // "StaticText2%o<StaticText>%o<Window1>"
        RuntimeLockString(&prm_0116);
        *(_DWORD *)(v2 + 40) = &unk_5668F3;
        *(_DWORD *)(v2 + 44) = &prm_0117;             // "PushButton1%o<PushButton>%o<Window1>"
        RuntimeLockString(&prm_0117);
        *(_DWORD *)(v2 + 48) = sub_566BCA;            // / Pressed BUTTON action
        *(_DWORD *)(v2 + 52) = &prm_0118;             // \ "PushButton1_Action%%o<Window1>o<PushButton>"
        RuntimeLockString(&prm_0118);
        *(_DWORD *)(v2 + 56) = &unk_566F60;
        *(_DWORD *)(v2 + 60) = &prm_0119;             // "status%o<StaticText>%o<Window1>"
        RuntimeLockString(&prm_0119);
        *(_DWORD *)(v2 + 64) = &unk_567237;           // / Init a SOCKET method
        *(_DWORD *)(v2 + 68) = &prm_0120;             // \ "Socket1%o<Socket>%o<Window1>"
        RuntimeLockString(&prm_0120);
        *(_DWORD *)(v2 + 72) = sub_56750E;            // / Read SOCKET data
        *(_DWORD *)(v2 + 76) = &prm_0121;             // \ "Socket1_DataAvailable%%o<Window1>o<Socket>"
        RuntimeLockString(&prm_0121);
        *(_DWORD *)(v2 + 80) = aUlB_;                 // / Connect SOCKET
        *(_DWORD *)(v2 + 84) = &prm_0122;             // \ "Socket1_Connected%%o<Window1>o<Socket>"
        RuntimeLockString(&prm_0122);
        *(_DWORD *)(v2 + 88) = &unk_569594;
        *(_DWORD *)(v2 + 92) = &prm_0123;             // "StaticText3%o<StaticText>%o<Window1>"
        RuntimeLockString(&prm_0123);
        *(_DWORD *)(v2 + 96) = &unk_56986B;
        *(_DWORD *)(v2 + 100) = &prm_0124;            // "EditUndo%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0124);
        *(_DWORD *)(v2 + 104) = &unk_569C0B;
        *(_DWORD *)(v2 + 108) = &prm_0125;            // "EditCut%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0125);
        *(_DWORD *)(v2 + 112) = &unk_569FAB;
        *(_DWORD *)(v2 + 116) = &prm_0126;            // "EditCopy%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0126);
        *(_DWORD *)(v2 + 120) = &unk_56A34B;
        *(_DWORD *)(v2 + 124) = &prm_0127;            // "EditPaste%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0127);
        *(_DWORD *)(v2 + 128) = &unk_56A6EB;
        *(_DWORD *)(v2 + 132) = &prm_0128;            // "EditClear%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0128);
        *(_DWORD *)(v2 + 136) = &unk_56AA8B;
        *(_DWORD *)(v2 + 140) = &prm_0129;            // "FileQuit%o<QuitMenuItem>%o<Window1>"
        RuntimeLockString(&prm_0129);
        v3 = *(_DWORD *)(var_0043 + 40);
        *(_DWORD *)v3 = &unk_564ED8;
        *(_DWORD *)(v3 + 4) = &prm_0130;              // "Layout"
        RuntimeLockString(&prm_0130);
        *(_DWORD *)(v3 + 8) = aUlBF;
        *(_DWORD *)(v3 + 12) = &prm_0131;             // "CreateControls"
        RuntimeLockString(&prm_0131);
    }
    return var_0043;
}

Alright, seems there's more than one places I should look.
First, as most obvious will be the button action prm_0118, associated with sub_566BCA():
sub_566BCA(prm_0118), IDA decompileint __cdecl sub_566BCA(int a1, int a2) {
    RuntimeLockObject(a1);
    RuntimeLockObject(a2);
    RuntimeStackCheck();
    RuntimeUnlockObject(0);
    v5 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
    if ( !v5 )
        RuntimeNilObject();
    staticCaptionSetter(v5, 112, &prm_0141);                    // "Status: Connecting..."
    RuntimeUnlockObject(0);
    v4 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
    if ( !v4 )
        RuntimeNilObject();
    SocketConnect(v4);

// trimmed

    return 0;
}

Nothing interesting here, so moving on.
prm_0120 is where the SOCKET is initialized and that's not interesting, so let's see the next one prm_0121 where the socket data is received - procedure sub_56750E():
sub_56750E(prm_0121), IDA decompileint __cdecl sub_56750E(int a1, int a2) {
    // trimmed
    RuntimeLockObject(a1);
    RuntimeLockObject(a2);
    RuntimeStackCheck();
    RuntimeUnlockString(0);
    if ( !a2 )
        RuntimeNilObject();
    v35 = SocketReadAll(a2);                                               // Read the socket reply data
    RuntimeLockUnlockStrings(v35, 0);
    RuntimeUnlockObject(0);
    v34 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
    if ( !v34 )
      RuntimeNilObject();
    staticCaptionSetter(v34, 112, &prm_0146);                              // 'Status: Reading data...'
    RuntimeUnlockString(0);
    RuntimeUnlockObject(0);
    v33 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 24))(a1);
    if ( !v33 )
        RuntimeNilObject();
    v32 = editTextGetter(v33);
    if (!StringInStr(0, v35, v32))        // check if the user code is contained in the data read from the socket
        goto LABEL_28;
    RuntimeUnlockString(0);
    RuntimeUnlockObject(0);
    v31 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 24))(a1);
    if ( !v31 )
        RuntimeNilObject();
    v30 = editTextGetter(v31);
    if ( StringDBCSLen(v30) == 23 )                                        // valid code should be 23 characters long
        v29 = 1;
    else
        LABEL_28:
    v29 = 0;
    if (v29) {                                                             // at this point the code is verified
        RuntimeUnlockObject(0);
        v28 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
        if ( !v28 )
            RuntimeNilObject();
        staticCaptionSetter(v28, 112, &prm_0147);                          // 'Status: Validated'

        // trimmed RuntimeUnlockString(0);

        v27 = StringDBCSChr(108);                                          // 'l'
        v26 = RuntimeAddString(&prm_0148, v27);                            // "Congratulations! The password to this level is \'"
        RuntimeUnlockString(0);
        v25 = StringDBCSChr(105);                                          // 'i'
        v24 = RuntimeAddString(v26, v25);
        RuntimeUnlockString(0);
        v23 = StringDBCSChr(98);                                           // 'b'
        v22 = RuntimeAddString(v24, v23);
        RuntimeUnlockString(0);
        v21 = StringDBCSChr(101);                                          // 'e'
        v20 = RuntimeAddString(v22, v21);
        RuntimeUnlockString(0);
        v19 = StringDBCSChr(114);                                          // 'r'
        v18 = RuntimeAddString(v20, v19);
        RuntimeUnlockString(0);
        v17 = StringDBCSChr(97);                                           // 'a'
        v16 = RuntimeAddString(v18, v17);
        RuntimeUnlockString(0);
        v15 = StringDBCSChr(116);                                          // 't'
        v14 = RuntimeAddString(v16, v15);
        RuntimeUnlockString(0);
        v13 = StringDBCSChr(105);                                          // 'i'
        v12 = RuntimeAddString(v14, v13);
        RuntimeUnlockString(0);
        v11 = StringDBCSChr(111);                                          // 'o'
        v10 = RuntimeAddString(v12, v11);
        RuntimeUnlockString(0);
        v9 = StringDBCSChr(110);                                           // 'n'
        v8 = RuntimeAddString(v10, v9);
        v7 = RuntimeAddString(v8, &prm_0149);
        RuntimeMsgBox(v7);                                                 // "\'"
        RuntimeUnlockObject(0);
        v6 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
        if ( !v6 )
            RuntimeNilObject();
        socketClose(v6);
    } else {
        RuntimeUnlockObject(0);
        v5 = (*(int (__cdecl **)(_DWORD))(*(_DWORD *)(a1 + 4) + 56))(a1);
        if ( !v5 )
            RuntimeNilObject();
        staticCaptionSetter(v5, 112, &prm_0150);     // "Status: Serial invalid"
        RuntimeMsgBox(&prm_0151);                    // "Sorry, you entered an incorrect serial number. Please re-enter"
        RuntimeUnlockObject(0);
        v4 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
        if ( !v4 )
            RuntimeNilObject();
        socketClose(v4);
    }

    // trimmed release functions

    return 0;
}

Alright I got the validation code and I got the correct password for the site - "liberation".
But where are the valid codes pulled from? I know it's a socket request, so let's see the socket connection procedure - prm_0122 and sub_568DD6():
sub_568DD6(prm_0122), IDA decompileint __cdecl sub_568DD6(int a1, int a2) {
    RuntimeLockObject(a1);
    RuntimeLockObject(a2);
    RuntimeStackCheck();
    RuntimeUnlockObject(0);
    v12 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
    if ( !v12 )
        RuntimeNilObject();
    staticCaptionSetter(v12, 112, &prm_0152);                            // 'Status: Sending request...'
    RuntimeUnlockObject(0);
    v11 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
    if ( !v11 )
        RuntimeNilObject();
    RuntimeUnlockString(0);
    RuntimeUnlockString(0);
    RuntimeUnlockString(0);
    RuntimeUnlockString(0);
    RuntimeUnlockString(0);
    v2 = StringDBCSChr(10);
    v3 = RuntimeAddString(&prm_0153, v2);                                // "GET /application/app2/keys123.txt HTTP/1.1"
    v4 = RuntimeAddString(v3, &prm_0154);                                // "Host: hackthissite.org"
    RuntimeUnlockString(0);
    v5 = StringDBCSChr(10);                                              // '\n'
    v6 = RuntimeAddString(v4, v5);
    RuntimeUnlockString(0);
    v7 = StringDBCSChr(10);                                              // '\n'
    v8 = RuntimeAddString(v6, v7);
    SocketWrite(v11, v8);

    // trimmed

    return 0;
}

A HTTP GET request builder.
The list of codes is located at http://hackthissite.org/application/app2/keys123.txt and it contains these valid codes:
valid user codes63482-74819-88456-98378
45910-18394-85113-51290
10110-19101-59111-41563
11424-74719-19578-99238
25182-28381-85611-85258
62351-12939-12481-58020
63482-74819-88456-98378
45910-18394-85113-51290
18381-21931-98680-86523
32910-21944-12391-51939
12389-16781-72893-71892
83478-91933-89823-98511

Trying any of these will work:


Application Challenge #2 (Mac)


Target: app2mac
Requirement: Find the Password
Difficulty: easy

Again, non-working Stuffit archive.

Application Challenge #3 (Windows)


Target: app3win.exe
Requirement: Find the Password
Difficulty: easy

The third challenge is again written in REALbasic and it looks like this:


This one actually is currently broken, but first things first.

Using the know-how from the previous challenges, that were also written in REALbasic, I can fast forward to the window create and process code:
sub_5647B4(), IDA decompileint sub_5647B4() {
    RuntimeStackCheck();
    if ( !var_0043 ) {
        v0 = RuntimeLookupClass(&prm_0110);             // "Window"
        var_0043 = RuntimeNewClass(v0, 18, 0, 21);
        *(_DWORD *)(var_0043 + 4) = &prm_0173;          // "Window1"
        RuntimeLockString(&prm_0173);                   // "Window1"
        v1 = var_0043;
        *(_DWORD *)(var_0043 + 16) = 0;
        *(_DWORD *)(v1 + 12) = 140;
        *(_DWORD *)(v1 + 20) = &unk_564776;
        *(_DWORD *)(v1 + 24) = &unk_564795;
        v2 = *(_DWORD *)(v1 + 32);
        *(_DWORD *)v2 = &unk_564EA1;
        *(_DWORD *)(v2 + 4) = &prm_0112;                 // "#Layout%%o<Window1>"
        RuntimeLockString(&prm_0112);
        *(_DWORD *)(v2 + 8) = aUlBF;
        *(_DWORD *)(v2 + 12) = &prm_0113;                // "#CreateControls%%o<Window1>"
        RuntimeLockString(&prm_0113);
        *(_DWORD *)(v2 + 16) = &unk_566037;
        *(_DWORD *)(v2 + 20) = &prm_0114;                // "StaticText1%o<StaticText>%o<Window1>"
        RuntimeLockString(&prm_0114);
        *(_DWORD *)(v2 + 24) = &unk_56630E;
        *(_DWORD *)(v2 + 28) = &prm_0115;                // "key%o<EditField>%o<Window1>"
        RuntimeLockString(&prm_0115);
        *(_DWORD *)(v2 + 32) = &unk_5665E5;
        *(_DWORD *)(v2 + 36) = &prm_0116;                // "StaticText2%o<StaticText>%o<Window1>"
        RuntimeLockString(&prm_0116);
        *(_DWORD *)(v2 + 40) = &unk_5668BC;
        *(_DWORD *)(v2 + 44) = &prm_0117;                // "PushButton1%o<PushButton>%o<Window1>"
        RuntimeLockString(&prm_0117);
        *(_DWORD *)(v2 + 48) = aUlB;
        *(_DWORD *)(v2 + 52) = &prm_0118;                // "PushButton1_Action%%o<Window1>o<PushButton>"
        RuntimeLockString(&prm_0118);
        *(_DWORD *)(v2 + 56) = &unk_566F29;
        *(_DWORD *)(v2 + 60) = &prm_0119;                // "status%o<StaticText>%o<Window1>"
        RuntimeLockString(&prm_0119);
        *(_DWORD *)(v2 + 64) = &unk_567200;
        *(_DWORD *)(v2 + 68) = &prm_0120;                // "Socket1%o<Socket>%o<Window1>"
        RuntimeLockString(&prm_0120);
        *(_DWORD *)(v2 + 72) = sub_5674D7;
        *(_DWORD *)(v2 + 76) = &prm_0121;                // "Socket1_DataAvailable%%o<Window1>o<Socket>"
        RuntimeLockString(&prm_0121);
        *(_DWORD *)(v2 + 80) = sub_568DFE;
        *(_DWORD *)(v2 + 84) = &prm_0122;                // "Socket1_Connected%%o<Window1>o<Socket>"
        RuntimeLockString(&prm_0122);
        *(_DWORD *)(v2 + 88) = &unk_56986F;
        *(_DWORD *)(v2 + 92) = &prm_0123;                // "StaticText3%o<StaticText>%o<Window1>"
        RuntimeLockString(&prm_0123);
        *(_DWORD *)(v2 + 96) = &unk_569B46;
        *(_DWORD *)(v2 + 100) = &prm_0124;               // "EditUndo%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0124);
        *(_DWORD *)(v2 + 104) = &unk_569EE6;
        *(_DWORD *)(v2 + 108) = &prm_0125;               // "EditCut%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0125);
        *(_DWORD *)(v2 + 112) = &unk_56A286;
        *(_DWORD *)(v2 + 116) = &prm_0126;               // "EditCopy%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0126);
        *(_DWORD *)(v2 + 120) = &unk_56A626;
        *(_DWORD *)(v2 + 124) = &prm_0127;               // "EditPaste%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0127);
        *(_DWORD *)(v2 + 128) = &unk_56A9C6;
        *(_DWORD *)(v2 + 132) = &prm_0128;               // "EditClear%o<MenuItem>%o<Window1>"
        RuntimeLockString(&prm_0128);
        *(_DWORD *)(v2 + 136) = &unk_56AD66;
        *(_DWORD *)(v2 + 140) = &prm_0129;               // "FileQuit%o<QuitMenuItem>%o<Window1>"
        RuntimeLockString(&prm_0129);
        v3 = *(_DWORD *)(var_0043 + 40);
        *(_DWORD *)v3 = &unk_564EA1;
        *(_DWORD *)(v3 + 4) = &prm_0130;
        RuntimeLockString(&prm_0130);                    // "Layout"
        *(_DWORD *)(v3 + 8) = aUlBF;
        *(_DWORD *)(v3 + 12) = &prm_0131;                // "CreateControls"
        RuntimeLockString(&prm_0131);
    }
    return var_0043;
}

Again sockets are involved, and I already have experience with those, so let's directly see what Socket1_DataAvailable at sub_5674D7() does:
Socket1_DataAvailable procedure, IDA decompileint __cdecl sub_5674D7(int a1, int a2) {
    // trimmed
    RuntimeLockObject(a1);
    RuntimeLockObject(a2);
    RuntimeStackCheck();
    RuntimeUnlockString(0);
    if ( !a2 )
        RuntimeNilObject();
    v35 = SocketReadAll(a2);
    RuntimeLockUnlockStrings(v35, 0);
    RuntimeUnlockObject(0);
    v34 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
    if ( !v34 )
        RuntimeNilObject();
    staticCaptionSetter(v34, 112, &prm_0154);                               // "Status: Reading data..."
    if ( StringInStr(0, v35, &prm_0147) ) {                                 // is the server response "true" ?
        RuntimeUnlockObject(0);
        v33 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
        if ( !v33 )
            RuntimeNilObject();
        staticCaptionSetter(v33, 112, &prm_0148);                           // "Status: Validated"
        // trimmed RuntimeUnlockString(0);
        v32 = StringDBCSChr(102);                                           // 'f'
        v31 = RuntimeAddString(&prm_0149, v32);                             // "Contratulations! The password to this level is \'"
        RuntimeUnlockString(0);
        v30 = StringDBCSChr(105);                                           // 'i'
        v29 = RuntimeAddString(v31, v30);
        RuntimeUnlockString(0);
        v28 = StringDBCSChr(114);                                           // 'r'
        v27 = RuntimeAddString(v29, v28);
        RuntimeUnlockString(0);
        v26 = StringDBCSChr(101);                                           // 'e'
        v25 = RuntimeAddString(v27, v26);
        RuntimeUnlockString(0);
        v24 = StringDBCSChr(121);                                           // 'y'
        v23 = RuntimeAddString(v25, v24);
        RuntimeUnlockString(0);
        v22 = StringDBCSChr(111);                                           // 'o'
        v21 = RuntimeAddString(v23, v22);
        RuntimeUnlockString(0);
        v20 = StringDBCSChr(117);                                           // 'u'
        v19 = RuntimeAddString(v21, v20);
        RuntimeUnlockString(0);
        v18 = StringDBCSChr(114);                                           // 'r'
        v17 = RuntimeAddString(v19, v18);
        RuntimeUnlockString(0);
        v16 = StringDBCSChr(98);                                            // 'b'
        v15 = RuntimeAddString(v17, v16);
        RuntimeUnlockString(0);
        v14 = StringDBCSChr(111);                                           // 'o'
        v13 = RuntimeAddString(v15, v14);
        RuntimeUnlockString(0);
        v12 = StringDBCSChr(115);                                           // 's'
        v11 = RuntimeAddString(v13, v12);
        RuntimeUnlockString(0);
        v10 = StringDBCSChr(115);                                           // 's'
        v9 = RuntimeAddString(v11, v10);
        v8 = RuntimeAddString(v9, &prm_0150);                               // "\'"
        RuntimeMsgBox(v8);
        RuntimeUnlockObject(0);
        v7 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
        if ( !v7 )
            RuntimeNilObject();
        socketClose(v7);
    } else if ( StringInStr(0, v35, &prm_0151) ) {                          // is the server response "false" ?
        RuntimeUnlockObject(0);
        v6 = (*(int (__cdecl **)(_DWORD))(*(_DWORD *)(a1 + 4) + 56))(a1);
        if ( !v6 )
            RuntimeNilObject();
        staticCaptionSetter(v6, 112, &prm_0152);    // "Status: Serial invalid"
        RuntimeMsgBox(&prm_0153);                   // "Sorry, you entered an incorrect serial number. Please re-enter."
        RuntimeUnlockObject(0);
        v5 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
        if ( !v5 )
            RuntimeNilObject();
        socketClose(v5);
    } else {
        RuntimeUnlockObject(0);
        v4 = (*(int (__cdecl **)(_DWORD))(*(_DWORD *)(a1 + 4) + 56))(a1);
        if ( !v4 )
            RuntimeNilObject();
        staticCaptionSetter(v4, 112, &prm_0154);                            // "Status: Reading data..."
    }
    // trimmed
    return 0;
}

I got the site's password again, but let's check the validation process.
The server response now is not a set of valid codes to be checked against the user entered code, but a boolean "true" or "false" value.
Let's see where's the validator URL, by checking Socket1_Connected procedure - sub_568DFE():
Socket1_Connected procedure, IDA decompileint __cdecl sub_568DFE(int a1, int a2) {
    // trimmed
    RuntimeLockObject(a1);
    RuntimeLockObject(a2);
    RuntimeStackCheck();
    RuntimeUnlockObject(0);
    v16 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
    if ( !v16 )
        RuntimeNilObject();
    staticCaptionSetter(v16, 112, &prm_0155);                         // "Status: Sending request..."
    RuntimeUnlockObject(0);
    v15 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
    if ( !v15 )
        RuntimeNilObject();
    // trimmed
    v14 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 24))(a1);
    if ( !v14 )
        RuntimeNilObject();
    v2 = editTextGetter(v14);
    v3 = RuntimeAddString(&prm_0156, v2);                             // "GET /missions/application/app3/auth.php?key="
    v4 = RuntimeAddString(v3, &prm_0157);                             // " HTTP/1.1"
    RuntimeUnlockString(0);
    v5 = StringDBCSChr(10);                                           // '\n'
    v6 = RuntimeAddString(v4, v5);
    v7 = RuntimeAddString(v6, &prm_0158);                             // "Host: hackthissite.org"
    RuntimeUnlockString(0);
    v8 = StringDBCSChr(10);                                           // '\n'
    v9 = RuntimeAddString(v7, v8);
    RuntimeUnlockString(0);
    v10 = StringDBCSChr(10);                                          // '\n'
    v11 = RuntimeAddString(v9, v10);
    SocketWrite(v15, v11);
    // trimmed
    return 0;
}

The HTTP request is now at http://hackthissite.org/missions/application/app3/auth.php?key=<user-key> and the response can be either "true" or "false".
However, when I click "Authenticate", the status gets stuck to "Status: Reading data...".
I fired up Wireshark to see what's going on, and here's what I got:
Request:0000   00 50 56 ea e8 4a 00 0c 29 58 d1 a4 08 00 45 00  .PV..J..)X....E.
0010   00 80 18 79 40 00 80 06 00 00 c0 a8 f1 9c c6 94  ...y@...........
0020   51 8b cb e7 00 50 e4 c2 5a ad 25 73 1b 68 50 18  Q....P..Z.%s.hP.
0030   fa f0 ca d7 00 00 47 45 54 20 2f 6d 69 73 73 69  ......GET /missi
0040   6f 6e 73 2f 61 70 70 6c 69 63 61 74 69 6f 6e 2f  ons/application/
0050   61 70 70 33 2f 61 75 74 68 2e 70 68 70 3f 6b 65  app3/auth.php?ke
0060   79 3d 00 31 32 33 31 32 33 31 32 33 20 48 54 54  y=.123123123 HTT
0070   50 2f 31 2e 31 0a 48 6f 73 74 3a 20 68 61 63 6b  P/1.1.Host: hack
0080   74 68 69 73 73 69 74 65 2e 6f 72 67 0a 0a        thissite.org..
What is this \x00 doing there?

Response:0000   00 0c 29 58 d1 a4 00 50 56 ea e8 4a 08 00 45 00  ..)X...PV..J..E.
0010   00 ce 3c 66 00 00 80 06 33 5f c6 94 51 8b c0 a8  ..<f....3_..Q...
0020   f1 9c 00 50 cb e7 25 73 1b 68 e4 c2 5b 05 50 18  ...P..%s.h..[.P.
0030   fa f0 0a af 00 00 3c 68 74 6d 6c 3e 0d 0a 3c 68  ......<html>..<h
0040   65 61 64 3e 3c 74 69 74 6c 65 3e 34 30 30 20 42  ead><title>400 B
0050   61 64 20 52 65 71 75 65 73 74 3c 2f 74 69 74 6c  ad Request</titl
0060   65 3e 3c 2f 68 65 61 64 3e 0d 0a 3c 62 6f 64 79  e></head>..<body
0070   20 62 67 63 6f 6c 6f 72 3d 22 77 68 69 74 65 22   bgcolor="white"
0080   3e 0d 0a 3c 63 65 6e 74 65 72 3e 3c 68 31 3e 34  >..<center><h1>4
0090   30 30 20 42 61 64 20 52 65 71 75 65 73 74 3c 2f  00 Bad Request</
00a0   68 31 3e 3c 2f 63 65 6e 74 65 72 3e 0d 0a 3c 68  h1></center>..<h
00b0   72 3e 3c 63 65 6e 74 65 72 3e 6e 67 69 6e 78 3c  r><center>nginx<
00c0   2f 63 65 6e 74 65 72 3e 0d 0a 3c 2f 62 6f 64 79  /center>..</body
00d0   3e 0d 0a 3c 2f 68 74 6d 6c 3e 0d 0a              >..</html>..

Boom. Ok, let's see the string inside the code:
prm_0156, IDA disassemblyOVERLAY:005709F8 prm_0156        struc_PRM <1, offset str_0156, 2Fh, 2Dh, 8000100h>
OVERLAY:00570A0C str_0156        db 45,'GET /missions/application/app3/auth.php?key=',0,0,0

The string itself is 44 bytes long, but the length is set to 45 (i actually need to change the 0x2D in the prm_0156 structure only). Seriously? It's buggy already?
Alright, I can easily patch this in HxD and try again, using my bad pass:
Request:0000   00 50 56 ea e8 4a 00 0c 29 58 d1 a4 08 00 45 00  .PV..J..)X....E.
0010   00 7f 0b 71 40 00 80 06 00 00 c0 a8 f1 9c c6 94  ...q@...........
0020   51 89 c0 81 00 50 39 23 fb ad 5e 5c 5c 89 50 18  Q....P9#..^\\.P.
0030   fa f0 ca d4 00 00 47 45 54 20 2f 6d 69 73 73 69  ......GET /missi
0040   6f 6e 73 2f 61 70 70 6c 69 63 61 74 69 6f 6e 2f  ons/application/
0050   61 70 70 33 2f 61 75 74 68 2e 70 68 70 3f 6b 65  app3/auth.php?ke
0060   79 3d 31 32 33 31 32 33 31 32 33 20 48 54 54 50  y=123123123 HTTP
0070   2f 31 2e 31 0a 48 6f 73 74 3a 20 68 61 63 6b 74  /1.1.Host: hackt
0080   68 69 73 73 69 74 65 2e 6f 72 67 0a 0a           hissite.org..

Ok, that looks better.
Response:0000   00 0c 29 58 d1 a4 00 50 56 ea e8 4a 08 00 45 00  ..)X...PV..J..E.
0010   02 cb 3d d6 00 00 80 06 2f f4 c6 94 51 89 c0 a8  ..=...../...Q...
0020   f1 9c 00 50 c0 81 5e 5c 5c 89 39 23 fc 04 50 18  ...P..^\\.9#..P.
0030   fa f0 d3 8e 00 00 48 54 54 50 2f 31 2e 31 20 33  ......HTTP/1.1 3
0040   30 31 20 4d 6f 76 65 64 20 50 65 72 6d 61 6e 65  01 Moved Permane
0050   6e 74 6c 79 0d 0a 44 61 74 65 3a 20 57 65 64 2c  ntly..Date: Wed,
0060   20 31 39 20 4a 75 6c 20 32 30 31 37 20 31 38 3a   19 Jul 2017 18:
0070   33 33 3a 32 31 20 47 4d 54 0d 0a 43 6f 6e 74 65  33:21 GMT..Conte
0080   6e 74 2d 54 79 70 65 3a 20 74 65 78 74 2f 68 74  nt-Type: text/ht
0090   6d 6c 3b 20 63 68 61 72 73 65 74 3d 69 73 6f 2d  ml; charset=iso-
00a0   38 38 35 39 2d 31 0d 0a 43 6f 6e 74 65 6e 74 2d  8859-1..Content-
00b0   4c 65 6e 67 74 68 3a 20 32 38 34 0d 0a 43 6f 6e  Length: 284..Con
00c0   6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c  nection: keep-al
00d0   69 76 65 0d 0a 4c 6f 63 61 74 69 6f 6e 3a 20 68  ive..Location: h
00e0   74 74 70 3a 2f 2f 77 77 77 2e 68 61 63 6b 74 68  ttp://www.hackth
00f0   69 73 73 69 74 65 2e 6f 72 67 2f 6d 69 73 73 69  issite.org/missi
0100   6f 6e 73 2f 61 70 70 6c 69 63 61 74 69 6f 6e 2f  ons/application/
0110   61 70 70 33 2f 61 75 74 68 2e 70 68 70 3f 6b 65  app3/auth.php?ke
0120   79 3d 31 32 33 31 32 33 31 32 33 0d 0a 53 65 72  y=123123123..Ser
0130   76 65 72 3a 20 48 61 63 6b 54 68 69 73 53 69 74  ver: HackThisSit
0140   65 20 4c 6f 61 64 20 42 61 6c 61 6e 63 65 72 0d  e Load Balancer.
0150   0a 53 74 72 69 63 74 2d 54 72 61 6e 73 70 6f 72  .Strict-Transpor
0160   74 2d 53 65 63 75 72 69 74 79 3a 20 6d 61 78 2d  t-Security: max-
0170   61 67 65 3d 33 31 35 33 36 30 30 30 3b 20 69 6e  age=31536000; in
0180   63 6c 75 64 65 53 75 62 64 6f 6d 61 69 6e 73 3b  cludeSubdomains;
0190   20 70 72 65 6c 6f 61 64 0d 0a 58 2d 43 6f 6e 74   preload..X-Cont
01a0   65 6e 74 2d 54 79 70 65 2d 4f 70 74 69 6f 6e 73  ent-Type-Options
01b0   3a 20 6e 6f 73 6e 69 66 66 0d 0a 0d 0a 3c 21 44  : nosniff....<!D
01c0   4f 43 54 59 50 45 20 48 54 4d 4c 20 50 55 42 4c  OCTYPE HTML PUBL
01d0   49 43 20 22 2d 2f 2f 49 45 54 46 2f 2f 44 54 44  IC "-//IETF//DTD
01e0   20 48 54 4d 4c 20 32 2e 30 2f 2f 45 4e 22 3e 0a   HTML 2.0//EN">.
01f0   3c 68 74 6d 6c 3e 3c 68 65 61 64 3e 0a 3c 74 69  <html><head>.<ti
0200   74 6c 65 3e 33 30 31 20 4d 6f 76 65 64 20 50 65  tle>301 Moved Pe
0210   72 6d 61 6e 65 6e 74 6c 79 3c 2f 74 69 74 6c 65  rmanently</title
0220   3e 0a 3c 2f 68 65 61 64 3e 3c 62 6f 64 79 3e 0a  >.</head><body>.
0230   3c 68 31 3e 4d 6f 76 65 64 20 50 65 72 6d 61 6e  <h1>Moved Perman
0240   65 6e 74 6c 79 3c 2f 68 31 3e 0a 3c 70 3e 54 68  ently</h1>.<p>Th
0250   65 20 64 6f 63 75 6d 65 6e 74 20 68 61 73 20 6d  e document has m
0260   6f 76 65 64 20 3c 61 20 68 72 65 66 3d 22 68 74  oved <a href="ht
0270   74 70 3a 2f 2f 77 77 77 2e 68 61 63 6b 74 68 69  tp://www.hackthi
0280   73 73 69 74 65 2e 6f 72 67 2f 6d 69 73 73 69 6f  ssite.org/missio
0290   6e 73 2f 61 70 70 6c 69 63 61 74 69 6f 6e 2f 61  ns/application/a
02a0   70 70 33 2f 61 75 74 68 2e 70 68 70 3f 6b 65 79  pp3/auth.php?key
02b0   3d 31 32 33 31 32 33 31 32 33 22 3e 68 65 72 65  =123123123">here
02c0   3c 2f 61 3e 2e 3c 2f 70 3e 0a 3c 2f 62 6f 64 79  </a>.</p>.</body
02d0   3e 3c 2f 68 74 6d 6c 3e 0a                       ></html>.

Oh for fucks sake, really? Now i need to change the domain and add "www." in front of it.
But how:
prm_0158, IDA disassemblyOVERLAY:00570A5C prm_0158        struc_PRM <1, offset str_0158, 18h, 16h, 8000100h>
OVERLAY:00570A70 str_0158        db 22,'Host: hackthissite.org',0

I need to add 4 bytes more, but there's not enough space in here... decisions, decisions.

Can't lie, I fiddle for a hour thinking of a elegant solution for this. In the end, I just updated the string, overwriting the next parameter, crossed fingers and gave it a try:


Damn, that actually worked without crash!
It's a shit solution, but it's also a working solution.

Now, I know I already got the password for the site, but why not try to fool the app, that any password is correct?
prm_0147 and prm_0151, IDA disassemblyOVERLAY:00570884 prm_0147        struc_PRM <1, offset str_0147, 6, 4, 8000100h>
OVERLAY:00570898 str_0147        db 4,'true',0,0,0
; trimmed
OVERLAY:00570928 prm_0151        struc_PRM <1, offset str_0151, 7, 5, 8000100h>
OVERLAY:0057093C str_0151        db 5,'false',0,0

Due to the dword alignment, there's enough space to flip that "true" to "false" and make the bad passwords valid, so let's give it a try:


Nice, challenge #3 is officially completed.

Application Challenge #3 (Mac)


Target: app3win.sit
Requirement: Find the Password
Difficulty: easy

And yet again, broken Stuffit archive.

Application Challenge #4 (Windows)


Target: app4win.exe
Requirement: Press the Button
Difficulty: easy

Here's what I got here:


Trying to focus on a button disables the pointed one and enables the other.
According to DiE, this challenge is written in VisualBasic:


That's fine. A standard way to enable and disable controls is using the EnableWindow() API function, so I'll set a breakpoint on it in x86dbg and try to point a button:
EnableWindow(), x86dbg75A58D02 | 8B FF             | mov edi,edi                ; EnableWindow entry point
75A58D04 | 55                | push ebp
75A58D05 | 8B EC             | mov ebp,esp
75A58D07 | 6A 62             | push 62
75A58D09 | FF 75 0C          | push dword ptr ss:[ebp+C]
75A58D0C | FF 75 08          | push dword ptr ss:[ebp+8]
75A58D0F | E8 BD 18 00 00    | call user32.75A5A5D1
75A58D14 | 5D                | pop ebp
75A58D15 | C2 08 00          | ret 8

This is a breakpoint inside kernel32.dll, so now I need to execute until RET instruction, so I end up in the app4win.exe, which is here:
EnableWindow(), x86dbg0040286D | 8B F8                | mov edi,eax
0040286F | 6A 00                | push 0
00402871 | 57                   | push edi
00402872 | 8B 0F                | mov ecx,dword ptr ds:[edi]
00402874 | FF 91 8C 00 00 00    | call dword ptr ds:[ecx+8C]    ; Call to a msvbvm60.dll wrapper over EnableWindow
0040287A | 85 C0                | test eax,eax
0040287C | DB E2                | fnclex
0040287E | 7D 12                | jge app4win.402892

That PUSH 0 looks promising, so I'll flip it to 1 and see what's going to happen:


Alright, that made the upper button to be permanently enabled but now I can't click it.
So there must be something I'm missing here. Back tracing the code upwards in IDA led me here:
IDA disassembly.text:00402590 loc_402590:                                              ; back trace to here
.text:00402590                 push    ebp
.text:00402591                 mov     ebp, esp
.text:00402593                 sub     esp, 0Ch
.text:00402596                 push    offset __vbaExceptHandler
.text:0040259B                 mov     eax, large fs:0
; more code
.text:0040247C loc_40247C:
.text:0040247C                 sub     dword ptr [esp+4], 33h           ; Create main window
.text:00402484                 jmp     loc_402590                       ; Back traced to here
.text:00402489 loc_402489:
.text:00402489                 sub     dword ptr [esp+4], 37h           ; unknown
.text:00402491                 jmp     loc_402750
.text:00402496 loc_402496:
.text:00402496                 sub     dword ptr [esp+4], 37h           ; Disable upper button
.text:0040249E                 jmp     loc_402810
.text:004024A3 loc_4024A3:
.text:004024A3                 sub     dword ptr [esp+4], 3Bh           ; unknown
.text:004024AB                 jmp     loc_402910
.text:004024B0 loc_4024B0:
.text:004024B0                 sub     dword ptr [esp+4], 3Bh           ; Disable lower button
.text:004024B8                 jmp     loc_4029D0
.text:004024BD loc_4024BD:
.text:004024BD                 sub     dword ptr [esp+4], 37h           ; unknown
.text:004024C5                 jmp     loc_402AD0
.text:004024CA loc_4024CA:
.text:004024CA                 sub     dword ptr [esp+4], 3Bh           ; unknown
.text:004024D2                 jmp     loc_403170

That looks like a event switch or something.
I've set breakpoints on these and it turns out that 0x33 is triggered when creating the main window, while 0x37 (addr 00402496) and 0x3B (addr 004024B0) are triggered when pointing the upper and respectively the bottom buttons.
The rest are unknown, so what can I do here? I can change the JMP of a button pointing to one of the unknown jumps, so I change that JMP loc_402810 to JMP loc_402750.

Now the upper button is permanently enabled. The event at 004024A3 does the same, but for the bottom button.
So, only the events at 004024BD and 004024CA are unknown. Doing the same trick, I changed the JMP loc_402810 to JMP loc_402AD0 and here's the result:


Nice, I've relocated the event that originally hide the upper button to act as if the upper button is pressed and that way I got the site's password.
So the events at 004024BD and 004024CA are handling the button press events.
Knowing that, I can now check where the site's password is constructed, by following that JMP loc_402AD0:
Site code construction ,IDA disassembly.text:00402B12                 mov     edi, ds:rtcVarBstrFromAnsi
.text:00402B18                 lea     eax, [ebp+var_28]
.text:00402B1B                 xor     esi, esi
.text:00402B1D                 push    'P'
.text:00402B1F                 push    eax
; trimmed
.text:00402C19                 call    edi ; rtcVarBstrFromAnsi
.text:00402C1B                 lea     ecx, [ebp+var_38]
.text:00402C1E                 push    'a'
.text:00402C20                 push    ecx
.text:00402C21                 call    edi ; rtcVarBstrFromAnsi
.text:00402C23                 lea     edx, [ebp+var_58]
.text:00402C26                 push    's'
.text:00402C28                 push    edx
.text:00402C29                 call    edi ; rtcVarBstrFromAnsi
.text:00402C2B                 lea     eax, [ebp+var_78]
.text:00402C2E                 push    's'
.text:00402C30                 push    eax
.text:00402C31                 call    edi ; rtcVarBstrFromAnsi
.text:00402C33                 lea     ecx, [ebp+var_98]
.text:00402C39                 push    'w'
.text:00402C3B                 push    ecx
.text:00402C3C                 call    edi ; rtcVarBstrFromAnsi
.text:00402C3E                 lea     edx, [ebp+var_B8]
.text:00402C44                 push    'o'
.text:00402C46                 push    edx
.text:00402C47                 call    edi ; rtcVarBstrFromAnsi
.text:00402C49                 lea     eax, [ebp+var_D8]
.text:00402C4F                 push    'r'
.text:00402C51                 push    eax
.text:00402C52                 call    edi ; rtcVarBstrFromAnsi
.text:00402C54                 lea     ecx, [ebp+var_F8]
.text:00402C5A                 push    'd'
.text:00402C5C                 push    ecx
.text:00402C5D                 call    edi ; rtcVarBstrFromAnsi
.text:00402C5F                 lea     edx, [ebp+var_118]
.text:00402C65                 push    ' '
.text:00402C67                 push    edx
.text:00402C68                 call    edi ; rtcVarBstrFromAnsi
.text:00402C6A                 lea     eax, [ebp+var_138]
.text:00402C70                 push    'i'
.text:00402C72                 push    eax
.text:00402C73                 call    edi ; rtcVarBstrFromAnsi
.text:00402C75                 lea     ecx, [ebp+var_158]
.text:00402C7B                 push    's'
.text:00402C7D                 push    ecx
.text:00402C7E                 call    edi ; rtcVarBstrFromAnsi
.text:00402C80                 lea     edx, [ebp+var_178]
.text:00402C86                 push    ' '
.text:00402C88                 push    edx
.text:00402C89                 call    edi ; rtcVarBstrFromAnsi
.text:00402C8B                 lea     eax, [ebp+var_198]
.text:00402C91                 push    '\''
.text:00402C93                 push    eax
.text:00402C94                 call    edi ; rtcVarBstrFromAnsi
.text:00402C96                 lea     ecx, [ebp+var_1B8]
.text:00402C9C                 push    'd'
.text:00402C9E                 push    ecx
.text:00402C9F                 call    edi ; rtcVarBstrFromAnsi
.text:00402CA1                 lea     edx, [ebp+var_1D8]
.text:00402CA7                 push    'a'
.text:00402CA9                 push    edx
.text:00402CAA                 call    edi ; rtcVarBstrFromAnsi
.text:00402CAC                 lea     eax, [ebp+var_1F8]
.text:00402CB2                 push    'y'
.text:00402CB4                 push    eax
.text:00402CB5                 call    edi ; rtcVarBstrFromAnsi
.text:00402CB7                 lea     ecx, [ebp+var_218]
.text:00402CBD                 push    't'
.text:00402CBF                 push    ecx
.text:00402CC0                 call    edi ; rtcVarBstrFromAnsi
.text:00402CC2                 lea     edx, [ebp+var_238]
.text:00402CC8                 push    'o'
.text:00402CCA                 push    edx
.text:00402CCB                 call    edi ; rtcVarBstrFromAnsi
.text:00402CCD                 lea     eax, [ebp+var_258]
.text:00402CD3                 push    'n'
.text:00402CD5                 push    eax
.text:00402CD6                 call    edi ; rtcVarBstrFromAnsi
.text:00402CD8                 lea     ecx, [ebp+var_278]
.text:00402CDE                 push    'a'
.text:00402CE0                 push    ecx
.text:00402CE1                 call    edi ; rtcVarBstrFromAnsi
.text:00402CE3                 lea     edx, [ebp+var_298]
.text:00402CE9                 push    '\''
.text:00402CEB                 push    edx
.text:00402CEC                 call    edi ; rtcVarBstrFromAnsi
.text:00402CEE                 cmp     dword_404010, esi
.text:00402CF4                 jnz     short loc_402D06
.text:00402CF6                 push    offset dword_404010
.text:00402CFB                 push    offset dword_402198
.text:00402D00                 call    ds:__vbaNew2

So that's how the password message is generated. Challenge completed!

Application Challenge #5 (Windows)


Target: app5win.exe
Requirement: Find the password
Difficulty: easy

We have a C/C++ console application here:


I find myself using the IDA's decompiler quite often lately, but it's just so useful:
Entry point, IDA decompileint __cdecl main(int argc, const char **argv, const char **envp) {
// trimmed
    v8 = 0xA67;                                                   // / Those values look interesting
    v9 = 0x6E697070;                                              // |
    v10 = 0x69727472;                                             // |
    v11 = 0x65776F70;                                             // \
    puts(aPleaseEnterThe);
    memset(v4, 0, 0x10u);
    v7 = 0;
    v6 = 0;
    v5 = 0;
    do {
        v13 = j___fgetchar();                                     // Read the user input
        v4[v7++] = v13;
    } while(v13 != 0xA && v13 && v7 < 0x10);                      // Read until ENTER is pressed (0x0A char)
                                                                  // The user input should be less than 0x10 characters
    v12 = (int)v4;
    v6 = 0;
    v5 = 3;
    while (v6 < 0xD) {                                            // The loop is limited to 0x0D characters (3 DWORDS)
        if (*(_DWORD *)(v12 + 4 * (v6 >> 2)) != *(&v8 + v5)) {    // "v12 + 4" takes a chunk of the user code as DWORD
                                                                  // and compares it to v8
            printf(aInvalidPasswor);                              // Bad password message
            return 0;
        }
        v6 += 4;                                                  // The iteration is set to 4 bytes
        --v5;
    }
    printf(aThePasswordIsS, v4);                                  // The valid password for the site is the same as the user pass
    return 0;
}

IDA's decompiler is good, but not perfect, so referring to the assembler code can throw light on the whole picture:
Entry point, IDA disassembly00401086 | 8D 45 CC                | lea eax,dword ptr ss:[ebp-34]
00401089 | 89 45 F8                | mov dword ptr ss:[ebp-8],eax
0040108C | C7 45 E0 00 00 00 00    | mov dword ptr ss:[ebp-20],0
00401093 | C7 45 DC 03 00 00 00    | mov dword ptr ss:[ebp-24],3
0040109A | EB 12                   | jmp app5win.4010AE
0040109C | 8B 4D E0                | mov ecx,dword ptr ss:[ebp-20]
0040109F | 83 C1 04                | add ecx,4
004010A2 | 89 4D E0                | mov dword ptr ss:[ebp-20],ecx
004010A5 | 8B 55 DC                | mov edx,dword ptr ss:[ebp-24]      ;/ EDX is the correct pass iterator
004010A8 | 83 EA 01                | sub edx,1                          ;|
004010AB | 89 55 DC                | mov dword ptr ss:[ebp-24],edx      ;\
004010AE | 83 7D E0 0D             | cmp dword ptr ss:[ebp-20],D        ; The loop limit is set to 0x0D
004010B2 | 73 28                   | jae app5win.4010DC                 ; if the loop limit is reached, break the loop
004010B4 | 8B 45 E0                | mov eax,dword ptr ss:[ebp-20]      ;/ EAX is the user's pass iterator
004010B7 | C1 E8 02                | shr eax,2                          ;\
004010BA | 8B 4D F8                | mov ecx,dword ptr ss:[ebp-8]       ; ECX is the user's password - "123123123"
004010BD | 8B 55 DC                | mov edx,dword ptr ss:[ebp-24]
004010C0 | 8B 04 81                | mov eax,dword ptr ds:[ecx+eax*4]   ; Take a dword from the user code
004010C3 | 3B 44 95 E8             | cmp eax,dword ptr ss:[ebp+edx*4-18]; and compares it to this value and
004010C7 | 74 11                   | je app5win.4010DA                  ; if they are equal, jumps and continues to compare the rest of it
004010C9 | 68 4C 70 40 00          | push app5win.40704C                ; otherwise prints "Invalid Password"
004010CE | E8 20 00 00 00          | call app5win.4010F3                ; that's a printf()
004010D3 | 83 C4 04                | add esp,4
004010D6 | 33 C0                   | xor eax,eax
004010D8 | EB 15                   | jmp app5win.4010EF                 ; jump to program exit
004010DA | EB C0                   | jmp app5win.40109C                 ; loop jump
004010DC | 8D 4D CC                | lea ecx,dword ptr ss:[ebp-34]
004010DF | 51                      | push ecx                           ; ECX holds the user entered password - "123123123"
004010E0 | 68 60 70 40 00          | push app5win.407060                ; format for the next printf() call is "The password is %s\n"
004010E5 | E8 09 00 00 00          | call app5win.4010F3                ; printf()
004010EA | 83 C4 08                | add esp,8
004010ED | 33 C0                   | xor eax,eax
004010EF | 8B E5                   | mov esp,ebp
004010F1 | 5D                      | pop ebp
004010F2 | C3                      | ret                                ; program exit

So, the loop is limited to 0x0D, and with every iteration it compares a DWORD of the user's pass with a DWORD of something stored at [ebp+edx*4-18].
If the DWORD values doesn't match, the "Invalid Password" message is thrown.
This means, the loop will run four times (because 0x0D/sizeof(DWORD)=3+1), therefore the valid password is 13 bytes long, stored in whatever [ebp+edx*4-18] holds.
I can easily check this in x86dbg:
Code validation, IDA disassembly004010C0 | 8B 04 81                           | mov eax,dword ptr ds:[ecx+eax*4]
004010C3 | 3B 44 95 E8                        | cmp eax,dword ptr ss:[ebp+edx*4-18]    ; tracing up to here
004010C7 | 74 11                              | je app5win.4010DA

; EIP
; eax=31333231                                   ; my bad password's first DWORD 1231
; dword ptr[ebp+edx*4-18]=[0012FF3C]=65776F70    ; the correct password's first DWORD is located at 0x0012FF3C

; Memory
; 0012FF2C  0D 00 00 00 67 0A 00 00 70 70 69 6E 72 74 72 69  ....g...ppinrtri  
; 0012FF3C  70 6F 77 65 14 FF 12 00 0A 20 40 00 88 FF 12 00  powe.ÿ... @..ÿ..    ; it's plaintext!

So the three DWORDs of the correct password are "powe", "rtri", "ppin", "g\n\x00\x00", and trying "powertripping" as correct password gives me this:


Nice, challenge completed!

By the way, there's a light obfuscation thing, where the user password iterator is incremented by 4, but then it's shifted right by 2, thus ending as a simple 0, 1, 2, 3:
Obfuscation?, IDA disassembly0040109C | 8B 4D E0                | mov ecx,dword ptr ss:[ebp-20]       ;/ Increment the iterator by 4
0040109F | 83 C1 04                | add ecx,4                           ;|
004010A2 | 89 4D E0                | mov dword ptr ss:[ebp-20],ecx       ;\
; trimmed
004010B4 | 8B 45 E0                | mov eax,dword ptr ss:[ebp-20]       ;/ Shift it right by 2
004010B7 | C1 E8 02                | shr eax,2                           ;\ this is basically equal to divide by 4
; trimmed
004010C0 | 8B 04 81                | mov eax,dword ptr ds:[ecx+eax*4]    ; use it as times 4


Application Challenge #6 (Windows)


Target: app6win.exe
Requirement: Find the password
Difficulty: easy

Another console application written in VC++:


And the IDA disassembly:
Entry oint, IDA decompileint __cdecl main(int argc, const char **argv, const char **envp) {
    // trimmed
    VirtualQuery(&loc_4010D3, &Buffer, 0x1Cu);
    v3 = Buffer.Protect & 0xFFFFFFDD;
    LOBYTE(v3) = Buffer.Protect & 0xDD | 4;
    VirtualProtect(&loc_4010D3, 0xD0u, v3, &flOldProtect);
    v8 = (int)&loc_4010D3;
    for ( i = 0; i < 0x33; ++i )
        *(_DWORD *)(v8 + 4 * i) ^= 0xBEEFCABE;
    return ((int (__stdcall *)(int, DWORD, PVOID, PVOID, DWORD, SIZE_T, DWORD, DWORD, DWORD, int, unsigned int))loc_4010D3)(
        v5,
        flOldProtect,
        Buffer.BaseAddress,
        Buffer.AllocationBase,
        Buffer.AllocationProtect,
        Buffer.RegionSize,
        Buffer.State,
        Buffer.Protect,
        Buffer.Type,
        v8,
        i);
}

That XOR decryptor routine is so obvious...
If we look at the assembly code, there's some minor code obfuscation too:
Code obfuscation, IDA disassembly.text:00401056 loc_401056:
.text:00401056                 mov     eax, [ebp+var_4]     ;/ EAX is the iterator
.text:00401059                 add     eax, 1               ;|
.text:0040105C                 mov     [ebp+var_4], eax     ;\
.text:0040105F loc_40105F:
.text:0040105F                 cmp     [ebp+var_4], 33h     ; The encrypted block is only 0x33 bytes long
.text:00401063                 jnb     short loc_40107E
.text:00401065                 mov     ecx, [ebp+var_4]
.text:00401068                 mov     edx, [ebp+var_8]
.text:0040106B                 mov     eax, [edx+ecx*4]     ; take a DWORD of the encrypted code
.text:0040106E                 xor     eax, 0BEEFCABEh      ; XOR it with that key
.text:00401073                 mov     ecx, [ebp+var_4]
.text:00401076                 mov     edx, [ebp+var_8]
.text:00401079                 mov     [edx+ecx*4], eax     ; update the decrypted DWORD
.text:0040107C                 jmp     short loc_401056
.text:0040107E loc_40107E:
.text:0040107E                 nop                          ;/ NOP "obfuscation"
                                                            ;| trimmed
.text:004010C6                 nop                          ;\
.text:004010C7                 add     esp, 4
.text:004010CA                 call    loc_4010D3           ; Call the decrypted code
.text:004010CF                 mov     esp, ebp
.text:004010D1                 pop     ebp
.text:004010D2                 retn

I wrote a simple Python script that will decrypt the code for me in IDA:
Code decryptor, IDAPythonimport idc

codeStart = 0x004010D3
codeSize = 0x33
codeKey = 0xBEEFCABE

i = 0;
while(i < codeSize):
    PatchDword(codeStart+(i*4), (Dword(codeStart+(i*4)) ^ codeKey))
    i += 1

And the code is decrypted:
Decrypted code, IDA decompileint sub_4010D3() {
    v5 = 0xA6C6163;                                                              // Correct code second DWORD
    v6 = 0x6967616D;                                                             // Correct code first DWORD
    puts(aPleaseEnterThe);
    memset(v2, 0, 0x10u);
    v4 = 0;
    i = 0;
    do {                                                                         // Read the user's input
        --stru_407088._cnt;
        if (stru_407088._cnt < 0) {
          v1 = _filbuf(&stru_407088);
        } else {
          v1 = *stru_407088._ptr;
          ++stru_407088._ptr;
        }
        v8 = v1;
        v2[v4++] = v1;
    } while (v8 != 10 && v8 && v4 < 0x10);
    v7 = (int)v2;
    for (i = 0; i < 8; i += 4) {                                                 // The correct code is 8 bytes long
        if (*(_DWORD *)(v7 + 4 * (i >> 2)) != *(&v4 + ((v4 - i) >> 2))) {        // Compare a DWORD from the user
                                                                                 // with a DWORD from the correct code
            ((void (__cdecl *)(_DWORD, int))sub_40140E)(aInvalidPasswor, v1);    // Incorrect code message
            return 0;
        }
    }
    sub_40140E(aThePasswordIsS, v2);                                             // Correct code message
    return 0;
}

That looks pretty much the same way as in the previous challenge.
Converting the two parts of the correct code from DWORDs to ASCII gives us "magi" and "cal\x0A" or just "magical":


Yep, this is it.

Application Challenge #7 (Windows)


Target: app7win.exe, encrypted.enc
Requirement: Find the password
Difficulty: medium

This one is marked as medium difficulty, and it's again a console VC++ application:


The encrypted.enc file contains this:
encrypted.enc, HxDOffset    00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000  31 4D 39 35 33 45 61 44 65 16 54 56 16 54 56 15    1M953EaDe.TV.TV.
00000010  67 84 56 23 45 67 64 56 78 96 43 57 89 54 98 75    g„V#EgdVx–CW‰T.u
00000020  98 74 32 59 82 39 F9 8A D9 8F 42 98 F4 39 79 34    .t2Y‚9щЉЩЏB.ф9y4
00000030  8E F9 A8 DF 98 AD 7F 09 8D 98 43 F2 98 A7 F0 A8    ЋщЁЯ.­..Ќ.Cт.§рЁ
00000040  DD A9 F7 6D BA F9 7B 48 7A DF 7D A9 F8 69 8E A8    Э©чmєщ{HzЯ}©шiЋЁ
00000050  7A 6B 84 E6 F8 B7 68 74 A6 6A 48 7F A6 46 FA 9F    zk„жш·ht¦jH.¦Fъџ
00000060  86 48 7A 79 A8 E6 92 38 F7 F9 76 92 38 69 82 35    †HzyЁж’8чщv’8i‚5
00000070  87 E6 F7 AD 6F 98 7A 57 8F 68 27 35 92 38 75 82    ‡жч­o.zWЏh'5’8u‚
00000080  36 52 35 A8 7F 53 29 67 89 36 24 73 25 98 36 27    6R5Ё.S)g‰6$s%.6'
00000090  36 29 87 35 87 32 63 65 93 82 56 5A 87 3A 37 67    6)‡5‡2ce“‚VZ‡:7g
000000A0  3A 67 8A 58 72 63 87 65 32 78 56 A9 87 35 A6 98    :gЉXrc‡e2xV©‡5¦.
000000B0  A6 38 76 38 75 63 29 85 68 79 32 65 87 26 53 92    ¦8v8uc)…hy2e‡&S’
000000C0  85 63 28 95 6F 98 76 3A 97 86 9A 87 56 96 39 87    …c(•o.v:—†љ‡V–9‡
000000D0  5A 65 98 A3 76 59 A3 87 6A 59 87 69 87 5A 63 98    Ze.ЈvYЈ‡jY‡i‡Zc.
000000E0  76 FA 39 87 68 7A 56 87 FA 96 87 46 A8 96 98 47    vъ9‡hzV‡ъ–‡FЁ–.G
000000F0  6A 48 96 4A 9F 84 A7 F6 87 4A 55 E9 68 7D 6F A8    jH–Jџ„§ц‡JUйh}oЁ
00000100  97 A6 F9 69 84 7A 64 98 76 93 53 98 98 26 3F 89    —¦щi„zd.v“S..&?‰
00000110  73 26 F8 72 94 F8 79 87 24 82 8F 5A 08 7D F5 A8    s&шr”шy‡$‚ЏZ.}хЁ
00000120  43 53 29 F9 52 87 3E 59 F6 32 B9 6D 8A 9F AD 78    CS)щR‡>Yц2№mЉџ­x
00000130  FA 6F D0 9A F5 DA 9F AF 29 32 98 5F 49 59 F8 7D    ъoРљхЪџЇ)2._IYш}
00000140  A5 FA D8 F6 9A 9D F9 64 29 F6 39 D6 5F 87 A6 7E    ҐъШцљќщd)ц9Ц_‡¦~
00000150  78 D9 F6 98 E7 68 7D AF 67 9A 8D F8 A6 E6 FA 90    xЩц.зh}ЇgљЌш¦жъђ
00000160  86 F7 AD F7 F0 9A 06 E8 F4 A9 7F 49 A8 76 D7 FA    †ч­чрљ.иф©.IЁvЧъ
00000170  D9 F6 7D FA D8 76 FA 98 76 F9 A8 7D FA F6 98 4F    Щц}ъШvъ.vщЁ}ъц.O
00000180  66 34 FA DF A8 76 98 47 6F 84 79 A6 F8 7A DF AD    f4ъЯЁv.Go„y¦шzЯ­
00000190  FA 94 63 78 6F AD 57 6F A5 76 DF 5F DA FA FA 86    ъ”cxo­WoҐvЯ_Ъъъ†
000001A0  F8 7D 6A F8 72 FA 87 6D 87 F6 A8 76 F8 74 6F 78    ш}jшrъ‡m‡цЁvшtox
000001B0  8A 68 76 F4 68 7F A6 87 46 82 76 58 73 26 58 76    Љhvфh.¦‡F‚vXs&Xv
000001C0  87 FA 68 7D A6 F8 7A 68 74 68 76 36 48 76 A8 A7    ‡ъh}¦шzhthv6HvЁ§
000001D0  6A 87 46 87 F6 A8 76 F4 87 A6 FA 67 A9 46 28 66    j‡F‡цЁvф‡¦ъg©F(f

And the code:
Entry point, IDA decompileint __cdecl main(int argc, const char **argv, const char **envp) {
    puts(aPleaseEnterThe);                                                  // "Please enter the password:"
    memset(v10, 0, 0x10u);                                                  // v10 will hold the correct pass
                                                                            // and it's limited to 0x10 bytes
    v8 = 0;
    v9 = 0;
    v6 = 0;
    do {                                                                    // / read the user's password
        v11 = j___fgetchar();                                               // | sum the characters from the user's password
        v8 += v11;                                                          // | this will also add the ENTER key code - 0x0A
    } while (v11 != '\n' && v11);                                           // \
    v4 = fopen(aEncrypted_enc, aRb);                                        // "encrypted.enc"
    if (v4) {
        while (!(v6 & 4) || !(v6 & 1)) {                                    // Written like that, the loop limit is set to 5
            if (sub_401279(&v7, 1u, 1u, v4) != 1) {                         // this is just a fread() wrapper
                puts(aAnErrorOccured);                                      // "An error occurred"
                return 0;
            }
            v9 += v8 ^ (unsigned __int8)v7;                                 // take the user's password sum and XOR it
                                                                            // with a byte from the encrypted.enc file
                                                                            // that value should later match the 0xDCA from down below
            v10[v6] = v8 ^ v7;                                              // / this will decrypt the correct site code
            for (i = 0; i < v8; ++i) {                                      // | by using the user code's sum
                if (v10[v6] & 1) {                                          // |
                    v10[v6] = (signed int)(unsigned __int8)v10[v6] >> 1;    // |
                    v10[v6] |= 0x80u;                                       // |
                } else {                                                    // |
                    v10[v6] = (signed int)(unsigned __int8)v10[v6] >> 1;    // |
                }                                                           // |
            }                                                               // |
            v10[v6++] += 3;                                                 // \
        }
        fclose(v4);
        if (v9 == 0xDCA) {                                                  // this check will actually help me beat the challenge!
            printf(aCongratulation, v10);                                   // "Congratulations, The password is \'"
        } else {
            puts(aInvalidPasswor);                                          // "Invalid Password"
        }
        result = 0;
    } else {
        puts(aFailedToOpenEn);                                              // "Failed to open encrypted.enc"
        result = 0;
    }
    return result;
}

I can use few methods to defeat this challenge. First, let's see what information I have so far:
1. The user code is summed up, and later that sum is used for calculations. Therefore, the user code itself doesn't matter much, as long the its sum is the correct one;
2. The second loop, that reads the encrypred.enc file runs only 5 times, so only the first 5 bytes of encrypted.enc are used;
3. The second loop calculates the verification sum (v9), using a byte from the encrypted.enc XOR-ed with the sum of the user's code;
4. The second loop decrypts the site password (v10) using a byte from encrypted.enc XOR-ed with the sum of the user's code;
5. The final verification checks if the verification sum (v9) is equal to the hardcoded value of 0xDCA;

Having in mind the above, it's obvious that there's a certain user code sum that XOR-ed and added with the first 5 bytes of the encrypted.enc must result in the value of 0xDCA.
I don't care about the exact user's code right now, so I can write some code to determine the exact sum that will end as 0xDCA:
User code sum bruteforcer, Pythonencrypted_enc_data = "1M953" # encrypted.enc first 5 bytes
sum = i = 0

while(sum != 0xDCA):
    i += 1
    sum = 0
    for c in encrypted_enc_data:
        sum += ord(c) ^ i

print "Correct user sum is: %X" % i

Running this code gives me the value of 0x2F1, and that's the correct sum that the user's code should make when every character is added.
From here I can subtract 0x0A, because that's the value of the ENTER key character (new line) and I now got 0x2E7.
I can now try to build a working user code, by subtracting character values from that value of 0x2E7:
Manual building of a working code0x2E7 - 0x7A ('z') = 0x26D
0x26D - 0x7A ('z') = 0x1F3
0x1F3 - 0x7A ('z') = 0x179
0x179 - 0x7A ('z') = 0xFF
0xFF - 0x7A ('z') = 0x85
; if I subtract 0x7A again, I'll end up with  0x0B, and there's no printable character with the value of 0x0B
; so instead I'll subtract 0x42 ('B') and leave with 0x43 ('C')
0x85 - 0x42 ('B') = 0x43 ('C')

According to my logic, a possible correct code could be "zzzzzBC" so let's try it out:


Nice!
Since I knew the correct user code sum, I could completely skip the above by just writing a implementation of the site code decryption routine:
Site code decrypt, Pythonencrypted_enc_data = "1M953" # encrypted.enc first 5 bytes
sum = 0x2F1
i = 0
password = ""
for c in encrypted_enc_data:
    a = (ord(c) ^ sum) & 0xFF
    for i in range(sum):
        if (a & 1):
            a = a >> 1 | 0x80
        else:
            a = a >> 1
    password += chr(a+3)

print "Site's password is:", password

Finally, to make this solution complete, I'll write a random key generator:
User code keygen, Pythonimport random

charset = range(0x21, 0x7E) # I'll use all of the printable characters

sum = 0x2F1 - 0x0A # subtract the value of \n
code = ""
while sum > 0:
    c = charset[random.randint(0, len(charset))-1]
    if (sum/2 in charset):
        c = sum/2
        code += chr(c)
        code += chr(sum-c)
        break
    code += chr(c)
    sum -= c
print "Correct app password: \"%s\"" % code

Alright, application challenge #7 is completed!

Application Challenge #8 (Windows)


Target: app8win.exe
Requirement: Find the 6 digit code
Difficulty: medium

Written in VB6, this one looks interesting (to say the least):


The user is required to enter 6 digits by pressing the keys on the keypad. On the 7th key pressed, the app will either show the site password or just exit without warning.
Even watching the IDA's navigator we can clearly distinct the the code blocks (in brown) out of the rest data:


Going to the beginning of the code, I already see something familiar from the previous challenge written in VB6 - #4:
VB6 event table, IDA disassembly.text:00405ECC loc_405ECC:
.text:00405ECC                 sub     dword ptr [esp+4], 37h
.text:00405ED4                 jmp     loc_406040
.text:00405ED9 loc_405ED9:
.text:00405ED9                 sub     dword ptr [esp+4], 3Fh
.text:00405EE1                 jmp     loc_406100
.text:00405EE6 loc_405EE6:
.text:00405EE6                 sub     dword ptr [esp+4], 43h
.text:00405EEE                 jmp     loc_406250
.text:00405EF3 loc_405EF3:
.text:00405EF3                 sub     dword ptr [esp+4], 47h
.text:00405EFB                 jmp     loc_4063A0
.text:00405F00 loc_405F00:
.text:00405F00                 sub     dword ptr [esp+4], 4Bh
.text:00405F08                 jmp     loc_4064F0
.text:00405F0D loc_405F0D:
.text:00405F0D                 sub     dword ptr [esp+4], 4Fh
.text:00405F15                 jmp     loc_406640
.text:00405F1A loc_405F1A:
.text:00405F1A                 sub     dword ptr [esp+4], 53h
.text:00405F22                 jmp     loc_406790
.text:00405F27 loc_405F27:
.text:00405F27                 sub     dword ptr [esp+4], 57h
.text:00405F2F                 jmp     loc_4068E0
.text:00405F34 loc_405F34:
.text:00405F34                 sub     dword ptr [esp+4], 5Bh
.text:00405F3C                 jmp     loc_406A30
.text:00405F41 loc_405F41:
.text:00405F41                 sub     dword ptr [esp+4], 5Fh
.text:00405F49                 jmp     loc_406B80
.text:00405F4E loc_405F4E:
.text:00405F4E                 sub     dword ptr [esp+4], 3Bh
.text:00405F56                 jmp     loc_406CD0
.text:00405F5B loc_405F5B:
.text:00405F5B                 sub     dword ptr [esp+4], 63h
.text:00405F63                 jmp     loc_406D40

This obviously is the same event table that redirects the code flow according to user's interaction.

But let's assume this is cheating, and move to the debugger instead. I'll first get a list of the intermodular calls:
Intermodular calls, x86dbgAddress  Disassembly                                 Destination                  
00406D0F call dword ptr ds:[<&__vbaEnd>]             app8win.&__vbaEnd
0040772B call dword ptr ds:[<&__vbaEnd>]             app8win.&__vbaEnd
00407702 call dword ptr ds:[<&__vbaLenBstr>]         app8win.&__vbaLenBstr
00406AD2 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
00406442 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
00406982 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
004066E2 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
00406C22 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
00406832 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
004061A2 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
004062F2 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
00406592 call dword ptr ds:[<&__vbaStrCat>]          app8win.&__vbaStrCat
00406ADD call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
004062FD call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
004060A8 call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
0040659D call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
0040683D call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
00406C2D call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
0040698D call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
004061AD call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
004066ED call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
0040644D call dword ptr ds:[<&__vbaStrMove>]         app8win.&__vbaStrMove
00407470 call dword ptr ds:[<&__vbaVarDup>]          app8win.&__vbaVarDup
004070CF call dword ptr ds:[<&__vbaVarTstEq>]        app8win.&__vbaVarTstEq
004075A3 call dword ptr ds:[<&rtcMsgBox>]            app8win.&rtcMsgBox
0040609D call dword ptr ds:[<&rtcStrReverse>]        app8win.&rtcStrReverse
00401299 call <app8win.ThunRTMain>                   app8win.ThunRTMain
; for clarity I've removed calls to __vbaFreeObj, __vbaFreeObjList, __vbaFreeStr, __vbaFreeStrList, __vbaFreeVarList,
; __vbaHresultCheckObj and __vbaObjSet

From all these, __vbaVarTstEq looks pretty interesting so, I'll put a breakpoint on it, and press some of the app's keys:
__vbaVarTstEq call, x86dbg004070CB | 52                       | push edx
004070CC | FF D7                    | call edi                                ; edi:__vbaVarCat
004070CE | 50                       | push eax
004070CF | FF 15 4C 10 40 00        | call dword ptr ds:[<&__vbaVarTstEq>]    ; break here
004070D5 | 8D 4D E4                 | lea ecx,dword ptr ss:[ebp-1C]
004070D8 | 89 85 68 FB FF FF        | mov dword ptr ss:[ebp-498],eax
004070DE | FF 15 B4 10 40 00        | call dword ptr ds:[<&__vbaFreeObj>]

If that's comparing the correct key with the user's one, I should look upwards to see how the valid code is constructed.
I can also check what EAX is holding:
__vbaVarTstEq parameter, x86dbg0012F114  08 00 00 00 90 2A 1D 00 D4 55 1E 00 50 04 00 00  .....*..ÔU..P...  
0012F124  08 00 AB 01 E8 04 1E 00 0C 55 1E 00 27 CD 97 72  ..«.è....U..'Í.r  

In VB6, the call parameters are actually objects with their own structure, so I can't just dump the address EAX points and expect to get the actual thing - in this case the valid code.
Here, the correct code is contained within the third DWORD - D4 55 1E 00, or address 001E55D4.
But gain, let's assume this is cheating, and go upwards from __vbaVarTstEq's call and look around:
Code before __vbaVarTstEq, x86dbg00406F28 | 50                                 | push eax                                                    |
00406F29 | 6A 01                              | push 1                                                      |
00406F2B | 8D 55 C4                           | lea edx,dword ptr ss:[ebp-3C]                               |
00406F2E | 89 5D E8                           | mov dword ptr ss:[ebp-18],ebx                               | ebx:rtcMidCharVar
00406F31 | 8B 1D 40 10 40 00                  | mov ebx,dword ptr ds:[<&rtcMidCharVar>]                     |
00406F37 | 83 C6 34                           | add esi,34                                                  | esi:&L"987654321"
00406F3A | BF 08 40 00 00                     | mov edi,4008                                                |
00406F3F | 51                                 | push ecx                                                    |
00406F40 | 52                                 | push edx                                                    |
00406F41 | C7 85 C4 FE FF FF 08 80 00 00      | mov dword ptr ss:[ebp-13C],8008                             |
00406F4B | C7 45 DC 01 00 00 00               | mov dword ptr ss:[ebp-24],1                                 |
00406F52 | C7 45 D4 02 00 00 00               | mov dword ptr ss:[ebp-2C],2                                 |
00406F59 | 89 B5 4C FD FF FF                  | mov dword ptr ss:[ebp-2B4],esi                              | [ebp-2B4]:&L"987654321"
00406F5F | 89 BD 44 FD FF FF                  | mov dword ptr ss:[ebp-2BC],edi                              | edi:__vbaVarCat
00406F65 | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar
00406F67 | 8D 45 B4                           | lea eax,dword ptr ss:[ebp-4C]                               |
00406F6A | 8D 8D 24 FD FF FF                  | lea ecx,dword ptr ss:[ebp-2DC]                              |
00406F70 | 50                                 | push eax                                                    |
00406F71 | 6A 08                              | push 8                                                      |
00406F73 | 8D 55 A4                           | lea edx,dword ptr ss:[ebp-5C]                               |
00406F76 | 51                                 | push ecx                                                    |
00406F77 | 52                                 | push edx                                                    |
00406F78 | C7 45 BC 01 00 00 00               | mov dword ptr ss:[ebp-44],1                                 |
00406F7F | C7 45 B4 02 00 00 00               | mov dword ptr ss:[ebp-4C],2                                 |
00406F86 | 89 B5 2C FD FF FF                  | mov dword ptr ss:[ebp-2D4],esi                              | [ebp-2D4]:&L"987654321"
00406F8C | 89 BD 24 FD FF FF                  | mov dword ptr ss:[ebp-2DC],edi                              | edi:__vbaVarCat
00406F92 | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar
00406F94 | 8D 45 84                           | lea eax,dword ptr ss:[ebp-7C]                               |
00406F97 | 8D 8D 04 FD FF FF                  | lea ecx,dword ptr ss:[ebp-2FC]                              |
00406F9D | 50                                 | push eax                                                    |
00406F9E | 6A 05                              | push 5                                                      |
00406FA0 | 8D 95 74 FF FF FF                  | lea edx,dword ptr ss:[ebp-8C]                               |
00406FA6 | 51                                 | push ecx                                                    |
00406FA7 | 52                                 | push edx                                                    |
00406FA8 | C7 45 8C 01 00 00 00               | mov dword ptr ss:[ebp-74],1                                 |
00406FAF | C7 45 84 02 00 00 00               | mov dword ptr ss:[ebp-7C],2                                 |
00406FB6 | 89 B5 0C FD FF FF                  | mov dword ptr ss:[ebp-2F4],esi                              | [ebp-2F4]:&L"987654321"
00406FBC | 89 BD 04 FD FF FF                  | mov dword ptr ss:[ebp-2FC],edi                              | edi:__vbaVarCat
00406FC2 | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar
00406FC4 | 8D 85 54 FF FF FF                  | lea eax,dword ptr ss:[ebp-AC]                               |
00406FCA | 8D 8D E4 FC FF FF                  | lea ecx,dword ptr ss:[ebp-31C]                              |
00406FD0 | 50                                 | push eax                                                    |
00406FD1 | 6A 08                              | push 8                                                      |
00406FD3 | 8D 95 44 FF FF FF                  | lea edx,dword ptr ss:[ebp-BC]                               |
00406FD9 | 51                                 | push ecx                                                    |
00406FDA | 52                                 | push edx                                                    |
00406FDB | C7 85 5C FF FF FF 01 00 00 00      | mov dword ptr ss:[ebp-A4],1                                 |
00406FE5 | C7 85 54 FF FF FF 02 00 00 00      | mov dword ptr ss:[ebp-AC],2                                 |
00406FEF | 89 B5 EC FC FF FF                  | mov dword ptr ss:[ebp-314],esi                              | [ebp-314]:&L"987654321"
00406FF5 | 89 BD E4 FC FF FF                  | mov dword ptr ss:[ebp-31C],edi                              | edi:__vbaVarCat
00406FFB | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar
00406FFD | 8D 85 24 FF FF FF                  | lea eax,dword ptr ss:[ebp-DC]                               |
00407003 | 8D 8D C4 FC FF FF                  | lea ecx,dword ptr ss:[ebp-33C]                              |
00407009 | 50                                 | push eax                                                    |
0040700A | 6A 06                              | push 6                                                      |
0040700C | 8D 95 14 FF FF FF                  | lea edx,dword ptr ss:[ebp-EC]                               |
00407012 | 51                                 | push ecx                                                    |
00407013 | 52                                 | push edx                                                    |
00407014 | C7 85 2C FF FF FF 01 00 00 00      | mov dword ptr ss:[ebp-D4],1                                 |
0040701E | C7 85 24 FF FF FF 02 00 00 00      | mov dword ptr ss:[ebp-DC],2                                 |
00407028 | 89 B5 CC FC FF FF                  | mov dword ptr ss:[ebp-334],esi                              | [ebp-334]:&L"987654321"
0040702E | 89 BD C4 FC FF FF                  | mov dword ptr ss:[ebp-33C],edi                              | edi:__vbaVarCat
00407034 | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar
00407036 | 8D 85 F4 FE FF FF                  | lea eax,dword ptr ss:[ebp-10C]                              |
0040703C | 8D 8D A4 FC FF FF                  | lea ecx,dword ptr ss:[ebp-35C]                              |
00407042 | 50                                 | push eax                                                    |
00407043 | 6A 02                              | push 2                                                      |
00407045 | 8D 95 E4 FE FF FF                  | lea edx,dword ptr ss:[ebp-11C]                              |
0040704B | 51                                 | push ecx                                                    |
0040704C | 52                                 | push edx                                                    |
0040704D | C7 85 FC FE FF FF 01 00 00 00      | mov dword ptr ss:[ebp-104],1                                |
00407057 | C7 85 F4 FE FF FF 02 00 00 00      | mov dword ptr ss:[ebp-10C],2                                |
00407061 | 89 B5 AC FC FF FF                  | mov dword ptr ss:[ebp-354],esi                              | [ebp-354]:&L"987654321"
00407067 | 89 BD A4 FC FF FF                  | mov dword ptr ss:[ebp-35C],edi                              | edi:__vbaVarCat
0040706D | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar

Ok, that looks promising and the fact that x86dbg resolved the string "987654321" makes it pretty obvious too.
rtcMidCharVar is actually the API name of Visual Basic's in-language function Mid().
Using MSDN, we can see the Mid() function is used to retrieve a part of a string, pretty much, like for example, JavaScript's substr() works.
Watching the PUSH instructions I can clearly see the third parameter is always an integer between 0 and 9 as follows:
indexes1, 8, 5, 8, 6, 2

I already know the correct code is 6 digits long, because that's in the challenge description, so it seems I'm on the right track.
Now, because Mid() is used, those values should be indexes of a pre-existing string, and since x86dbg was so kind to resolve it, I know it's the string "987654321".
There's something I should mention here. Usually indexes in arrays, strings, etc. start from 0, but this one actually starts from 1.
So, the index of 1 actually points to '9' instead of pointing to '8'.

Having that in mind, after applying the indexes from above to that string gives me the code "925248":


Alright, my code is correct, and I have the site's password.
Speaking of which, where is the site's password created?
The answer lies beneath the __vbaVarTstEq CALL:
Site password contruction, x86asm00407173 | 6A 08                              | push 8                                                      |
00407175 | 8D 55 C4                           | lea edx,dword ptr ss:[ebp-3C]                               |
00407178 | 51                                 | push ecx                                                    |
00407179 | 52                                 | push edx                                                    |
0040717A | C7 45 DC 01 00 00 00               | mov dword ptr ss:[ebp-24],1                                 |
00407181 | C7 45 D4 02 00 00 00               | mov dword ptr ss:[ebp-2C],2                                 |
00407188 | 89 B5 4C FD FF FF                  | mov dword ptr ss:[ebp-2B4],esi                              | [ebp-2B4]:&L"987654321"
0040718E | C7 85 44 FD FF FF 08 40 00 00      | mov dword ptr ss:[ebp-2BC],4008                             |
00407198 | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar
0040719A | 8D 45 A4                           | lea eax,dword ptr ss:[ebp-5C]                               |
0040719D | 8D 8D 14 FD FF FF                  | lea ecx,dword ptr ss:[ebp-2EC]                              |
004071A3 | 50                                 | push eax                                                    |
004071A4 | 6A 04                              | push 4                                                      |
004071A6 | 8D 55 94                           | lea edx,dword ptr ss:[ebp-6C]                               |
004071A9 | 51                                 | push ecx                                                    |
004071AA | 52                                 | push edx                                                    |
004071AB | C7 45 AC 01 00 00 00               | mov dword ptr ss:[ebp-54],1                                 |
004071B2 | C7 45 A4 02 00 00 00               | mov dword ptr ss:[ebp-5C],2                                 |
004071B9 | 89 B5 1C FD FF FF                  | mov dword ptr ss:[ebp-2E4],esi                              |
004071BF | C7 85 14 FD FF FF 08 40 00 00      | mov dword ptr ss:[ebp-2EC],4008                             |
004071C9 | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar
004071CB | 8D 85 74 FF FF FF                  | lea eax,dword ptr ss:[ebp-8C]                               |
004071D1 | 8D 8D F4 FC FF FF                  | lea ecx,dword ptr ss:[ebp-30C]                              |
004071D7 | 50                                 | push eax                                                    |
004071D8 | 6A 06                              | push 6                                                      |
004071DA | 8D 95 64 FF FF FF                  | lea edx,dword ptr ss:[ebp-9C]                               |
004071E0 | 51                                 | push ecx                                                    |
004071E1 | 52                                 | push edx                                                    |
004071E2 | C7 85 7C FF FF FF 01 00 00 00      | mov dword ptr ss:[ebp-84],1                                 | [ebp-84]:"$hjw"
004071EC | C7 85 74 FF FF FF 02 00 00 00      | mov dword ptr ss:[ebp-8C],2                                 |
004071F6 | 89 B5 FC FC FF FF                  | mov dword ptr ss:[ebp-304],esi                              |
004071FC | C7 85 F4 FC FF FF 08 40 00 00      | mov dword ptr ss:[ebp-30C],4008                             |
00407206 | FF D3                              | call ebx                                                    | ebx:rtcMidCharVar
00407208 | 8D 85 44 FF FF FF                  | lea eax,dword ptr ss:[ebp-BC]                               |
0040720E | 8D 8D D4 FC FF FF                  | lea ecx,dword ptr ss:[ebp-32C]                              |
; continues

So, indexes 8, 4 and 6, pointing to '2', '6', '4' and so on.

And that's all about challenge #8

Application Challenge #9 (Windows)


Target: app9win.exe
Requirement: Match the beeps to the 'Play' button
Difficulty: medium

Yet another odd looking challenge written in VB6:


With the experience from the previous VB6 challenges, I can actually waltz through this one pretty easy.
Thanks to IDA's navigator, I'll locate the event table, set breakpoints and determine what's what:
Event table, IDA disassembly.text:00404F40 loc_404F40:
.text:00404F40                 sub     dword ptr [esp+4], 53h    ; Main window create
.text:00404F48                 jmp     loc_4054C0
.text:00404F4D loc_404F4D:
.text:00404F4D                 sub     dword ptr [esp+4], 4Bh    ; "Play" button pressed
.text:00404F55                 jmp     loc_405570
.text:00404F5A loc_404F5A:
.text:00404F5A                 sub     dword ptr [esp+4], 3Fh    ; "Match1" button pressed
.text:00404F62                 jmp     loc_405610
.text:00404F67 loc_404F67:
.text:00404F67                 sub     dword ptr [esp+4], 43h    ; "Match2" button pressed
.text:00404F6F                 jmp     loc_405690
.text:00404F74 loc_404F74:
.text:00404F74                 sub     dword ptr [esp+4], 47h    ; "Match3" button pressed
.text:00404F7C                 jmp     loc_405710
.text:00404F81 loc_404F81:
.text:00404F81                 sub     dword ptr [esp+4], 4Fh    ; mystery action, that is constantly running in loop
.text:00404F89                 jmp     loc_405790

If I want I can redirect some of the button's actions to that mystery action down there, but that wont show me the correct password.

First, let's see what the "Play" button does, and how these sounds are generated:
Play button action, IDA disassembly.text:00405570 loc_405570:
.text:00405570                 push    ebp
.text:00405571                 mov     ebp, esp
.text:00405573                 sub     esp, 0Ch
.text:00405576                 push    offset loc_4010F6
.text:0040557B                 mov     eax, large fs:0
.text:00405581                 push    eax
.text:00405582                 mov     large fs:0, esp
.text:00405589                 sub     esp, 8
.text:0040558C                 push    ebx
.text:0040558D                 push    esi
.text:0040558E                 push    edi
.text:0040558F                 mov     [ebp-0Ch], esp
.text:00405592                 mov     dword ptr [ebp-8], offset dword_4010C0
.text:00405599                 mov     eax, [ebp+8]
.text:0040559C                 mov     ecx, eax
.text:0040559E                 and     ecx, 1
.text:004055A1                 mov     [ebp-4], ecx
.text:004055A4                 and     al, 0FEh
.text:004055A6                 push    eax
.text:004055A7                 mov     [ebp+8], eax
.text:004055AA                 mov     edx, [eax]
.text:004055AC                 call    dword ptr [edx+4]
.text:004055AF                 push    12Ch                            ;/ First sound
.text:004055B4                 push    64h                             ;|
.text:004055B6                 call    sub_40519C                      ;\ DllFunctionCall(kernel32.Beep)
.text:004055BB                 mov     esi, ds:__vbaSetSystemError
.text:004055C1                 call    esi ; __vbaSetSystemError
.text:004055C3                 push    12Ch                            ;/ Second sound
.text:004055C8                 push    1F4h                            ;|
.text:004055CD                 call    sub_40519C                      ;\ DllFunctionCall(kernel32.Beep)
.text:004055D2                 call    esi ; __vbaSetSystemError
.text:004055D4                 push    12Ch                            ;/ Third sound
.text:004055D9                 push    3E8h                            ;|
.text:004055DE                 call    sub_40519C                      ;\ DllFunctionCall(kernel32.Beep)
;trimmed

So, the sounds are generated using the Beep function. From MSDN we can see Beep takes two parameters:
BeepBOOL WINAPI Beep(
  _In_ DWORD dwFreq,
  _In_ DWORD dwDuration
);

The dwDuration is set to 0x012C for all three of them, and only the dwFreq is different.
Now let's see the "Match1" button code:
Match1 action.text:00405610                 push    ebp
.text:00405611                 mov     ebp, esp
.text:00405613                 sub     esp, 0Ch
.text:00405616                 push    offset loc_4010F6
.text:0040561B                 mov     eax, large fs:0
.text:00405621                 push    eax
.text:00405622                 mov     large fs:0, esp
.text:00405629                 sub     esp, 8
.text:0040562C                 push    ebx
.text:0040562D                 push    esi
.text:0040562E                 push    edi
.text:0040562F                 mov     [ebp-0Ch], esp
.text:00405632                 mov     dword ptr [ebp-8], offset dword_4010C8
.text:00405639                 mov     esi, [ebp+8]
.text:0040563C                 mov     eax, esi
.text:0040563E                 and     eax, 1
.text:00405641                 mov     [ebp-4], eax
.text:00405644                 and     esi, 0FFFFFFFEh
.text:00405647                 push    esi
.text:00405648                 mov     [ebp+8], esi
.text:0040564B                 mov     ecx, [esi]
.text:0040564D                 call    dword ptr [ecx+4]
.text:00405650                 mov     edx, [esi+34h]    ; where's this coming from?
.text:00405653                 push    12Ch              ;/ dwDuration
.text:00405658                 push    edx               ;| dwFreq
.text:00405659                 call    sub_40519C        ;\ DllFunctionCall(kernel32.Beep)
;trimmed

; edx=C8
; dword ptr[esi+34]=[0060051C]=C8


The dwDuration is again 0x012C, but the dwFreq here comes from a dynamic buffer instead from a hardcoded value and it's set to 0xC8.
Time to check that Main window initialization procedure and see where that 0xC8 came from:
Main window create procedure, IDA disassemble.text:004054C0                 push    ebp
.text:004054C1                 mov     ebp, esp
.text:004054C3                 sub     esp, 0Ch
.text:004054C6                 push    offset loc_4010F6
.text:004054CB                 mov     eax, large fs:0
.text:004054D1                 push    eax
.text:004054D2                 mov     large fs:0, esp
.text:004054D9                 sub     esp, 0Ch
.text:004054DC                 push    ebx
.text:004054DD                 push    esi
.text:004054DE                 push    edi
.text:004054DF                 mov     [ebp-0Ch], esp
.text:004054E2                 mov     dword ptr [ebp-8], offset dword_4010B0
.text:004054E9                 mov     esi, [ebp+8]
.text:004054EC                 mov     eax, esi
.text:004054EE                 and     eax, 1
.text:004054F1                 mov     [ebp-4], eax
.text:004054F4                 and     esi, 0FFFFFFFEh
.text:004054F7                 push    esi
.text:004054F8                 mov     [ebp+8], esi
.text:004054FB                 mov     ecx, [esi]
.text:004054FD                 call    dword ptr [ecx+4]
.text:00405500                 xor     ebx, ebx
.text:00405502                 mov     edx, offset aHtsApplication ; "HTS Application Challenge Programmed by"...
.text:00405507                 lea     ecx, [ebp-18h]
.text:0040550A                 mov     [ebp-18h], ebx
.text:0040550D                 call    ds:__vbaStrCopy
.text:00405513                 mov     edi, ds:__vbaI4Str
.text:00405519                 push    offset a200     ;/ Parameter = "200"
.text:0040551E                 call    edi             ;\ __vbaI4Str (convert string to integer)
.text:00405520                 push    offset a600     ;"600"
.text:00405525                 mov     [esi+34h], eax  ; result is stored here!
.text:00405528                 call    edi ; __vbaI4Str
.text:0040552A                 push    offset a1100    ; "1100"
.text:0040552F                 mov     [esi+38h], eax
.text:00405532                 call    edi ; __vbaI4Str
.text:00405534                 mov     [esi+3Ch], eax
.text:00405537                 mov     [ebp-4], ebx
.text:0040553A                 push    offset loc_405549
.text:0040553F                 lea     ecx, [ebp-18h]
.text:00405542                 call    ds:__vbaFreeStr
.text:00405548                 retn

Got it! That string to integer thing actually stores the dwFreq values for the three buttons.
I checked the three buttons and they are playing sounds with dwFreq equal to 0x64 (Match1), 0x1F4 (Match2) and 0x3E8 (Match3), but the values here are 200 (0xC8), 600 (0x258) and 1100 (0x44C).
What I did here was to change these strings to 100, 500 and 1000, let the program run and BAM!:


It's interesting to mention that pressing the Play, Match1, Match2 or Match3 buttons does nothing, and the only way to solve this challenge is to patch it like I did.

The correct password is constructed pretty much like it was done in the previous VB6 challenges:
Site code construction, IDA disassembly.text:00405B9A                 push    0Fh                                  ; first string index
.text:00405B9C                 lea     edx, [ebp-48h]
.text:00405B9F                 mov     ebx, 2
.text:00405BA4                 mov     esi, 4008h
.text:00405BA9                 push    ecx
.text:00405BAA                 push    edx
.text:00405BAB                 mov     dword ptr [ebp-240h], offset dword_4052C8
.text:00405BB5                 mov     dword ptr [ebp-248h], 8
.text:00405BBF                 mov     dword ptr [ebp-30h], 1
.text:00405BC6                 mov     [ebp-38h], ebx
.text:00405BC9                 mov     [ebp-228h], esi
.text:00405BCF                 call    edi ; rtcMidCharVar                  ; get character based on it's index in the charset string
.text:00405BD1                 lea     eax, [ebp-18h]
.text:00405BD4                 lea     ecx, [ebp-68h]
.text:00405BD7                 mov     [ebp-250h], eax
.text:00405BDD                 push    ecx
.text:00405BDE                 lea     edx, [ebp-258h]
.text:00405BE4                 push    12h                                  ; second string index
.text:00405BE6                 lea     eax, [ebp-78h]
.text:00405BE9                 push    edx
.text:00405BEA                 push    eax
.text:00405BEB                 mov     dword ptr [ebp-60h], 1
.text:00405BF2                 mov     [ebp-68h], ebx
.text:00405BF5                 mov     [ebp-258h], esi
.text:00405BFB                 call    edi ; rtcMidCharVar                  ; get character based on it's index in the charset string
; trimmed


Application Challenge #10 (Windows)


Target: app10win.exe
Requirement: Find the Password
Difficulty: medium

Yet another VB6 challenge:


Moving directly to the event handler table:
Event table, IDA disassembly.text:00404904 loc_404904:
.text:00404904                 sub     dword ptr [esp+4], 3Bh    ; mystery event
.text:0040490C                 jmp     loc_4049E0
.text:00404911 loc_404911:
.text:00404911                 sub     dword ptr [esp+4], 33h    ; main window create
.text:00404919                 jmp     loc_405470
.text:0040491E loc_40491E:
.text:0040491E                 sub     dword ptr [esp+4], 37h    ; "Proceed" button action
.text:00404926                 jmp     loc_405500

Inside the "Proceed" event we have:
Proceed button action, IDA decompileint __cdecl sub_405500(int a1) {
    // trimmed 
    v35 = &v3;
    v36 = (int)dword_4010B0;
    v1 = a1;
    v37 = a1 & 1;
    LOBYTE(v1) = a1 & 0xFE;
    v38 = v1;
    (*(_DWORD *)v1 + 4)(                                          /// x86dbg resolved this call as BASIC_CLASS_AddRef
        v1, v3, v4, v5, v6, v7, v8, v9,                           //|
        v10, v11, v12, v13, v14, v15, v16, v17,                   //|
        v18, v19, v20, v21, v22, v23, v24, v25,                   //|
        v26, v27, v28, v29, v30, v31, v32);                       //\
    v26 = 0x80020004;
    v33 = 0;
    v24 = 10;
    v18 = (int)L"Error-266";                                      // error message caption
    v16 = 8;
    _vbaVarDup(&v32, &v16);
    v22 = (int)L"Error: 404 object(pwd); not found!";             // error message text
    v20 = 8;
    _vbaVarDup(&v33, &v20);
    rtcMsgBox(&v33, 48, &v32, &v28, &v24);        // show the error message
    _vbaFreeVarList(4, &v33, &v32, &v28, &v24);
    v37 = 0;
    v34 = &loc_4055F5;
    v33 = v38;
    (*(void (__stdcall **)(int))(*(_DWORD *)v38 + 8))(v38);
    return v37;
}

Ok, that look pointless, and it pretty much is. The "game" here is that the challenge is "broken".
In this case the error message serves as a hint, that the Proceed code is incomplete or points to the wrong place.
Remember that mystery event from the event table at address 00404904?
Like I did in Challenge #4, I'll redirect the event from "Proceed" button action so it jumps to where the mystery event goes - loc_4049E0():
Patching the Proceed action code, x86dbg00404904 | 81 6C 24 04 3B 00 00 00    sub dword ptr ss:[esp+4],3B    ; mystery event
0040490C | E9 CF 00 00 00             jmp app10win.4049E0            ; mystery event procedure
00404911 | 81 6C 24 04 33 00 00 00    sub dword ptr ss:[esp+4],33    ; Main window create
00404919 | E9 52 0B 00 00             jmp app10win.405470            ;
0040491E | 81 6C 24 04 37 00 00 00    sub dword ptr ss:[esp+4],37    ; "Proceed" button action
00404926 | E9 B5 00 00 00             jmp app10win.4049E0            ; my memory patch, redirect the code flow
                                                                     ; to the mystery event's procedure

Resuming the process gives me this:


Well, that was... anticlimactic.

Again, the site's password is constructed character by character as follows:
Site password constructing, IDA disassembly
.text:00404BA7                 push    'T'
.text:00404BA9                 push    eax
; trimmed
.text:00404BE6                 call    edi ; rtcVarBstrFromAnsi
.text:00404BE8                 lea     ecx, [ebp+var_34]
.text:00404BEB                 push    'h'
.text:00404BED                 push    ecx
.text:00404BEE                 call    edi ; rtcVarBstrFromAnsi
.text:00404BF0                 lea     edx, [ebp+var_54]
.text:00404BF3                 push    'e'
.text:00404BF5                 push    edx
.text:00404BF6                 call    edi ; rtcVarBstrFromAnsi
.text:00404BF8                 lea     eax, [ebp+var_74]
.text:00404BFB                 push    ' '
.text:00404BFD                 push    eax
.text:00404BFE                 call    edi ; rtcVarBstrFromAnsi
.text:00404C00                 lea     ecx, [ebp+var_94]
.text:00404C06                 push    'P'
.text:00404C08                 push    ecx
.text:00404C09                 call    edi ; rtcVarBstrFromAnsi
.text:00404C0B                 lea     edx, [ebp+var_B4]
.text:00404C11                 push    'a'
.text:00404C13                 push    edx
.text:00404C14                 call    edi ; rtcVarBstrFromAnsi
; trimmed


Application Challenge #11 (Windows)


Target: app11win.exe
Requirement: Find the Password
Difficulty: medium

If you thought app 11 will be again a VB6 challenge - you were absolutely right:


Moving to the event table:
Event table, IDA disassembly.text:00405A04 loc_405A04:
.text:00405A04                 sub     dword ptr [esp+4], 3Bh    ; Main window create
.text:00405A0C                 jmp     loc_405E30
.text:00405A11 loc_405A11:
.text:00405A11                 sub     dword ptr [esp+4], 37h    ; "Check" button action
.text:00405A19                 jmp     loc_405EC0

Once you solve few of those they get a bit boring.
Anyway, let's see what the "Check" button does:
Check button procedure, IDA decompilevoid __cdecl sub_405EC0() {
// trimmed
    _vbaStrCopy(&v174, L"abcdefghijklmnopqrstuvwxyz:!.'"); /// clone the charset in upper case!!
    v68 = &v174;                                           //|
    v67 = 0x4008;                                          //|
    rtcUpperCaseVar(&v168, &v67);                          //| here - &v168
    v52 = &v174;                                           //|
    v51 = 0x4008;                                          //|
    rtcUpperCaseVar(&v114, &v51);                          //\ and here - &v114
    v1 = *(_DWORD *)(v178 & 0xFFFFFFFE);
    v171 = (int *)(v178 & 0xFFFFFFFE);
    v2 = (*(int (__stdcall **)(unsigned int))(v1 + 0x300))(v178 & 0xFFFFFFFE);
    v3 = _vbaObjSet(&v172, v2);
    v4 = v3;
    v171 = &v173;
    v170 = (int *)v3;
    v5 = (*(int (__stdcall **)(int, int *))(*(_DWORD *)v3 + 0xA0))(v3, &v173);
    __asm { fnclex }
    if ( v5 < 0 )
        _vbaHresultCheckObj(v5, v4, dword_405C78, 0xA0);
    v98 = v173;
    v173 = 0;
    v97 = 0x8008;
    rtcMidCharVar(&v160, &v168, 16, &v164);    // "ABCDEFGHIJKLMNO[P]QRSTUVWXYZ:!.'" this one is in uppercase
    v64 = &v174;
    v63 = 0x4008;
    rtcMidCharVar(&v152, &v63, 15, &v156);     // "abcdefghijklmn[o]pqrstuvwxyz:!.'"
    v62 = &v174;
    v61 = 0x4008;
    rtcMidCharVar(&v140, &v61, 9, &v144);      // "abcdefgh[i]jklmnopqrstuvwxyz:!.'"
    v60 = &v174;
    v134 = (int *)1;
    v132 = (int *)2;
    v59 = 0x4008;
    rtcMidCharVar(&v128, &v59, 19, &v132);     // "abcdefghijklmnopqr[s]tuvwxyz:!.'"
    v58 = &v174;
    v123 = 1;
    v122 = 2;
    v57 = 0x4008;
    rtcMidCharVar(&v121, &v57, 15, &v122);     // "abcdefghijklmn[o]pqrstuvwxyz:!.'"
    v56 = &v174;
    v119 = 1;
    v118 = 2;
    v55 = 0x4008;
    rtcMidCharVar(&v117, &v55, 14, &v118);     // "abcdefghijklm[n]opqrstuvwxyz:!.'"
    v113 = 1;
    v112 = 2;
    rtcMidCharVar(&v110, &v114, 19, &v112);    // "ABCDEFGHIJKLMNOPQR[S]TUVWXYZ:!.'" this one is also in uppercase
    v48 = &v174;
    v108 = 1;
    v107 = 2;
    v47 = 0x4008;
    rtcMidCharVar(&v105, &v47, 5, &v107);      // "abcd[e]fghijklmnopqrstuvwxyz:!.'"
    v44 = &v174;
    v103 = 1;
    v102 = 2;
    v43 = 0x4008;
    rtcMidCharVar(&v100, &v43, 1, &v102);      // "[a]bcdefghijklmnopqrstuvwxyz:!.'"
    v6 = _vbaVarCat(&v148, &v152, &v160, &v97);
    v7 = _vbaVarCat(&v136, &v140, v6, v172);
    v8 = _vbaVarCat(&v124, &v128, v7, v172);
    v9 = _vbaVarCat(&v120, &v121, v8, v172);
    v10 = _vbaVarCat(&v115, &v117, v9, v172);
    v11 = _vbaVarCat(&v109, &v110, v10, v172);
    v12 = _vbaVarCat(&v104, &v105, v11, v172);
    v13 = _vbaVarCat(&v99, &v100, v12, v172);
    v28 = _vbaVarTstEq(v13);                              // Compare the user's with the correct code
    // continues


So, the user code is compared against "poisonsea". But take note on the charset used by every rtcMidCharVar().
The first ('p') and the seventh ('s') uses a uppercase charset, so the password should be "PoisonSea". Let's try that:


Oh really, am I?

Alright, let's see the rest of the code then:
Check button procedure, IDA decompile    if ( (_WORD)v28 ) {                                  // this is the _vbaVarTstEq() branching
        v158 = (int *)0x80020004;
        v162 = (int *)0x80020004;
        v156 = (int *)10;
        v160 = (int *)10;
        v66 = &dword_405CD8;
        v65 = 8;
        _vbaVarDup(&v164, &v65);
        v68 = (int *)L"Your looking in the wrong place...";
        v67 = 8;
        _vbaVarDup(&v168, &v67);
        rtcMsgBox(&v168, 0x40, &v164, &v160, &v156);    // throw the "Your looking in the wrong place..."
		                                        // the typo is not mine, btw
        v171 = (int *)&v156;
        v170 = (int *)&v160;
        _vbaFreeVarList(4, (int)&v168, (int)&v164);
    } else {                                            // so, if the above was a bad message, this one should also be a bad message?
        v68 = &v174;
        v67 = 0x4008;
        rtcUpperCaseVar(&v168, &v67);                    // &v168 = Charset in uppercase
        rtcMidCharVar(&v160, &v168, 23, &v164);          // "ABCDEFGHIJKLMNOPQRSTUV[W]XYZ:!.'"
        v64 = &v174;
        v63 = 0x4008;
        rtcMidCharVar(&v152, &v63, 18, &v156);           // "abcdefghijklmnopq[r]stuvwxyz:!.'"
        v62 = &v174;
        v61 = 0x4008;
        rtcMidCharVar(&v140, &v61, 15, &v144);           // "abcdefghijklmn[o]pqrstuvwxyz:!.'"
        v60 = &v174;
        v59 = 0x4008;
        rtcMidCharVar(&v128, &v59, 14, &v132);           // "abcdefghijklm[n]opqrstuvwxyz:!.'"
        v58 = &v174;
        v122 = 2;
        v57 = 0x4008;
        rtcMidCharVar(&v121, &v57, 7, &v122);            // "abcdef[g]hijklmnopqrstuvwxyz:!.'"
        v54 = &v174;
        v53 = 0x4008;
        rtcUpperCaseVar(&v117, &v53);                    // &v117 = Charset in uppercase
        v116 = 1;
        v115 = 2;
        rtcMidCharVar(&v114, &v117, 1, &v115);           // "[A]BCDEFGHIJKLMNOPQRSTUVWXYZ:!.'"
        v50 = &v174;
        v111 = 1;
        v110 = 2;
        v49 = 0x4008;
        rtcMidCharVar(&v109, &v49, 23, &v110);           // "abcdefghijklmnopqrstuv[w]xyz:!.'"
        v46 = &v174;
        v106 = 1;
        v105 = 2;
        v45 = 0x4008;
        rtcMidCharVar(&v104, &v45, 14, &v105);           // "abcdefghijklm[n]opqrstuvwxyz:!.'"
        v42 = &v174;
        v101 = 1;
        v100 = 2;
        v41 = 0x4008;
        rtcMidCharVar(&v99, &v41, 19, &v100);           // "abcdefghijklmnopqr[s]tuvwxyz:!.'"
        v40 = &v174;
        v96 = 1;
        v95 = 2;
        v39 = 0x4008;
        rtcMidCharVar(&v94, &v39, 5, &v95);             // "abcd[e]fghijklmnopqrstuvwxyz:!.'"
        v38 = &v174;
        v92 = 1;
        v91 = 2;
        v37 = 0x4008;
        rtcMidCharVar(&v90, &v37, 18, &v91);           // "abcdefghijklmnopq[r]stuvwxyz:!.'"
        v36 = &v174;
        v35 = 0x4008;
        rtcUpperCaseVar(&v88, &v35);                   // &v88 = Charset in uppercase
        v87 = 1;
        v86 = 2;
        rtcMidCharVar(&v85, &v88, 14, &v86);           // "ABCDEFGHIJKLM[N]OPQRSTUVWXYZ:!.'"
        v34 = &v174;
        v84 = 1;
        v83 = 2;
        v33 = 0x4008;
        rtcMidCharVar(&v82, &v33, 15, &v83);           // "abcdefghijklmn[o]pqrstuvwxyz:!.'"
        v32 = &v174;
        v80 = 1;
        v79 = 2;
        v31 = 0x4008;
        rtcMidCharVar(&v78, &v31, 16, &v79);          // "abcdefghijklmno[p]qrstuvwxyz:!.'"
        v30 = &v174;
        v76 = 1;
        v75 = 2;
        v29 = 0x4008;
        rtcMidCharVar(&v74, &v29, 5, &v75);           // "abcd[e]fghijklmnopqrstuvwxyz:!.'"
        v69 = 10;
        v71 = 10;
        v70 = 0x80020004;
        v72 = 0x80020004;
        v171 = &v69;
        v56 = dword_405CE0;
        v55 = 8;
        // trimmed _vbaVarCat() calls
        rtcMsgBox(v26, v168, v169, v170, v171);     // Throw the "Wrong Answer" message

        // trimmed
    }
    v177 = 0;
    _vbaFreeStr(&v174, &loc_406D1D);
}
Damn! I'm looking in the wrong place!

Alright, it feel like I should throw a hint here. The correct password is indeed hidden, in a steganography manner.
Thus, I shouldn't look inside the code at all.
Where else can I look then? Maybe Overlay data appended to the EXE's end:


Nope. There's no overlay data here.

What else, resources?


Nothing in here either.

Maybe I should dig deeper. What does that icon contain? Can it hold any hidden messages?
It technically can, but the icon resources are stored in a raw uncompressed form inside the EXE, so I should be able to see the message in the HEX editor.
I've already looked for strings inside the EXE and found nothing, so the icon is clean.
Wait a second... that application has a background image. Where is the image located?
In VB6, the image resources used as backgrounds (and not only) are stored in the .frx file that use them, so they are not visible in the resource viewers like Resource Hacker or PE Explorer.
Let's make a search for JPEG data in the executable.
According to the JPEG file format specifications, a standard JPEG image should have the following structure:

SegmentCode
Start of ImageFF D8
JFIF-APP0FF E0 s1 s2 4A 46 49 46 00
End of ImageFF D9

So let's try to find the Start of Image bytes FF D8 and the first two bytes from the JFIF part - FF E0:


Nice, I can now extract the JPG and check it out:


That was one really odd challenge.

Application Challenge #12 (Windows)


Target: app12win.exe
Requirement: Find the Password
Difficulty: hard

This is the first "hard" challenge by HTS and it's again written in VB6:


How hard could it be, right?
Let's go straight to the event table:
Event table, IDA disassembly.text:004062D8 loc_4062D8:
.text:004062D8                 sub     dword ptr [esp+4], 3Bh    ;/ Main window create
.text:004062E0                 jmp     loc_406950                ;\
.text:004062E5 loc_4062E5:
.text:004062E5                 sub     dword ptr [esp+4], 33h    ;/ "Check Password" button action
.text:004062ED                 jmp     loc_406A70                ;\
.text:004062F2 loc_4062F2:
.text:004062F2                 sub     dword ptr [esp+4], 4Bh    ;/ Edit field change event
.text:004062FA                 jmp     loc_406D60                ;\ that's odd, so this should be interesting
.text:004062FF                 sub     dword ptr [esp+4], 4Bh    ;/ no references to here? hmm...
.text:00406307                 jmp     loc_406FC0                ;\
.text:0040630C loc_40630C:
.text:0040630C                 sub     dword ptr [esp+4], 3Fh    ;/ Controls the "Veryfying Password" animation
.text:00406314                 jmp     loc_407100                ;\
.text:00406319 loc_406319:
.text:00406319                 sub     dword ptr [esp+4], 47h    ;/ Final stage, where the "Password is Incorrect." message is shown
.text:00406321                 jmp     loc_407410                ;\

I'll go to the obvious and check that loc_406A70() procedure that handles the "Check Password" button action:
Check Password button action, IDA decompileint __cdecl sub_406A70(int a1) {
    // trimmed
    v75 = &v33;
    v76 = (int)dword_4010D8;
    v77 = a1 & 1;
    v78 = a1 & 0xFFFFFFFE;
    (*(_DWORD *)v78 + 4)(v78, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, 
                         v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63,
                         v64, v65, v66, v67, v68, v69, v70, v71);                          // BASIC_CLASS_AddRef()
    v1 = *(_DWORD *)v78;
    v71 = (int *)v78;
    v74 = 0;
    v73 = 0;
    v69 = 0;
    v65 = 0;
    v61 = 0;
    v57 = 0;
    v53 = 0;
    v49 = 0;
    v2 = (*(int (__stdcall **)(unsigned int))(v1 + 0x308))(v78);                           // __vbaGosubFree+69E
    v3 = _vbaObjSet(&v73, v2);
    v4 = *(_DWORD *)v3;
    v71 = &v74;
    v70 = v3;
    v40 = v3;
    v5 = (*(int (__stdcall **)(int, int *))(v4 + 0xA0))(v3, &v74);                         // IID_IVbaHost+D663
    __asm { fnclex }
    if (v5 < 0)
        _vbaHresultCheckObj(v5, v40, dword_406598, 0xA0);
    v6 = -(_vbaStrCmp(L"Enter Password Here", v74) == 0);                                  /// Check if the user entered any password
    _vbaFreeStr(&v74, v72);                                                                //|
    _vbaFreeObj(&v73);                                                                     //|
    if ((_WORD)v6) {                                                                       //\
        v59 = 0x80020004;
        v63 = 0x80020004;
        v57 = 10;
        v61 = 10;
        v51 = (int)L"Alert";
        v49 = 8;
        _vbaVarDup(&v65, &v49);
        v55 = (int)L"Please Enter a Password";
        v53 = 8;
        _vbaVarDup(&v69, &v53);
        rtcMsgBox(&v69, 0x40, &v65, &v61, &v57);                                           // "Please Enter a Password"
        _vbaFreeVarList(4, &v69, &v65, &v61, &v57);
        v7 = *(_DWORD *)v78;
        v71 = (int *)v78;
        v8 = (*(int (__stdcall **)(unsigned int))(v7 + 0x308))(v78);                       // __vbaGosubFree+69E
        v9 = _vbaObjSet(&v73, v8);
        v10 = v9;
        v71 = &dword_40661C;
        v70 = v9;
        v11 = (*(int (__stdcall **)(int, int *))(*(_DWORD *)v9 + 0xA4))(v9, &dword_40661C);
        __asm { fnclex }
        if ( v11 < 0 )
          _vbaHresultCheckObj(v11, v10, dword_406598, 0xA4);
        _vbaFreeObj(&v73);
    } else {                                                                               // Continue with the password validation
        v12 = *(_DWORD *)v78;
        v71 = (int *)v78;
        v13 = (*(int (__stdcall **)(unsigned int))(v12 + 0x308))(v78);                     // __vbaGosubFree+69E
        v14 = _vbaObjSet(&v73, v13);
        v15 = v14;
        v71 = 0;
        v70 = v14;
        v16 = (*(int (__stdcall **)(int, _DWORD))(*(_DWORD *)v14 + 0x8C))(v14, 0);         // IID_IVbaHost+D603
        __asm { fnclex }
        if ( v16 < 0 )
            _vbaHresultCheckObj(v16, v15, dword_406598, 0x8C);
        _vbaFreeObj(&v73);
        v17 = *(_DWORD *)v78;
        v71 = (int *)v78;
        v18 = (*(int (__stdcall **)(unsigned int))(v17 + 0x304))(v78);                     // __vbaGosubFree+696
        v19 = _vbaObjSet(&v73, v18);
        v20 = v19;
        v71 = 0;
        v70 = v19;
        v21 = (*(int (__stdcall **)(int, _DWORD))(*(_DWORD *)v19 + 0x8C))(v19, 0);         // IID_IVbaHost+D603
        __asm { fnclex }
        if ( v21 < 0 )
            _vbaHresultCheckObj(v21, v20, dword_406620, 0x8C);
        _vbaFreeObj(&v73);
        v22 = *(_DWORD *)v78;
        v71 = (int *)v78;
        v23 = (*(int (__stdcall **)(unsigned int))(v22 + 0x300))(v78);                     // __vbaGosubFree+68E
        v24 = _vbaObjSet(&v73, v23);
        v25 = v24;
        v71 = (int *)-1;
        v70 = v24;
        v26 = (*(int (__stdcall **)(int, signed int))(*(_DWORD *)v24 + 0x5C))(v24, -1);    // IID_IVbaHost+12541
        __asm { fnclex }
        if ( v26 < 0 )
            _vbaHresultCheckObj(v26, v25, dword_406588, 0x5C);
        _vbaFreeObj(&v73);
        v27 = *(_DWORD *)v78;
        v71 = (int *)v78;
        v28 = (*(int (__stdcall **)(unsigned int))(v27 + 0x30C))(v78);                     // __vbaGosubFree+6A6
        v29 = _vbaObjSet(&v73, v28);
        v30 = v29;
        v71 = (int *)L"Verifying Password";
        v70 = v29;
        v31 = (*(int (__stdcall **)(int, _DWORD))(*(_DWORD *)v29 + 0x54))(v29, L"Verifying Password");
        __asm { fnclex }
        if ( v31 < 0 )
            _vbaHresultCheckObj(v31, v30, dword_40665C, 0x54);                             // IID_IVbaHost+E6EA
        _vbaFreeObj(&v73);
    }
    v77 = 0;
    v71 = (int *)v78;
    (*(void (__stdcall **)(unsigned int))(*(_DWORD *)v78 + 8))(v78);                       // Zombie_Release
    return v77;
}

Nothing interesting in here. Naturally, the next breakpoint will be loc_407100() where the "Verifying Password" animation is made:
Verifying Password animation, IDA decompilevoid __cdecl sub_407100(int a1) {
// trimmed
    v57 = &_vbaExceptHandler;
    v56 = v1;
    v48 = v2;
    v47 = v3;
    v58 = (int)&v47;
    v59 = (int)dword_401108;
    v60 = a1 & 1;
    v62 = a1 & 0xFFFFFFFE;
    (*(_DWORD *)v62 + 4)(v62, v3, v4, v2, v49, v50, v51, v52, v53, v54, v55, v1,
                         &_vbaExceptHandler, &v47, dword_401108, v60);
    v5 = *(_DWORD *)v62;
    v60 = v62;
    v55 = 0;
    v54 = 0;
    v6 = (*(int (__stdcall **)(int))(v5 + 0x30C))(v62);
    v7 = _vbaObjSet(&v54, v6);
    v8 = v7;
    v60 = (int)&v55;
    v59 = v7;
    v9 = (*(int (__stdcall **)(int, int *))(*(_DWORD *)v7 + 0x50))(v7, &v55);
    __asm { fnclex }
    if (v9 < 0)
        _vbaHresultCheckObj(v9, v8, dword_40665C, 0x50);
    v10 = -(_vbaStrCmp(L"Verifying Password", v55) == 0);
    _vbaFreeStr();
    _vbaFreeObj(&v54);
    v11 = *(_DWORD *)v62;
    savedregs = v62;
    if ((_WORD)v10) {
        v12 = (*(int (__cdecl **)(int))(v11 + 0x30C))(savedregs);
        v13 = _vbaObjSet(&v54, v12);
        v14 = v13;
        v60 = (int)L".Verifying Password.";
        v59 = v13;
        v15 = (*(int (__stdcall **)(int, _DWORD))(*(_DWORD *)v13 + 84))(v13, L".Verifying Password.");
        __asm { fnclex }
        if ( v15 < 0 )
            goto LABEL_15;
        goto LABEL_16;
    }
// trimmed

This code doesn't seems to be interesting. All it does is to update the status message as animation, so I let the program continue running until.
Next breakpoint was at address 00406319 where the bad message is shown - loc_407410():
Final stage, IDA decompilevoid *__cdecl sub_407410(int a1) {
    // trimmed
    _vbaStrCopy(&v420, L"abcdefghijklmnopqrstuvwxyz.!:-");                      // a character set string, used later
    v2 = (*(int (__cdecl **)(unsigned int))(*(_DWORD *)v1 + 0x308))(v1);        // __vbaGosubFree+69E
    v3 = _vbaObjSet(&v414, v2);
    v4 = v3;
    v5 = (*(int (__cdecl **)(int, int *))(*(_DWORD *)v3 + 0xA0))(v3, &v417);    /// IID_IVbaHost+D663 get the user password
    __asm { fnclex }                                                            //| check if the user entered something
    if ( v5 < 0 )                                                               //|
        _vbaHresultCheckObj(v5, v4, dword_406598, 0xA0);                        //\
    v6 = (*(int (__cdecl **)(unsigned int))(*(_DWORD *)v424 + 0x308))(v424);    // __vbaGosubFree+69E
    v7 = _vbaObjSet(&v413, v6);
    v8 = v7;
    v9 = (*(int (__cdecl **)(int, int *))(*(_DWORD *)v7 + 0xA0))(v7, &v416);    /// IID_IVbaHost+D663 get the user password again
    __asm { fnclex }                                                            //| check if the user entered something
    if ( v9 < 0 )                                                               //|
        _vbaHresultCheckObj(v9, v8, dword_406598, 0xA0);                        //\
    v412 = v416;
    v410 = 1;
    v409 = 2;
    v416 = 0;
    v411 = 8;
    rtcMidCharVar(&v408, &v411, 3, &v409);                        // get the third character from the user code
    v10 = _vbaStrVarVal(&v415, &v408, dword_40667C, 1, -1, 0);    /// that dword_40667C is actually pointer
                                                                  //| to a string containing a single "\x20" (SPACE) character
    v15 = rtcReplace(v417, v10, v11, v12, v13, v14);              //| this replaces all occurrences of the taken character to "\x20"
                                                                  //| 
    _vbaStrMove(&v419, v15);                                      //\ In the end, my password "123123123" becomes "12 12 12 "
    _vbaFreeStrList(2, &v417, &v415);
    _vbaFreeObjList();
    _vbaFreeVarList(3, &v411, &v409, &v408, 2);
    v276 = &v420;
    v275 = 0x4008;
    rtcUpperCaseVar(&v411, &v275);              // Converts the charset string to UPPERCASE
    v260 = v419;
    v259 = 0x8008;
    v410 = 1;
    v409 = 2;
    rtcMidCharVar(&v408, &v411, 3, &v409);      // grab the 3rd character ('C') from the uppercase charset string
    v407 = 1;
    v406 = 2;
    v274 = &v420;
    v273 = 0x4008;
    rtcMidCharVar(&v405, &v273, 18, &v406);     // grab the 18th character ('r') from the lowercase charset string
    v271 = 8;
    v269 = 8;
    v272 = dword_40667C;
    v270 = (int)dword_40667C;
    v268 = &v420;
    v400 = 1;
    v399 = 2;
    v267 = 0x4008;
    rtcMidCharVar(&v397, &v267, 16, &v399);     // grab the 16th character ('p') from the lowercase charset string
    v264 = &v420;
    v266 = dword_40667C;
    v265 = 8;
    v394 = 1;
    v393 = 2;
    v263 = 0x4008;
    rtcMidCharVar(&v392, &v263, 18, &v393);     // grab the 18th character ('r') from the lowercase charset string
    v16 = _vbaVarCat(&v404, &v405, &v408, &v259);    /// concat "C" and "r"
    v18 = _vbaVarCat(&v402, &v271, v16, v17);                //| concat "Cr" and " " (Space character)
    v20 = _vbaVarCat(&v401, &v269, v18, v19);                //| concat "Cr " and " " (Space character)
    v22 = _vbaVarCat(&v396, &v397, v20, v21);                //| concat "Cr  " and "p"
    v24 = _vbaVarCat(&v395, &v265, v22, v23);                //| concat "Cr  p" and " " (Space character)
    v26 = _vbaVarCat(&v391, &v392, v24, v25);                //\ concat "Cr  p " and "r"
    LOWORD(v186) = _vbaVarTstEq(v26);                                // compares "Cr  p r" with my modified password "12 12 12 "
    _vbaFreeVarList(15, &v411, &v409, &v406, &v408);
    if ((_WORD)v186) {
// trimmed

The comments I add are based on debugging the application in x86dbg and verifying the values.
Let's sum it up:
1. The third character from the user's code is taken
2. All occurrences of this character are replaced with " " (Space)
3. The string "Cr p r" is constructed, using upper and lower cased letters from a character set
4. The modified user code is compared with the constructed one "Cr p r"

Basically I need to construct a 7 characters long string, where the third, fourth and sixth are the same and they aren't 'C', 'r' or 'p'.
Even better, I can set the fourth and the sixth characters to be spaces, and only need to pick a character for the 3rd one.
Alright, I'll try my crafted code "Cr1 p r":


Notice how the spaces were actually removed from the user code field? Remember that event that was triggered on every key stroke inside the edit field?
Let's see its code at address 00406D60:
Key stroke event, IDA decompile// trimmed
    v37 = v45;
    v16 = *(_DWORD *)v14;
    v17 = rtcReplace(v45, dword_40667C, &dword_40661C, 1, -1, 0);  // dword_40667C is holding a " " (Space) character
                                                                   // dword_40661C is holding a \x00 character, used as string terminator
    v18 = _vbaStrMove(&v44, v17);
    v42 = (int *)v18;
// trimmed

So the key stroke event actually removed the SPACE characters from the user code, while the user enters it.
Alright, let's try another code then, following the plan A - third, fourth and sixth characters to be the same, like "Cr11p1r" for example:


Seems that's all? Ok, like in the previous challenge I'll throw a hint here. Even if the app accepts it that doesn't mean my password is correct.
Here the app will accept any well crafted password, but the site will accept the only one that's a "real word".
It's pretty obvious that the right password is "Creeper", right? There no hints inside the app so generating a list of the possible passwords can make it more obvious:
Possible valid passwords generator, Pythonfor c in "abcdefghijklmnopqrstuvwxyz.!:-":
    print "Cr%c%cp%cr" % (c, c, c)

List of possible valid passwords, outputCraapar
Crbbpbr
Crccpcr
Crddpdr
Creeper
Crffpfr
Crggpgr
Crhhphr
Criipir
Crjjpjr
Crkkpkr
Crllplr
Crmmpmr
Crnnpnr
Croopor
Crppppr
Crqqpqr
Crrrprr
Crsspsr
Crttptr
Cruupur
Crvvpvr
Crwwpwr
Crxxpxr
Cryypyr
Crzzpzr
Cr..p.r
Cr!!p!r
Cr::p:r
Cr--p-r

That wasn't hard at all, to be honest.
Oh, and one more thing. The correct/wrong password messages are both constructed character by character as usual, after that _vbaVarTstEq().

Application Challenge #13 (Windows)


Target: app13win.exe
Requirement: Find the numbers
Difficulty: medium

Well, that's something fresh:


According to DiE it also has some protections:


According to the authors, I should think out of the box, and not jump straight into debugging it.
They also gave a small hint in the challenge's description, so let's think of a solution using that hint.
The hint talks about how smart cards were (i don't think that trick works nowadays) being cracked in the beginning of the 2000's.
Basically by observing the current drops and spikes, you could narrow a bruteforce algorithm ranges.

The way this app works is to validate the user code chunk by chunk, so if the first one isn't correct, the second, third and fourth validation wont even execute.
The smartcard thing is pretty... smart (ignore the pun) and I can apply that logic here.
But I can't measure voltage and current, and even if I could that would be pointless on a multi-threading machine, like the nowadays computers are.
What I can measure however is the execution time, or the time that it takes to run a certain command or piece of code.

A simple code like this:
Example code, Python 3sum = 0
for i in range(0, 1000):
    sum += 1
print(sum)
will take only a second to execute. But what if I extend the range to 1 000 000 or 1 000 000 000?
The execution time will of course rise.

The hint basically says exactly this - measure the execution time.
So here's the plan:
1. brute the first part, by trying every number between 1 and 999, and set 1 for the part 2, 3 and 4
2. measure the time it takes to verify the number (start time minus end time)
3. continue down to the last code part

So, this is the code I ended up with:
valid user code bruteforcerimport time
import os

def brute(format):
    code = 0
    time_peak = 0.0
    for i in range(1, 1000):
        time_start = time.time()
        os.system("app13win.exe " + (format % i))
        time_delay = time.time() - time_start
        if time_delay > time_peak:
            time_peak = time_delay
            code = i
    return code

pt1 = brute("%d 1 1 1")
pt2 = brute("%d %%d 1 1" % pt1)
pt3 = brute("%d %d %%d 1" % (pt1, pt2))
pt4 = brute("%d %d %d %%d" % (pt1, pt2, pt3))

There's one pitfall here.
I was able to get the valid code two out of a five tries running that script, and I actually was lucky I'm running it in a virtual machine.
If you just run the bruteforce and fire up some game to kill the time, you will probably never get the correct password.
It's a precise analysis and other processes running in the meantime will add up delay to the python execution time.
So, what I did was running the bruteforce under window safe mode with command line only.
This way I was able to get a valid code five times out of five tires:


Alright, challenge completed!
But why stop here? Let's see what's going on and actually reverse engineer it.
I already know the executable is protected by Yoda's Crypter, so before anything else I have to deal with this.
There's plenty of tutorials on how to strip Yoda's Crypter, so I'm, not going to reinvent the wheel here.

After stripping the protection i can now load the executable in IDA and decompile it:
App13, IDA decompileint __cdecl main(int argc, const char **argv, const char **envp) {
    // stripped

    // First we have the "usage" screen, printed when the user didn't entered all the parameters:
    if (argc != 5) {
        printf(&strTable[0x108]);    // "\n\n\nusage:\n======\n"
        printf(&strTable[0x11A]);    // "                    app13win.exe <num1> <num2> <num3> <num4>\n"
        printf(&strTable[0x158]);    // "           Example: app13win.exe  123    289    673    98\n\n\n\n"
        printf(&strTable[0x196]);    // "       All numbers must be in the range   0 < number < 1000\n"
        printf(&strTable[0x1D3]);    // "       If all numbers are correct, the program will give you\n"
        printf(&strTable[0x211]);    // "       the final password, otherwise it quits without any message.\n\n\n"
        printf(&strTable[0x257]);    // "       This app was  designed to be as hard to  debug as possible.\n"
        printf(&strTable[0x29B]);    // "       It uses several methods to detect if a debugger is present,\n"
        printf(&strTable[0x2DF]);    // "       some of the code that validates the user - input is called\n"
        printf(&strTable[0x322]);    // "       by timer interrupt service - routines and some of the code\n"
        printf(&strTable[0x365]);    // "       is self  modifying.   So debugging  is (almost) impossible\n"
        printf(&strTable[0x3A8]);    // "       and not the way to beat this app.  But there _is_ a design\n"
        printf(&strTable[0x3EB]);    // "       problem you can use to find the correct input numbers.\n"
        printf(&strTable[0x42A]);    // "       Think a bit different ;-)\n"
        printf(&strTable[0x44C]);    // "       By the way,  did you know that some hackers  cracked smart\n"
        printf(&strTable[0x48F]);    // "       cards   by monitoring their  electrical  power consumption \n"
        printf(&strTable[0x4D3]);    // "       while trying different passwords?\n\n"
        printf(&strTable[0x4FE]);    // "       Happy cracking!\n"
        printf(&strTable[0x516]);    // "       html\n\n\n"
        printf(&strTable[0x525]);    // "                    <<       Hit any key         >>\n"
        getch();
        exit(0);
    }

    // Next, the parameter validity verification is placed:
    pt1 = 0;
    pt2 = 0;
    pt3 = 0;
    pt4 = 0;
    sscanf(argv[1], &strTable[0x55A], &pt1);    // "%d"
    sscanf(argv[2], &strTable[0x55D], &pt2);    // "%d"
    sscanf(argv[3], &strTable[0x560], &pt3);    // "%d"
    sscanf(argv[4], &strTable[0x563], &pt4);    // "%d"
    if (pt1 <= 0 || pt1 >= 1000 || pt2 <= 0 || pt2 >= 1000 || pt3 <= 0 || pt3 >= 1000 || pt4 <= 0 || pt4 >= 1000 ) {
        printf(&strTable[0x566]);    // "\n\n\nNot all numbers are in the range   0 < number < 1000\n\n\n"
        printf(&strTable[0x5A1]);    // "          <<  Hit any key to continue  >>\n"
        getch();
        exit(0);
    }

    // If the four parameters are not in range of 1 to 999, the program exits with exit(0):
    v3 = strlen(*argv) - 1;
    for (i = &(*argv)[v3]; (v3 & 0x80000000) == 0 && *i != '\\'; --i)
        --v3;
    strcpy((char *)&szExeName, &(*argv)[v3 + 1]);

    // Preserve the user input
    strcpy((char *)&pt1_str, argv[1]);
    strcpy((char *)&pt2_str, argv[2]);
    strcpy((char *)&pt3_str, argv[3]);
    strcpy((char *)&pt4_str, argv[4]);

    // Code below does the delay routine that helped me find the correct code, by observing the execution time:
    initial_tickcount = GetTickCount();                                     // Take the current tick count
    dest = malloc(0x3E8u);
    crc32_table = (int)malloc(0x400u);                                      // Allocate space for crc32 table...
    crc32_create_table(crc32_table);                                        // ...and generate the table itself
    data_size1 = (char *)data1_proc_end - (char *)(int (*)())data1_proc;    // Calculate the size of data1_proc
    data1 = data1_proc;                                                     // Assign data1_proc to data1
    data_size2 = (char *)data2_proc_end - (char *)(int (*)())data2_proc;    // Calculate the size of data2_proc
    data2 = data2_proc;                                                     // Assign data2_proc to data2
    current_part = pt1;                                                     // Initialize current_part as pt1
    signal(4, signal_A);                                                    // Create a signal_A with id 4
    signal(8, signal_B);                                                    // Create a signal_B with id 8
    timer1 = CreateWaitableTimerA(0, 0, &strTable[0x5CC]);                  // "KEYBOARD"
    DueTime = (LARGE_INTEGER)0xFFFFFFFFFECED300i64;                         // 2000 ms timeout
    SetWaitableTimer(timer1, &DueTime, 30, timer_complete1, 0, 0);          // Run timer_complete1 every 30ms, after 2000ms
    timer2 = CreateWaitableTimerA(0, 0, &strTable[0x5D5]);                  // "MOUSE"
    DueTime = (LARGE_INTEGER)0xFFFFFFFFFEB04E80i64;                         // 2200 ms timeout
    SetWaitableTimer(timer2, &DueTime, 25, timer_complete2, 0, 0);          // Run timer_complete2 every 25ms, after 2200ms
    timer3 = CreateWaitableTimerA(0, 0, &strTable[0x5DB]);                  // "INT3"
    DueTime = (LARGE_INTEGER)0xFFFFFFFFFFFF8AD0i64;                         // 3 ms timeout
    SetWaitableTimer(timer3, &DueTime, 5, timer_complete3, 0, 0);           // Run timer_complete3 every 5ms, after 3ms

    // This code here looks kind of promising:
    v8 = 0;
    do {
        SleepEx(0xAu, 1);
        CRC_sum = CRC(CRC_sum, v8 * pt2 + v8 * pt1 - v8 * pt3 - v8 * pt4);
        ++v8;
    } while(v8 < 255);

    if(CRC_sum == 0x435F2C82) {
        printf(&strTable[0x5E0]);                                          // "\nThe numbers are all correct.\n"
        printf(&strTable[0x5FF]);                                          // "The final password is:\n"
        printf(&strTable[0x617], &pt1_str, &pt2_str, &pt3_str, &pt4_str);  // "%s-%s-%s-%s\n\n"
        printf(&strTable[0x625]);                                          // "<<    Hit any key    >>\n"
        getch();
    }
    return 1;
}

So, if the final checksum equals 0x435F2C82, this is considered as correct password.
That's nice, because I can now reverse engineer the CRC() routine:
Correct code calculator, IDA decompileint __cdecl CRC(int crc, int sum) {
    sprintf(&data, format, crc);
    i = 0;
    p_data = &data;
    while (strlen(&data) > i) {
        *p_data = (sum + *p_data) % 0xFF;
        ++i;
        ++p_data;
    }
    data_len = strlen(&data);
    return crc32((int)&data, data_len);    // Standard CRC32
}
That's really simple.

The checksum and the result of the (v8 * pt2 + v8 * pt1 - v8 * pt3 - v8 * pt4) calculation are passed to function that takes these two values to calculate a new checksum.
Let's put that to the test and write a proof of concept code:
CRC verification PoCDWORD CRC(DWORD crc, DWORD sum) {
    char data[MAX_PATH];

    sprintf(data, "%u", crc);
    for(DWORD i = 0; i < strlen(data); i++) {
        data[i] = (data[i] + sum) % 0xFF;
    }
    return crc32(data, strlen(data));
}

int main() {
    DWORD CRC_sum = 0;
    DWORD parts[4] = {537, 314, 137, 616};

    for (int i = 0; i < 0xFF; i++) {
        CRC_sum = CRC(CRC_sum, i * parts[1] + i * parts[0] - i * parts[2] - i * parts[3]);
    }

    printf("Final checksum: %08X\n", CRC_sum);
    return 0;
}

My code gave me 0xDF493F04 and I've expected to get 0x435F2C82.
Running the app in the debugger with the correct key, it never get me to that comparison.
Whoops, seems like I took the bait, because that appears to be a fake verification.

Alright, let's get back to the timer thingy and write down some logic.
There are three timers - timer1, timer2 and timer3. The order of execution of their pfnCompletionRoutine is: timer3 after 3ms, timer1 after 2000ms and timer2 after 2200ms.
So, let's start with timer3 and its pfnCompletionRoutine - timer_complete3():
timer_complete3(), IDA decompilevoid __stdcall timer_complete3(LPVOID lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue) {
    raise(8);
    v3 = LoadLibraryA(&strTable[124]);
    if (v3) {
        h_IsDebuggerPresent = (int)GetProcAddress(v3, &strTable[137]);
        if (((int (*)(void))h_IsDebuggerPresent)())
            CRC_sum_final = CRC(CRC_sum_final, dword_40F0BC + 7);
    }
    CRC_sum_final = CRC(CRC_sum_final, dword_40F0BC + 7);
    raise(4);
    switch (dword_40F0BC) {
        case 4:
            current_part = pt2;
            if (CRC_sum_final != 0x98F52A54)
                _exit(0);
            pt2_CRC_sum = CRC_sum_final;
        break;
        case 8:
            current_part = pt3;
            if (CRC_sum_final != 0x7023AE57)
                _exit(0);
            pt3_CRC_sum = CRC_sum_final;
        break;
        case 12:
            current_part = pt4;
            if (CRC_sum_final != 0x8986EE55)
                _exit(0);
            pt4_CRC_sum = CRC_sum_final;
        break;
    }
    CRC_sum_final += GetTickCount() - initial_tickcount > 0x2710;
    raise(8);
    v4 = dword_40F0BC++;
    if (v4 == 16) {
        dword_412520 = CRC_sum_final;
        if (CRC_sum_final != 0xD9D9886E)
            _exit(0);
        printf(&strTable[0xA8]);                                            // "\nThe numbers are all correct!\n"
        printf(&strTable[0xC7]);                                            // "The final password is:\n"
        printf(&strTable[0xDF], &pt1_str, &pt2_str, &pt3_str, &pt4_str);    // "%s-%s-%s-%s\n\n"
        printf(&strTable[0xED]);                                            // "  <<  Hit any key   >>\n"
        getch();
        _exit(0);
    }
    CRC_sum_final += GetTickCount() - initial_tickcount > 0x2710;
    raise(4);
}

Nice. There's a lot of code that will try to detect debugging.
IsDebuggerPresent() is easily fooled by patching the PEB in x86dbg.
The timeout check will mess up the CRC_sum_final value by either adding 0 if the execution is going normally, or adding 1 if the execution time exceeds 0x2710ms (10 seconds).

According to the code above, it takes 16 complete iterations of this code to verify if the user code is correct or not.
So, having in mind that timer_complete3() is executed 3ms after creating its timer and it runs every 5ms until it completes 16 full runs, we can calculate the complete time the verification process will take - 3 + 5 * 16 = 83ms
Now, that's a rough calculation and would be valid if we assume the code execution itself will take 0ms, but still, even if we double or triple that value, it will be way less than the starting timers of timer_complete1() and timer_complete2().
So, it appears that timer_complete1() and timer_complete2() are there to mess up our results in case "it takes longer" (as when tracing in a debugger *wink-wink*) to execute the verification procedure.

I can now start designing my PoC code by stripping the bad code:
cleaned timer_complete3(), IDA decompilevoid __stdcall timer_complete3(LPVOID lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue) {
    raise(8);
    CRC_sum_final = CRC(CRC_sum_final, dword_40F0BC + 7);
    raise(4);
    switch (dword_40F0BC) {
        case 4:
            current_part = pt2;
            if (CRC_sum_final != 0x98F52A54)
                _exit(0);
            pt2_CRC_sum = CRC_sum_final;
        break;
        case 8:
            current_part = pt3;
            if (CRC_sum_final != 0x7023AE57)
                _exit(0);
            pt3_CRC_sum = CRC_sum_final;
        break;
        case 12:
            current_part = pt4;
            if (CRC_sum_final != 0x8986EE55)
                _exit(0);
            pt4_CRC_sum = CRC_sum_final;
        break;
    }
    raise(8);
    v4 = dword_40F0BC++;
    if (v4 == 16) {
        dword_412520 = CRC_sum_final;
        if (CRC_sum_final != 0xD9D9886E)
            _exit(0);
        printf(&strTable[0xA8]);                                            // "\nThe numbers are all correct!\n"
        printf(&strTable[0xC7]);                                            // "The final password is:\n"
        printf(&strTable[0xDF], &pt1_str, &pt2_str, &pt3_str, &pt4_str);    // "%s-%s-%s-%s\n\n"
        printf(&strTable[0xED]);                                            // "  <<  Hit any key   >>\n"
        getch();
        _exit(0);
    }
    raise(4);
}

I already reverse engineered the CRC() function for the trap I previously feel in, so only the two signals raise(4) and raise(8) needs to be analysed.
Starting with raise(4), that points to signal_A():
signal_A(), IDA decompilevoid __cdecl signal_A() {
    memcpy(dest, data1, data_size1);
    ((void (__cdecl *)(_DWORD))dest)(current_part);
    v0 = LoadLibraryA(strTable);
    if (v0) {
        h_IsDebuggerPresent = (int)GetProcAddress(v0, ProcName);
        if (((int (*)(void))h_IsDebuggerPresent)())
            CRC_sum_final = CRC(CRC_sum_final, dword_40F0AC + 0xD);
    }
    CRC_sum_final = CRC(CRC_sum_final, dword_40F0AC);
    CRC_sum_final += GetTickCount() - initial_tickcount > 0x2710;
    signal(4, signal_A);
    ++dword_40F0AC;
}

The same IsDebuggerPresent() and GetTickCount() checks are present here, and again I can strip them for my PoC code:
cleaned signal_A(), IDA decompilevoid __cdecl signal_A() {
    memcpy(dest, data1, data_size1);
    ((void (__cdecl *)(_DWORD))dest)(current_part);
    CRC_sum_final = CRC(CRC_sum_final, dword_40F0AC);
    signal(4, signal_A);
    ++dword_40F0AC;
}

The signal_B() procedure is almost the same, so I did that too:
cleaned signal_B(), IDA decompilevoid __cdecl signal_B() {
    memcpy(dest, data2, data_size2);
    ((void (__cdecl *)(_DWORD))dest)(current_part);
    CRC_sum_final = CRC(CRC_sum_final, dword_40F0B0 + 0xD);
    signal(8, signal_B);
    ++dword_40F0B0;
}

Only thing that left are those data1() (data1_proc()) and data2() (data2_proc()) that are first copied by memcpy() then called by passing the current_part value.
data1()/data1_proc(), IDA decompileunsigned int __cdecl data1_proc(int a1) {
    v1 = 0;
    for ( i = 0; i < (signed int)data_size1; ++i ) {
        v3 = *((_BYTE *)dest + i) - (signed int)*((_BYTE *)data1 + i);
        v1 += (HIDWORD(v3) ^ v3) - HIDWORD(v3);
    }
    v4 = -1;
    v5 = 0;
    v6 = crc32_table;
    do {
        v7 = ((unsigned int)v4 >> 8) ^ *(_DWORD *)(crc32_table + 4 * (unsigned __int8)(v4 ^ (v1 + a1 + *(_DWORD *)v6) % 0xFFu));
        v4 = ((unsigned int)v4 >> 8) ^ *(_DWORD *)(crc32_table + 4 * (unsigned __int8)(v4 ^ (v1 + a1 + *(_DWORD *)v6) % 0xFFu));
        ++v5;
        v6 += 4;
    } while(v5 < 0x400);
    result = (unsigned int)(~v7 * v1 + a1 + CRC_sum_final) >> 1;
    CRC_sum_final = (unsigned int)(~v7 * v1 + a1 + CRC_sum_final) >> 1;
    return result;
}

Alright, that's simple.
That for loop subtract byte from the original data1_proc() from a byte from the memory copied data1() then adds the result to a variable v1.
The idea behind it is to create a difference checksum between the static code (data1_proc) and the one that is currently executed (data1), thus detecting a breakpoints placed on the currently executed code.
So, in a non-debugged environment, the final sum that v1 holds should be 0, as there shouldn't be any difference between both codes.

Moving to the do-while loop. One thing that puzzled me at first was the fact it iterates through crc32_table that should have 0x100 DWORDS in it, but the loop runs as it holds 0x400 DWORDS.
That's an obvious buffer overflow and the result of the for loop and the code that follows explains everything:
explained and cleaned data1()/data1_proc(), IDA decompileunsigned int __cdecl data1_proc(int a1) {
    // v7 - the result of the buffer overflowed while() loop
    // v1 - the result of the for() that it should always be 0
    // a1 - passed argument - current_part
    // CRC_sum_final = (~v7 * 0 + a1 + CRC_sum_final) >> 1;
    CRC_sum_final = (a1 + CRC_sum_final) >> 1;
}

As simple as that!
The data2() / data2_proc() is almost the same:
cleaned data2()/data2_proc(), IDA decompileunsigned int __cdecl data2_proc(int a1) {
    CRC_sum_final += 3;
    CRC_sum_final = (a1 + CRC_sum_final) >> 1;
}

Cool, now I got everything I need to write a proof of concept code that actually work and verify everything so far:
proof of concept, IDA decompileint main() {
    DWORD CRC_sum_final, dword_40F0AC, dword_40F0B0, dword_40F0BC, current_part;
    DWORD parts[4] = {537, 314, 137, 616};

    CRC_sum_final = 0;
    dword_40F0AC = 1;
    dword_40F0B0 = 1;
    dword_40F0BC = 1;

    current_part = parts[0];

    printf("Testing: %d-%d-%d-%d\n", parts[0], parts[1], parts[2], parts[3]);

    for(int i = 0; i <= 16; i++) {
        // signal_B() and data2_proc()
        CRC_sum_final = CRC(((current_part + CRC_sum_final + 3) >> 1), dword_40F0B0 + 0xD);
        dword_40F0B0++;

        CRC_sum_final = CRC(CRC_sum_final, dword_40F0BC + 7);

        // signal_A() and data1_proc()
        CRC_sum_final = CRC(((current_part + CRC_sum_final) >> 1), dword_40F0AC);
        dword_40F0AC++;

        switch (dword_40F0BC) {
            case 4:
                current_part = parts[1];
                if (CRC_sum_final == 0x98F52A54)
                    printf("Pt1 matched!\n");
            break;
            case 8:
                current_part = parts[2];
                if (CRC_sum_final == 0x7023AE57)
                    printf("Pt2 matched!\n");
            break;
            case 12:
                current_part = parts[3];
                if (CRC_sum_final == 0x8986EE55)
                    printf("Pt3 matched!\n");
            break;
        }

        // signal_B() and data2_proc()
        CRC_sum_final = CRC(((current_part + CRC_sum_final + 3) >> 1), dword_40F0B0 + 0xD);
        dword_40F0B0++;

        if (dword_40F0BC == 16) {
            if (CRC_sum_final == 0xD9D9886E)
                printf("Pt4 matched!\n");
            break;
        }
        dword_40F0BC++;

        // signal_A() and data1_proc()
        CRC_sum_final = CRC(((current_part + CRC_sum_final) >> 1), dword_40F0AC);
        dword_40F0AC++;
    }
}

And I get the following output:
Console outputTesting: 537-314-137-616
Pt1 matched!
Pt2 matched!
Pt3 matched!
Pt4 matched!

So my proof of concept proves my concept (lame pun was intended).
To fully complete that challenge I wrote a (quite hacky in the shitty way) bruteforcer that will find the correct code:
Application challenge 13 - reverse engineered bruteforcer// HackThisSite / application challenge #13
// Reverse engineered bruteforcer
// XpoZed, nullsecurity.org / 30 Oct, 2017
#include <windows.h>
#include <stdio.h>

// crc32 using a short table
DWORD crc32(byte *data, DWORD data_len) {
    DWORD   crc;
    DWORD   crc32table[] = {0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC,
                            0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C,
                            0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C,
                            0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C};

    crc = ~0;
    for(DWORD i = 0; i < data_len; i++) {
        crc = (crc >> 0x04) ^ crc32table[(data[i] ^ crc) & 0x0F];
        crc = (crc >> 0x04) ^ crc32table[(data[i] >> 0x04) ^ (crc & 0x0F)];
    }

    return ~crc;
}

// custom CRC used by he challenge
int CRC(int crc, int sum) {
    char data[MAX_PATH];
    int data_len;

    data_len = sprintf(data, "%u", crc);
    for(int i = 0; i < data_len; i++) {
        data[i] = (data[i] + sum) % 0xFF;
    }
    return crc32(data, strlen(data));
}

// calculator
BOOL check(DWORD pt1, DWORD pt2, DWORD pt3, DWORD pt4) {
    DWORD sum, pt, A;

    sum = 0;
    A = 1;
    pt = pt1;

    for (int i = 1; i <= 16; i++, A+=2) {
        sum = CRC(((sum + 3 + pt) >> 1), A + 0x0D);
        sum = CRC(sum, i + 0x07);
        sum = CRC(((sum + pt) >> 1), A);

        switch(i) {
            case 4:
                if (sum == 0x98F52A54 && pt2 == -1)
                    return TRUE;
                pt = pt2;
            break;
            case 8:
                if (sum == 0x7023AE57 && pt3 == -1)
                    return TRUE;
                pt = pt3;
            break;
            case 12:
                if (sum == 0x8986EE55 && pt4 == -1)
                    return TRUE;
                pt = pt4;
            break;
        }

        sum = CRC(((sum + 3 + pt) >> 1), A + 1 + 0x0D);
        if (i == 16) {
            if (sum == 0xD9D9886E)
                return TRUE;
            break;
        }

        sum = CRC(((sum + pt) >> 1), A+1);
    }

    return FALSE;
}

int main() {
    DWORD parts[4] = {0, -1, -1, -1};
    for(int i = 0; i < 4; i++) {
        for(parts[i] = 1; parts[i] <= 999; parts[i]++) {
            if (check(parts[0], parts[1], parts[2], parts[3]))
                break;
        }
    }

    printf("Correct code: %d-%d-%d-%d\n", parts[0], parts[1], parts[2], parts[3]);

    return 0;
}

And that completes application challenge number 13!

Application Challenge #14 (Windows)


Target: app14.exe
Requirement: It's Windows only for a reason.
Difficulty: easy

Well, that's an odd requirement. The application is written in .NET:


The challenge itself looks a bit strange:


There's some great tools for .NET applications such is ILSpy, so let's see what we can pull out of this app.
Diving directly into ILSpy leads to here:


There's two classes - "goes" and "Encrypt", where "goes" is the main() procedure.
The button is named "vla", so I'll have to look for a Click action and this action is located here:
vla_Click(), ILSpy decompileprivate void vla_Click(object sender, EventArgs e) {
  int[] array = new int[12];
  VariantType variantType = (VariantType)Conversions.ToInteger(
    MyProject.Computer.Registry.GetValue("HKEY_CURRENT_USER\\valid", "", 0)
  );
  decimal value = new decimal(((double)variantType + 1.5) * (double)variantType + 0.025);
  if (Operators.CompareString(this.txt.Text, "somerandomvl", false) == 0) {
    Interaction.MsgBox(
      RuntimeHelpers.GetObjectValue(
        Encrypt.ParseandDecrypt(Conversions.ToString(Encrypt.ParseandEncrypt("ydXX!if not txt is blah youfailed")))
      ), MsgBoxStyle.Exclamation, "585mfg9gf");
  } else {
    Interaction.MsgBox(
      RuntimeHelpers.GetObjectValue(
        Encrypt.ParseandDecrypt("ydXX!if not txt is blah youfailed")
      ), MsgBoxStyle.Exclamation, "585mfg9gf");
  }
  if (Convert.ToDouble(value) == ((double)variantType + 1.5) * (double)variantType + 0.025) {
    if (variantType == VariantType.Empty | MyProject.Application.Info.DirectoryPath.Length.ToString().Length == 458) {
      Interaction.MsgBox("nope", MsgBoxStyle.Information, "sorry");
    } else if ((Operators.CompareString(this.txt.Text, this.txt.Text, false) != 0 & false) | false | variantType == VariantType.Null) {
      this.txt.Text = Conversions.ToString(Encrypt.ParseandDecrypt("fm`{f}kpwrn"));
    }
  }
}

Alright, looks simple enough.
First, a value from the registry key "HKEY_CURRENT_USER\valid" is pulled to variantType.
That registry key is not standard, and it shouldn't exist on your system.
It's actually created and set to 0 here:
vla_Click(), ILSpy decompileprivate void goes_Load(object sender, EventArgs e) {
  MyProject.Computer.Registry.CurrentUser.CreateSubKey("valid");
  MyProject.Computer.Registry.SetValue("HKEY_CURRENT_USER\\valid", "", "0");
}

After that, the variable "value" is set to (variantType + 1.5) * (variantType + 0.025), which is ((0 + 1.5) * 0 + 0.025) = 0.025.

Next a if-else statement compares the user entered password with the word "somerandomvl".
Using that password gives the following message, that is obviously not what we are looking for:


The if-elseif statement is where I should look - "Convert.ToDouble(value) == ((double)variantType + 1.5) * (double)variantType + 0.025".
This will always be true, so i can move to the actual bad/good message code inside.
Well, this looks pointless. According to this if-elseif code I'll never be able to get to the correct message.
I guess I need to reverse engineer the Decrypt routine and decrypt it by myself?
Alright then, let's see what the Encrypt class holds:
Encrypt.ParseandDecrypt(), ILSpy decompilepublic static object ParseandDecrypt(string stringd) {
  int[] array = new int[128];
  int num = 0;
  checked {
    do {
      if (num % 2 == 0) {
        array[num] = num - 3;
      }
      if (num % 2 == 1) {
        array[num] = num + 4;
      }
      num++;
    } while (num <= 127);
    num = 0;
    string text = "";
    int arg_4B_0 = 1;
    int length = stringd.Length;
    for (int i = arg_4B_0; i <= length; i++) {
      string @string = Strings.Mid(stringd, i, 1);
      int charCode = Strings.Asc(@string) - array[num];
      text += Conversions.ToString(Strings.Chr(charCode));
      num++;
      if (num > 127) {
        num = 0;
      }
    }
    return text;
  }
}

Porting the ParseandDecrypt() method to C code, (because fuck .NET):
ParseandDecrypt(), C implementationvoid ParseandDecrypt(char *data) {
    int array[128];

    for (int i = 0; i < 127; i++) {
        if (i % 2 == 0) {
            array[i] = i - 3;
        } else if (i % 2 == 1) {
            array[i] = i + 4;
        }
    }

    for (int i = 0; i < strlen(data); i++) {
        data[i] -= array[i % 127];
    }
}

And passing the "fm`{f}kpwrn" to it, I got the site's password - "ihatethereg".

The Encrypt class also has a ParseandEncrypt() method that I ported to C.
For the heck of it, here's a two way encryption proof of concept for this challenge:
Encrypt/Decrypt proof of concept, C implementation#include <windows.h>
#include <stdio.h>

#define MODE_DECRYPT 0
#define MODE_ENCRYPT 1

void crypt(char *data, int mode) {
    int array[128];

    for (int i = 0; i < 127; i++) {
        if (i % 2 == 0) {
            array[i] = i - 3;
        } else if (i % 2 == 1) {
            array[i] = i + 4;
        }
    }

    for (int i = 0; i < strlen(data); i++) {
        if (mode == MODE_DECRYPT) {
            data[i] -= array[i % 127];
        } else {
            data[i] += array[i % 127];
        }
    }
}

int main() {
    char data[] = "fm`{f}kpwrn";

    printf("encrypted: '%s'\n", data);

    crypt(data, MODE_DECRYPT);
    printf("decrypted: '%s'\n", data);

    crypt(data, MODE_ENCRYPT);
    printf("encrypted: '%s'\n", data);

    return 0;
}

And that's everything about application challenge #14.

Application Challenge #15 (Windows)


Target: app.exe
Requirement: Think N64 hacking.
Difficulty: hard

According to DiE this one is written in C++:


Let's see how the challenge itself looks:

What the actual fuck?!
Alright seems like we have game modding challenge.
The problem is all the movements are too rapid and the jump itself is too short. Literally touching the arrow keys make the guy spin like crazy and the jump barely lifts his feet of the platform.
When I first solved this challenge I don't remember the movements to be that sensitive, but that was years ago, when the computers back then were a lot slower than nowadays.

I was able to move our guy a bit to one corner of the platform he spawns on:


The developers kindly provided us with X and Z coordinates of our guy but these coordinates are not displayed by their exact values.
Instead, they are shown as a positive or negative values on the X and Z scales.

And why the hell they use Z scale while this should clearly be Y?

Seems like I have a few ways to solve this.
The most straight forward solution would be to make him take the jump on the other platform, by lowering the "gravity" value or extending the jump height.
Maybe I can poke the body position coordinates and "teleport" him on the other platform?
Or teleport the destination platform to bridge together with mine so I can just waltz like a boss to the other side?
Or just dive in into the code and fish out the password without even playing the game?
Why not try doing all of these methods?

Since I'll try to accomplish all of these, let's first reverse engineer a bit the game engine.

Running the app.exe file creates a directory named "dbpdata" in my %TEMP% folder and delete it on program exit.
The directory is getting populated by the following files:
media/body.jpg
media/boy.3ds
media/head.jpg
_virtual.dat
bump.fx
cartoon.fx
compress.dll
conv3ds.dll
convmd2.dll
convmd3.dll
convmdl.dll
convx.dll
dbprobasic2ddebug.dll
dbprobasic3ddebug.dll
dbprocameradebug.dll
dbprocore.dll
dbproimagedebug.dll
dbproinputdebug.dll
dbprolightdebug.dll
dbpromatrixdebug.dll
dbprosetupdebug.dll
dbprospritesdebug.dll
dbprosystemdebug.dll
dbprotextdebug.dll
dbprovectorsdebug.dll
icon.ico
rainbow.fx


Those two jpg files are probably the textures, although they seems to be some odd JPEG format and cannot be opened with Photoshop or Windows Photo Viewer.
The 3ds should be the model of our guy and like the JPEG's it seems to be some odd format too, because I wasn't able to import it in Autodesk 3ds Max.
The fx files are plain text scripts, but they seems to be leftovers from something else and they aren't used by the app.
All the dll files are part of the engine itself, so with the ico I'll ignore them all for now.

So, only the _virtual.dat file left so let's see what this guy holds:

Hey hello, what's this?
Seems like this file holds the game data, so technically if I find where all these are used in runtime, I can try to statically analyse the code and pull out the password.

Let's start debugging and try to find how _virtual.dat is parsed.
Setting a breakpoint to CreateFileA can easily get me to where this happens:
parse_dat(), IDA decompilechar __thiscall parse_dat(void *this, LPCSTR lpFileName) {
    v2 = this;
    v3 = CreateFileA(lpFileName, 0x80000000, 1u, 0, 3u, 0x80u, 0);    // here
    v4 = v3;
    if ( v3 == (HANDLE)-1 ) {
        result = 0;
    } else {
        read_dword(v3, (char *)v2 + 8);
        read_dword(v4, (char *)v2 + 0xC);
        read_dword(v4, (char *)v2 + 0x10);
        read_dword(v4, (char *)v2 + 0x14);
        lpFileName = (LPCSTR)256;
        read_bytearray(v4, (int)((char *)v2 + 0x18), (int)&lpFileName);
        read_dword(v4, (char *)v2 + 0x28);
        read_dword_array(v4, (int)((char *)v2 + 0x2C), (int)((char *)v2 + 0x28));
        read_string_array(v4, (int)((char *)v2 + 0x30), (int)((char *)v2 + 0x28));
        read_dword_array(v4, (int)((char *)v2 + 0x34), (int)((char *)v2 + 0x28));
        memset(*((void **)v2 + 0xD), 0, 4 * *((_DWORD *)v2 + 0xA));
        read_dword(v4, (char *)v2 + 0x38);
        read_dword_array(v4, (int)((char *)v2 + 0x3C), (int)((char *)v2 + 0x38));
        read_dword_array(v4, (int)((char *)v2 + 0x40), (int)((char *)v2 + 0x38));
        read_dword_array(v4, (int)((char *)v2 + 0x44), (int)((char *)v2 + 0x38));
        read_dword(v4, (char *)v2 + 0x54);
        read_string_array(v4, (int)((char *)v2 + 0x58), (int)((char *)v2 + 0x54));
        read_dword(v4, (char *)v2 + 0x5C);
        read_bytearray(v4, (int)((char *)v2 + 0x60), (int)((char *)v2 + 0x5C));
        read_dword(v4, (char *)v2 + 0x68);
        read_dword_array(v4, (int)((char *)v2 + 0x6C), (int)((char *)v2 + 0x68));
        read_string_array(v4, (int)((char *)v2 + 0x70), (int)((char *)v2 + 0x68));
        read_dword(v4, (char *)v2 + 0x74);
        read_string_array(v4, (int)((char *)v2 + 0x78), (int)((char *)v2 + 0x74));
        read_dword(v4, (char *)v2 + 0x7C);
        read_bytes(v4, (int)((char *)v2 + 0x80), 0xA * *((_DWORD *)v2 + 0x1F));
        read_string_array(v4, (int)((char *)v2 + 0x84), (int)((char *)v2 + 0x7C));
        read_dword(v4, (char *)v2 + 0x8C);
        read_dword(v4, (char *)v2 + 0x98);
        read_dword(v4, (char *)v2 + 0xA0);
        read_dword_array(v4, (int)((char *)v2 + 0xA4), (int)((char *)v2 + 0xA0));
        read_dword_array(v4, (int)((char *)v2 + 0xA8), (int)((char *)v2 + 0xA0));
        CloseHandle(v4);
        result = 1;
    }
    return result;
}

Having this information I can write down a topology if the file's format:
_virtual.dat file formatstruct PASCAL_STR {
    DWORD len;
    char  str[len];
};

struct _virtual_dat {
    DWORD u1;               // 01 00 00 00                                         - window style
    DWORD u2;               // 80 02 00 00                                         - window width
    DWORD u3;               // E0 01 00 00                                         - window height
    DWORD u4;               // 20 00 00 00
    byte u5[0x100];         // 48 54 53 20 61 70 70 00 03 00 02 00 21 01 08 01 ... - window title
    DWORD u6;               // 12 00 00 00                                         - number of engine libs
    DWORD u7[u6];           // 04 00 00 00,01 00 00 00,02 00 00 00,03 00 00 00 ... - engine lib order
    PASCAL_STR u8[u6];      // 0D 00 00 00 64 62 70 72 6F 63 6F 72 65 2E 64 6C ... - engine lib names
    DWORD u9[u6];           // 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... - engine lib ID
    DWORD u10;              // CA 0F 00 00                                         - number of patches
    DWORD u11[u10];         // 03 00 00 00 09 00 00 00 0E 00 00 00 16 00 00 00 ... - patch offsets
    DWORD u12[u10];         // 03 00 00 00,03 00 00 00,01 00 00 00,03 00 00 00 ...
    DWORD u13[u10];         // 00 00 00 00,14 00 00 00,00 00 00 00,10 00 00 00 ...
    DWORD u14;              // 0F 27 00 00
    PASCAL_STR u15[u14];    // 00 00 00 00 44 00 00 00 42 72 6F 6B 65 20 66 72 ...
    DWORD u16;              // 47 71 00 00                                         - game logic code length
    DWORD u17[u16];         // 60 89 25 FF,FF FF FF 89,25 FF FF FF,FF BB FF FF ... - game logic code
    DWORD u18;              // 30 00 00 00                                         - number of used export functions
    DWORD u19[u18];         // 01 00 00 00,02 00 00 00,03 00 00 00,02 00 00 00 ... - export lib ID
    PASCAL_STR u20[u18];    // 16 00 00 00 3F 4D 61 78 69 6D 69 73 65 57 69 6E ... - export lib function
    DWORD u21;              // 15 00 00 00
    PASCAL_STR u22[u21];    // 07 00 00 00 62 6F 79 2E 33 64 73 08 00 00 00 62 ...
    DWORD u23;              // 00 00 00 00
    byte u24[u23+u23*4];    // N/A
    PASCAL_STR u25[u23];    // N/A
    DWORD u26;              // DC 00 00 00
    DWORD u27;              // 00 00 00 00
    DWORD u28;              // 04 00 00 00
    DWORD u29[u28];         // BC 00 00 00,C0 00 00 00,C4 00 00 00,CC 00 00 00
    DWORD u30[u28];         // 00 00 00 00,00 00 00 00,00 00 00 00,00 00 00 00
};

Took me some time to understand it, but this structure will help me accomplish my modding ideas and eventually beat the challenge.

The key point is the u17 where the user code is located. Originally this code looks like this:
Logic code before patching, HEX dumpOffset     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
----------------------------------------------------------
00000000   60 89 25 FF FF FF FF 89 25 FF FF FF FF BB FF FF
00000010   FF FF FF D3 C7 05 FF FF FF FF FF FF FF FF A1 FF
00000020   FF FF FF 3D FF FF FF FF 0F 85 FF FF FF FF B8 01
00000030   00 00 00 50 B8 FF FF FF FF 50 89 25 FF FF FF FF
00000040   BB FF FF FF FF FF D3 5B 5B C7 05 FF FF FF FF FF

But after it's parsed by the engine's code and these FF FF FF FF bytes get patched with the appropriate data, the code starts to take shape:
Logic code after patching, HEX dumpOffset     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
----------------------------------------------------------
00000000   60 89 25 80 90 1E 00 89 25 94 90 1E 00 BB 3C 33
00000010   3E 00 FF D3 C7 05 74 EA 40 00 02 00 00 00 A1 70
00000020   EA 40 00 3D 00 00 00 00 0F 85 F4 70 00 00 B8 01
00000030   00 00 00 50 B8 B0 CB C0 01 50 89 25 94 90 1E 00
00000040   BB 7D 2E 72 03 FF D3 5B 5B C7 05 74 EA 40 00 03

Although this code is IDA decompilable, it looks way better in x86dbg , where all the function names and strings are already resolved.
So, let's see what this code hides:
Code logic start, x86dbg disassembly022EDBE8 | 60                                 | pushal
022EDBE9 | 89 25 80 90 1E 00                  | mov dword ptr ds:[1E9080],esp
022EDBEF | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp
022EDBF5 | BB 3C 33 3E 00                     | mov ebx,<dbprosetupdebug.void __cdecl MaximiseWindow(void)>
022EDBFA | FF D3                              | call ebx           ; dbprosetupdebug.void __cdecl MaximiseWindow(void)
; trimmed
022EDC16 | B8 01 00 00 00                     | mov eax,1
022EDC1B | 50                                 | push eax
022EDC1C | B8 B0 CB C0 01                     | mov eax,1C0CBB0    ; "boy.3ds"
022EDC21 | 50                                 | push eax
022EDC22 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp
022EDC28 | BB 7D 2E 72 03                     | mov ebx,<dbprobasic3ddebug.void __cdecl Load(unsigned long,int)>
022EDC2D | FF D3                              | call ebx           ; dbprobasic3ddebug.void __cdecl Load(unsigned long,int)
;trimmed
022EDC4B | B8 01 00 00 00                     | mov eax,1
022EDC50 | 50                                 | push eax
022EDC51 | B8 90 CB C0 01                     | mov eax,1C0CB90    ; "body.jpg"
022EDC56 | 50                                 | push eax
022EDC57 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp
022EDC5D | BB DD 24 F1 00                     | mov ebx,<dbproimagedebug.void __cdecl LoadEx(char *,int)>
022EDC62 | FF D3                              | call ebx           ; dbproimagedebug.void __cdecl LoadEx(char *,int)
; trimmed
022EDC80 | B8 02 00 00 00                     | mov eax,2
022EDC85 | 50                                 | push eax
022EDC86 | B8 70 CB C0 01                     | mov eax,1C0CB70    ; "head.jpg"
022EDC8B | 50                                 | push eax
022EDC8C | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp
022EDC92 | BB DD 24 F1 00                     | mov ebx,<dbproimagedebug.void __cdecl LoadEx(char *,int)>
022EDC97 | FF D3                              | call ebx           ; dbproimagedebug.void __cdecl LoadEx(char *,int)
; trimmed
This looks promising. I can see where the media files boy.3ds, body.jpg and head.jpg are loaded.

In matter of fact, it turned out these file are encrypted by a crypt/decrypt function inside dbprocore.dll :
dbprocore.DecryptCrypt(), IDA decompilechar __thiscall sub_1000A2C0(int this, unsigned int data, unsigned int data_len, char mode) {
    v17 = *(_DWORD *)(this + 4);
    if (v17) {
        for (i = 0; i < 8; ++i) {
            v8 = v17 / (i + 1) % 0xFF;
            if ( (signed int)(unsigned __int8)v8 < 32 )
                v8 = 32;
            v11[i] = v8;
        }
        v12 = 0;
        v13 = 0;
        v16 = strlen(v11);
        v15 = data_len >> 10;
        v10 = data;
        v14 = data_len + data;
        while ( v10 < v14 ) {
            v6 = v11[2 * (v13 >> 1)];
            if ((double)(v13 / 3) == (double)v13 / 3.0)
                v6 = -v6;
            abs(v6);
            v7 = v17 % 0x40;
            for ( j = 0; j < v7; ++j ) {
                if ( mode ) {
                    if ( *(_BYTE *)v10 == 255 )
                        *(_BYTE *)v10 = 0;
                    else
                        ++*(_BYTE *)v10;
                } else if ( *(_BYTE *)v10 ) {
                    --*(_BYTE *)v10;
                } else {
                    *(_BYTE *)v10 = -1;
                }
            }
            v10 += v15;
            ++v13;
            if ( v13 >= v16 )
                v13 = 0;
        }
    }
    return 1;
}

These files don't hold any information I care about, but being able to view them now is nice:


Alright, let's continue.
After loading the 3D model and its textures, the following code is executed:
platform texture generation, x86dbg disassembly; trimmed
022EDF4B | B8 00 01 00 00                     | mov eax,100                      ;// rand() limit
022EDF50 | 50                                 | push eax                         ;|\
022EDF51 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EDF57 | BB 20 6D 00 10                     | mov ebx,<dbprocore.int __cdecl RndLL(int)>
022EDF5C | FF D3                              | call ebx                         ;| dbprocore.RndLL(int)
022EDF5E | A3 EC 90 1E 00                     | mov dword ptr ds:[1E90EC],eax    ;\ random value from 0 to 0x100
; trimmed
022EDF7E | A1 EC 90 1E 00                     | mov eax,dword ptr ds:[1E90EC]
022EDF83 | A3 48 91 1E 00                     | mov dword ptr ds:[1E9148],eax
; trimmed
022EDFA2 | A1 48 91 1E 00                     | mov eax,dword ptr ds:[1E9148]    ;// blue
022EDFA7 | 50                                 | push eax                         ;|\
022EDFA8 | A1 48 91 1E 00                     | mov eax,dword ptr ds:[1E9148]    ;|/ green
022EDFAD | 50                                 | push eax                         ;|\
022EDFAE | A1 48 91 1E 00                     | mov eax,dword ptr ds:[1E9148]    ;|/ red
022EDFB3 | 50                                 | push eax                         ;|\
022EDFB4 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EDFBA | BB 50 19 F7 00                     | mov ebx,<dbprobasic2ddebug.unsigned long __cdecl Rgb(int,int,int)>
022EDFBF | FF D3                              | call ebx                         ;| dbprobasic2ddebug.Rgb(int,int,int)
022EDFC1 | A3 A0 90 1E 00                     | mov dword ptr ds:[1E90A0],eax    ;\ generated color
; trimmed
022EDFE3 | A1 A0 90 1E 00                     | mov eax,dword ptr ds:[1E90A0]    ;// color
022EDFE8 | 50                                 | push eax                         ;|\
022EDFE9 | A1 58 91 1E 00                     | mov eax,dword ptr ds:[1E9158]    ;|/ Y position
022EDFEE | 50                                 | push eax                         ;|\
022EDFEF | A1 54 91 1E 00                     | mov eax,dword ptr ds:[1E9154]    ;|/ X position
022EDFF4 | 50                                 | push eax                         ;|\
022EDFF5 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EDFFB | BB 50 21 F7 00                     | mov ebx,<dbprobasic2ddebug.void __cdecl Dot(int,int,unsigned long)>
022EE000 | FF D3                              | call ebx                         ;\ dbprobasic2ddebug.Dot(int,int,unsigned long)
; trimmed
022EE05D | B8 63 00 00 00                     | mov eax,63                       ;// 0x63 pixels width?
022EE062 | 50                                 | push eax                         ;|\
022EE063 | B8 63 00 00 00                     | mov eax,63                       ;|/ 0x63 pixels height?
022EE068 | 50                                 | push eax                         ;|\
022EE069 | B8 00 00 00 00                     | mov eax,0                        ;|
022EE06E | 50                                 | push eax                         ;|
022EE06F | B8 00 00 00 00                     | mov eax,0                        ;|
022EE074 | 50                                 | push eax                         ;|
022EE075 | B8 03 00 00 00                     | mov eax,3                        ;|
022EE07A | 50                                 | push eax                         ;|
022EE07B | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE081 | BB 60 2F F1 00                     | mov ebx,<dbproimagedebug.void __cdecl GrabImageEx(int,int,int,int,int)>
022EE086 | FF D3                              | call ebx                         ;\ dbproimagedebug.GrabImageEx(int,int,int,int,int)
; trimmed
This code running in a loop, generates a texture for the two platforms. Because of the randomization using RndLL() and the fact it passes the same value for red, green and blue to Rgb(), the texture has that static noise grayscale look.

Next, the two platforms are generated:
Platform draw, x86dbg disassembly022EE0A7 | B8 19 00 00 00                     | mov eax,19                       ;// 25 (long)
022EE0AC | 50                                 | push eax                         ;|\
022EE0AD | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE0B3 | BB 50 65 00 10                     | mov ebx,<dbprocore.unsigned long __cdecl CastLtoF(int)>
022EE0B8 | FF D3                              | call ebx                         ;| dbprocore.CastLtoF(int)
022EE0BA | 5B                                 | pop ebx                          ;|
022EE0BB | A3 AC 90 1E 00                     | mov dword ptr ds:[1E90AC],eax    ;\ 25 (float)
022EE0C0 | B8 19 00 00 00                     | mov eax,19                       ;// 25 (long)
022EE0C5 | 50                                 | push eax                         ;|\
022EE0C6 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE0CC | BB 50 65 00 10                     | mov ebx,<dbprocore.unsigned long __cdecl CastLtoF(int)>
022EE0D1 | FF D3                              | call ebx                         ;| dbprocore.CastLtoF(int)
022EE0D3 | 5B                                 | pop ebx                          ;|
022EE0D4 | A3 C8 90 1E 00                     | mov dword ptr ds:[1E90C8],eax    ;\ 25 (float)
022EE0D9 | B8 01 00 00 00                     | mov eax,1                        ;/
022EE0DE | 50                                 | push eax                         ;|
022EE0DF | B8 01 00 00 00                     | mov eax,1                        ;|
022EE0E4 | 50                                 | push eax                         ;|
022EE0E5 | A1 C8 90 1E 00                     | mov eax,dword ptr ds:[1E90C8]    ;|/ 25 units in Z direction
022EE0EA | 50                                 | push eax                         ;|\
022EE0EB | A1 AC 90 1E 00                     | mov eax,dword ptr ds:[1E90AC]    ;|/ 25 units in X direction
022EE0F0 | 50                                 | push eax                         ;|\
022EE0F1 | B8 01 00 00 00                     | mov eax,1                        ;|
022EE0F6 | 50                                 | push eax                         ;|
022EE0F7 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE0FD | BB D2 41 F9 00                     | mov ebx,<dbpromatrixdebug.void __cdecl MakeEx(int,float,float,int,int)>
022EE102 | FF D3                              | call ebx                         ;\ dbpromatrixdebug.MakeEx(int,float,float,int,int)
; trimmed
022EE17F | B8 00 00 48 C1                     | mov eax,C1480000                 ;// -12.5 units platform Z position
022EE184 | 50                                 | push eax                         ;|\
022EE185 | A1 C8 90 1E 00                     | mov eax,dword ptr ds:[1E90C8]    ;|/ 0 units platform Y position?
022EE18A | 50                                 | push eax                         ;|\
022EE18B | B8 00 00 48 C1                     | mov eax,C1480000                 ;|/ -12.5 units platform X position
022EE190 | 50                                 | push eax                         ;|\
022EE191 | B8 01 00 00 00                     | mov eax,1                        ;|
022EE196 | 50                                 | push eax                         ;|
022EE197 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE19D | BB A8 43 F9 00                     | mov ebx,<dbpromatrixdebug.void __cdecl PositionEx(int,float,float,float)>
022EE1A2 | FF D3                              | call ebx                         ;\ dbpromatrixdebug.PositionEx(int,float,float,float)
; trimmed
022EE281 | B8 CE FF FF FF                     | mov eax,FFFFFFCE                 ;// -50 (long)
022EE286 | 50                                 | push eax                         ;|\
022EE287 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE28D | BB 50 65 00 10                     | mov ebx,<dbprocore.unsigned long __cdecl CastLtoF(int)>
022EE292 | FF D3                              | call ebx                         ;| dbprocore.CastLtoF(int)
022EE294 | 5B                                 | pop ebx                          ;|
022EE295 | A3 AC 90 1E 00                     | mov dword ptr ds:[1E90AC],eax    ;\ -50 (float)
022EE29A | B8 00 00 00 00                     | mov eax,0                        ;// 0 (long)
022EE29F | 50                                 | push eax                         ;|\
022EE2A0 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE2A6 | BB 50 65 00 10                     | mov ebx,<dbprocore.unsigned long __cdecl CastLtoF(int)>
022EE2AB | FF D3                              | call ebx                         ;| dbprocore.CastLtoF(int)
022EE2AD | 5B                                 | pop ebx                          ;|
022EE2AE | A3 C8 90 1E 00                     | mov dword ptr ds:[1E90C8],eax    ;\ 0 (float)
022EE2B3 | B8 32 00 00 00                     | mov eax,32                       ;// 50 (long)
022EE2B8 | 50                                 | push eax                         ;|\
022EE2B9 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE2BF | BB 50 65 00 10                     | mov ebx,<dbprocore.unsigned long __cdecl CastLtoF(int)>
022EE2C4 | FF D3                              | call ebx                         ;| dbprocore.CastLtoF(int)
022EE2C6 | 5B                                 | pop ebx                          ;|
022EE2C7 | A3 CC 90 1E 00                     | mov dword ptr ds:[1E90CC],eax    ;\ 50 (float)
022EE2CC | A1 CC 90 1E 00                     | mov eax,dword ptr ds:[1E90CC]    ;// 50 units platform Z position
022EE2D1 | 50                                 | push eax                         ;|\
022EE2D2 | A1 C8 90 1E 00                     | mov eax,dword ptr ds:[1E90C8]    ;|/ 0 units platform Y position
022EE2D7 | 50                                 | push eax                         ;|\
022EE2D8 | A1 AC 90 1E 00                     | mov eax,dword ptr ds:[1E90AC]    ;|/ -50 units platform X position
022EE2DD | 50                                 | push eax                         ;|\
022EE2DE | B8 02 00 00 00                     | mov eax,2                        ;|
022EE2E3 | 50                                 | push eax                         ;|
022EE2E4 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE2EA | BB A8 43 F9 00                     | mov ebx,<dbpromatrixdebug.void __cdecl PositionEx(int,float,float,float)>
; trimmed

So, I know there's two square platforms, 25 by 25 units each. The first one, that it turned out to be the platform my guy stands on, is located at -12.5 by X and -12.5 by Z and the second that I need to jump to is located at -50 units by X and +50 units by Z.
I also know that my guy is positioned in the center of the second platform and that's coordinates X = 0 and Z = 0, so this means the platform is positioned relative to its lower left corner.
Ok, let's put that down in Photoshop:


What I can do now is to adjust the platform B position from X = -50 and Z = +50 to X = -12.5 and Z = +12.5 and they both should be joined together:


Now the two platforms are attached to each other and I can walk backwards to get to platform B. Doing so however didn't work and instead I fell off again.
Seems like the place I'm required to land on, is not determined only by the platform's position, and cheating it this way wont work.

Ok, let's keep digging then.
Next point of interest in the code is this place here:
Player movement, x86dbg disassembly022EE383 | BB C8 10 44 02                     | mov ebx,<dbproinputdebug.int __cdecl UpKey(void)>
022EE388 | FF D3                              | call ebx                         ;| dbproinputdebug.UpKey(void)
022EE38A | A3 E8 90 1E 00                     | mov dword ptr ds:[1E90E8],eax    ;\ EAX holds 1 if the UP key is pressed
; trimmed
022EE3A9 | B8 01 00 00 00                     | mov eax,1                        ;/ This whole chunk
022EE3AE | 50                                 | push eax                         ;| basically compares the result
022EE3AF | A1 E8 90 1E 00                     | mov eax,dword ptr ds:[1E90E8]    ;| of UpKey() with 1
022EE3B4 | 8B D8                              | mov ebx,eax                      ;|
022EE3B6 | 5A                                 | pop edx                          ;|
022EE3B7 | B8 00 00 00 00                     | mov eax,0                        ;|
022EE3BC | 3B DA                              | cmp ebx,edx                      ;|
022EE3BE | 0F 94 C0                           | sete al                          ;| and sets the flag to AL
022EE3C1 | A3 EC 90 1E 00                     | mov dword ptr ds:[1E90EC],eax    ;| stores it here
022EE3C6 | A1 EC 90 1E 00                     | mov eax,dword ptr ds:[1E90EC]    ;|
022EE3CB | 3D 00 00 00 00                     | cmp eax,0                        ;| compares that flag with 0
022EE3D0 | 0F 84 4F 00 00 00                  | je 22EE425                       ;\ and jump if key press event is not triggered
022EE3D6 | B8 CD CC 4C BD                     | mov eax,BD4CCCCD                 ;// -0.05
022EE3DB | 50                                 | push eax                         ;|\
022EE3DC | B8 01 00 00 00                     | mov eax,1                        ;|/
022EE3E1 | 50                                 | push eax                         ;|\
022EE3E2 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE3E8 | BB A9 2A 72 03                     | mov ebx,<dbprobasic3ddebug.void __cdecl Move(int,float)>
022EE3ED | FF D3                              | call ebx                         ;\ dbprobasic3ddebug.Move(int,float)
So, pressing the UP key moves the player with -0.05 units forward (technically backwards, but because of the messed up coordinate system it's forward here).

There's similar code for DOWN, LEFT, RIGHT and SPACE keys, and obviously the SPACE one is what I'm interested into:
Player jump, x86dbg disassembly022EE6C9 | BB 45 11 44 02                     | mov ebx,<dbproinputdebug.int __cdecl SpaceKey(void)>
022EE6CE | FF D3                              | call ebx                         ;| dbproinputdebug.SpaceKey(void)
022EE6D0 | A3 E8 90 1E 00                     | mov dword ptr ds:[1E90E8],eax    ;\
022EE6D5 | C7 05 74 EA 40 00 24 00 00 00      | mov dword ptr ds:[<dword_40EA74>],24
022EE6DF | A1 70 EA 40 00                     | mov eax,dword ptr ds:[<dword_40EA70>]
022EE6E4 | 3D 00 00 00 00                     | cmp eax,0
022EE6E9 | 0F 85 1B 66 00 00                  | jne 022F4D0A
022EE6EF | B8 01 00 00 00                     | mov eax,1                        ;/ check if SPACE key is pressed
022EE6F4 | 50                                 | push eax                         ;|
022EE6F5 | A1 E8 90 1E 00                     | mov eax,dword ptr ds:[1E90E8]    ;|
022EE6FA | 8B D8                              | mov ebx,eax                      ;|
022EE6FC | 5A                                 | pop edx                          ;|
022EE6FD | B8 00 00 00 00                     | mov eax,0                        ;|
022EE702 | 3B DA                              | cmp ebx,edx                      ;|
022EE704 | 0F 94 C0                           | sete al                          ;|
022EE707 | A3 EC 90 1E 00                     | mov dword ptr ds:[1E90EC],eax    ;\ save the status here
022EE70C | B8 01 00 00 00                     | mov eax,1                        ;//
022EE711 | 50                                 | push eax                         ;|\
022EE712 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE718 | BB 2B 1C 72 03                     | mov ebx,<dbprobasic3ddebug.unsigned long __cdecl GetYPositionEx(int)>
022EE71D | FF D3                              | call ebx                         ;| dbprobasic3ddebug.GetYPositionEx(int)
022EE71F | A3 CC 90 1E 00                     | mov dword ptr ds:[1E90CC],eax    ;\ EAX is the Y position
022EE724 | 5B                                 | pop ebx
022EE725 | C7 05 74 EA 40 00 24 00 00 00      | mov dword ptr ds:[<dword_40EA74>],24
022EE72F | A1 70 EA 40 00                     | mov eax,dword ptr ds:[<dword_40EA70>]
022EE734 | 3D 00 00 00 00                     | cmp eax,0
022EE739 | 0F 85 CB 65 00 00                  | jne 022F4D0A
022EE73F | B8 00 00 00 00                     | mov eax,0                        ;// 0 (long)
022EE744 | 50                                 | push eax                         ;|\
022EE745 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE74B | BB 50 65 00 10                     | mov ebx,<dbprocore.unsigned long __cdecl CastLtoF(int)>
022EE750 | FF D3                              | call ebx                         ;| dbprocore.CastLtoF(int)
022EE752 | 5B                                 | pop ebx                          ;|
022EE753 | A3 D0 90 1E 00                     | mov dword ptr ds:[1E90D0],eax    ;\ 0 (float)
022EE758 | A1 D0 90 1E 00                     | mov eax,dword ptr ds:[1E90D0]    ;// 0 (float)
022EE75D | 50                                 | push eax                         ;|\
022EE75E | A1 CC 90 1E 00                     | mov eax,dword ptr ds:[1E90CC]    ;|/ player's Y position
022EE763 | 50                                 | push eax                         ;|\
022EE764 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE76A | BB 80 5B 00 10                     | mov ebx,<dbprocore.unsigned long __cdecl EqualLFF(float,float)>
022EE76F | FF D3                              | call ebx                         ;| dbprocore.EqualLFF(float,float)
022EE771 | 5B                                 | pop ebx                          ;|
022EE772 | A3 28 91 1E 00                     | mov dword ptr ds:[1E9128],eax    ;\ comparison result
022EE777 | 58                                 | pop eax
022EE778 | A1 EC 90 1E 00                     | mov eax,dword ptr ds:[1E90EC]    ;/ take key press status
022EE77D | 50                                 | push eax                         ;|
022EE77E | A1 28 91 1E 00                     | mov eax,dword ptr ds:[1E9128]    ;| and Y position status
022EE783 | 5B                                 | pop ebx                          ;|
022EE784 | 21 D8                              | and eax,ebx                      ;| AND them (basically compare them and get the result)
022EE786 | A3 2C 91 1E 00                     | mov dword ptr ds:[1E912C],eax    ;\ and store the flag here
022EE78B | A1 2C 91 1E 00                     | mov eax,dword ptr ds:[1E912C]    ;/ If the SPACE key is pressed
022EE790 | 3D 00 00 00 00                     | cmp eax,0                        ;| and the Y value is 0 (our guy is not jumped already)
022EE795 | 0F 84 3E 00 00 00                  | je 022EE7D9                      ;| continue here
022EE79B | B8 CD CC 4C 3E                     | mov eax,3E4CCCCD                 ;| and set the jump height value to 0.2
022EE7A0 | A3 50 91 1E 00                     | mov dword ptr ds:[1E9150],eax    ;\
Cool! I can poke the jump height now and take the jump.

One problem I already mentioned before is that the game runs so fast and the movements are so rapid, you basically can't control the player normally.
Before trying to take the jump I'll have to think of a solution to fix this issue.

Because the key press events are executed in a loop, the code above will be continuously executed, so I can put a Sleep() function and set a delay that will make the game playable again.
A place where this can happen is here:
Potential space for a code cave, x86dbg disassembly022EE725 | C7 05 74 EA 40 00 24 00 00 00      | mov dword ptr ds:[<dword_40EA74>],24
022EE72F | A1 70 EA 40 00                     | mov eax,dword ptr ds:[<dword_40EA70>]
022EE734 | 3D 00 00 00 00                     | cmp eax,0
022EE739 | 0F 85 CB 65 00 00                  | jne 022F4D0A

I played a little with the delay, and what seems to do the trick was setting a delay of 2 milliseconds like so:
Slowing the game patch, x86dbg disassembly022EE725 | 6A 02                              | push 2
022EE727 | E8 1A DB 1C 74                     | call <kernel32.Sleep>
022EE72C | 90                                 | nop
; trimmed NOP instructions
022EE73E | 90                                 | nop
And the game is again playable.

Next by trial and error (not enough power / too much power) I was able to get the golden ratio of about 1.8 that is enough to jump (roughly) to the other platform.
I did so and yet again, the player fell through the platform!!
Well, that's fucking annoying. Is there even a way to get to the other platform?

Moving down the code then:
Player positioning, x86dbg disassembly022EE94D | A1 CC 90 1E 00                     | mov eax,dword ptr ds:[1E90CC]    ;// Z
022EE952 | 50                                 | push eax                         ;|\
022EE953 | A1 D8 90 1E 00                     | mov eax,dword ptr ds:[1E90D8]    ;|/ Y
022EE958 | 50                                 | push eax                         ;|\
022EE959 | A1 AC 90 1E 00                     | mov eax,dword ptr ds:[1E90AC]    ;|/ X 
022EE95E | 50                                 | push eax                         ;|\
022EE95F | B8 01 00 00 00                     | mov eax,1                        ;|/
022EE964 | 50                                 | push eax                         ;|\
022EE965 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EE96B | BB B6 4F 72 03                     | mov ebx,<dbprobasic3ddebug.void __cdecl Position(int,float,float,float)>
022EE970 | FF D3                              | call ebx                         ;\ dbprobasic3ddebug.Position(int,float,float,float)

I thought setting coordinates that point to the center of platform B here will finally work, but yet again, the player fell through the platform.
What I didn't keep in mind was those coordinate values were copies of the original ones, and swapping these will throw me out of my platform, and in the same time the back end code will still see me as I'm still on platform A.

So, to cheat it the correct way, I need to see where the X and Z values (Y should be always 0, so I don't care about it) are pulled from, flip them to my new coordinates and run the game.
Alright, let's backtrace them then. X coordinates are pulled here
Z022EE8CE | B8 01 00 00 00                     | mov eax,1                           ;// parameter passed to GetXPositionEx()
022EE8D3 | 50                                 | push eax                            ;|\
022EE8D4 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp       ;|
022EE8DA | BB 62 3F 72 03                     | mov ebx,<dbprobasic3ddebug.unsigned long __cdecl GetXPositionEx(int);>
022EE8DF | FF D3                              | call ebx                            ;| dbprobasic3ddebug.GetXPositionEx(int)
022EE8E1 | A3 AC 90 1E 00                     | mov dword ptr ds:[1E90AC],eax       ;\
; and getting into GetXPositionEx() itself
0373E78E | 56                                 | push esi
0373E78F | 8B 74 24 08                        | mov esi,dword ptr ss:[esp+8]        ; this is 1 as it is passed to GetXPositionEx()
0373E793 | 56                                 | push esi                            ;/ Some check, probably to verify if the
0373E794 | E8 D0 4B FE FF                     | call dbprobasic3ddebug.3723369      ;| buffer below is allocates and stuff like that
0373E799 | 84 C0                              | test al,al                          ;|
0373E79B | 59                                 | pop ecx                             ;|
0373E79C | 75 04                              | jne dbprobasic3ddebug.373E7A2       ;\ here it should always jump if everything is correct
0373E79E | 33 C0                              | xor eax,eax
0373E7A0 | 5E                                 | pop esi
0373E7A1 | C3                                 | ret
0373E7A2 | A1 B4 23 83 03                     | mov eax,dword ptr ds:[38323B4]      ;/ This is where all the magic happens
0373E7A7 | 8B 04 B0                           | mov eax,dword ptr ds:[eax+esi*4]    ;|
0373E7AA | 8B 80 84 00 00 00                  | mov eax,dword ptr ds:[eax+84]       ;\
0373E7B0 | 5E                                 | pop esi
0373E7B1 | C3                                 | ret

So X is stored at offset 0x84 in a buffer located at address 038323B4 + 1 * 4 (what a mess, huh?).
I've traced that code up to 0373E7AA and wrote down the address where X is saved - 00EF9BC4.
Same thing applied for the Z point (back trace, locate the GetZPositionEx() and trace inside) gave me the address 00EF9BCC.
By the way, those are dynamic addresses, because this code is currently executed from the memory, so they will differ from yours.

The exact center of platform B is X = -37.5 (0xC2160000) and Z = 62.5 (0x427A0000) but when I tried changing them here at the Position() call, plan didn't worked.
Theoretically on the right path, in practise thought I'm messing up something.

Scrolling down I passed a lot of potential patch point but I also noticed this code:
Code builder, x86dbg disassembly022EFA9D | A1 28 91 1E 00                     | mov eax,dword ptr ds:[1E9128]    ; result of GreaterLFF(GetZPositionEx(), CastLtoF(0x32))
022EFAA2 | 50                                 | push eax
022EFAA3 | A1 FC 90 1E 00                     | mov eax,dword ptr ds:[1E90FC]    ; result of LessLFF(GetZPositionEx(), CastLtoF(0x4B))
022EFAA8 | 5B                                 | pop ebx
022EFAA9 | 21 D8                              | and eax,ebx                      ;/ AND the two results
022EFAAB | A3 10 91 1E 00                     | mov dword ptr ds:[1E9110],eax    ;| and store the value here
022EFAB0 | A1 10 91 1E 00                     | mov eax,dword ptr ds:[1E9110]    ;\
022EFAB5 | 50                                 | push eax
022EFAB6 | A1 38 91 1E 00                     | mov eax,dword ptr ds:[1E9138]    ; result of GreaterLFF(GetXPositionEx(), CastLtoF(-0x32))
022EFABB | 5B                                 | pop ebx
022EFABC | 21 D8                              | and eax,ebx                      ;/ AND again
022EFABE | A3 14 91 1E 00                     | mov dword ptr ds:[1E9114],eax    ;| and store the result
022EFAC3 | A1 14 91 1E 00                     | mov eax,dword ptr ds:[1E9114]    ;\
022EFAC8 | 50                                 | push eax
022EFAC9 | A1 0C 91 1E 00                     | mov eax,dword ptr ds:[1E910C]    ; result of LessLFF(GetXPositionEx(), CastLtoF(-0x19))
022EFACE | 5B                                 | pop ebx
022EFACF | 21 D8                              | and eax,ebx                      ;/ final AND
022EFAD1 | A3 1C 91 1E 00                     | mov dword ptr ds:[1E911C],eax    ;| and store the final result
022EFAD6 | A1 1C 91 1E 00                     | mov eax,dword ptr ds:[1E911C]    ;\
022EFADB | 3D 00 00 00 00                     | cmp eax,0                        ;/ If the final result is not 1
022EFAE0 | 0F 84 0B 4A 00 00                  | je 22F44F1                       ;\ jump over the following code
022EFAE6 | B8 02 00 00 00                     | mov eax,2                        ;// Y
022EFAEB | 50                                 | push eax                         ;|\
022EFAEC | B8 00 00 00 00                     | mov eax,0                        ;|/ X
022EFAF1 | 50                                 | push eax                         ;|\
022EFAF2 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EFAF8 | BB 50 1E F7 00                     | mov ebx,<dbprobasic2ddebug.void __cdecl Dot(int,int)>
022EFAFD | FF D3                              | call ebx                         ;\ dbprobasic2ddebug.Dot(int,int)
; trimmed
022EFB1B | B8 03 00 00 00                     | mov eax,3                        ;// Y
022EFB20 | 50                                 | push eax                         ;|\
022EFB21 | B8 00 00 00 00                     | mov eax,0                        ;|/ X
022EFB26 | 50                                 | push eax                         ;|\
022EFB27 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EFB2D | BB 50 1E F7 00                     | mov ebx,<dbprobasic2ddebug.void __cdecl Dot(int,int)>
022EFB32 | FF D3                              | call ebx                         ;\ dbprobasic2ddebug.Dot(int,int)
; trimmed
022EFB50 | B8 04 00 00 00                     | mov eax,4                        ;// Y
022EFB55 | 50                                 | push eax                         ;|\
022EFB56 | B8 00 00 00 00                     | mov eax,0                        ;|/ X
022EFB5B | 50                                 | push eax                         ;|\
022EFB5C | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EFB62 | BB 50 1E F7 00                     | mov ebx,<dbprobasic2ddebug.void __cdecl Dot(int,int)>
022EFB67 | FF D3                              | call ebx                         ;\ dbprobasic2ddebug.Dot(int,int)
; trimmed
022EFB85 | B8 05 00 00 00                     | mov eax,5                        ;// Y
022EFB8A | 50                                 | push eax                         ;/\
022EFB8B | B8 00 00 00 00                     | mov eax,0                        ;|/ X
022EFB90 | 50                                 | push eax                         ;|\
022EFB91 | 89 25 94 90 1E 00                  | mov dword ptr ds:[1E9094],esp    ;|
022EFB97 | BB 50 1E F7 00                     | mov ebx,<dbprobasic2ddebug.void __cdecl Dot(int,int)>
022EFB9C | FF D3                              | call ebx                         ;\ dbprobasic2ddebug.Dot(int,int)
; trimmed

Those Dot() calls continue further down and I already know this function creates a pixel at given X-Y coordinates.
I could just NOP the JZ in the beginning, so I did so and ran the game:

Well, that finally worked.

So, after all, is there a way to teleport the player to a place where all the requirements will be matched and the rule is: (((Z > 50) & (Z < 75)) & ((X > -50) & (X < -25))).
This perfectly matches my drawing from above and my whole plan was correct, so it seems that, indeed - I'm patching it in the wrong place.

Modding these coordinates here however is pointless as i already know that just flipping the JZ is enough, so instead doing that, I pulled all the X/Y parameters for the Dot() calls and wrote a code generator using PHP's GD lib:
Answer generator, PHP+GD$XY = array(0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09,
            0x00, 0x0A, 0x00, 0x0B, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x07,
            0x01, 0x08, 0x01, 0x09, 0x01, 0x0A, 0x01, 0x0B, 0x02, 0x02, 0x02, 0x08, 0x03, 0x02, 0x03, 0x08,
            0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, 0x08, 0x05, 0x03,
            0x05, 0x04, 0x05, 0x05, 0x05, 0x06, 0x05, 0x07, 0x08, 0x00, 0x08, 0x08, 0x09, 0x00, 0x09, 0x08,
            0x0A, 0x00, 0x0A, 0x01, 0x0A, 0x02, 0x0A, 0x03, 0x0A, 0x04, 0x0A, 0x05, 0x0A, 0x06, 0x0A, 0x07,
            0x0A, 0x08, 0x0B, 0x00, 0x0B, 0x01, 0x0B, 0x02, 0x0B, 0x03, 0x0B, 0x04, 0x0B, 0x05, 0x0B, 0x06,
            0x0B, 0x07, 0x0B, 0x08, 0x0C, 0x08, 0x0D, 0x08, 0x10, 0x06, 0x10, 0x07, 0x11, 0x02, 0x11, 0x05,
            0x11, 0x06, 0x11, 0x07, 0x11, 0x08, 0x12, 0x02, 0x12, 0x05, 0x12, 0x08, 0x13, 0x02, 0x13, 0x05,
            0x13, 0x08, 0x14, 0x02, 0x14, 0x03, 0x14, 0x04, 0x14, 0x05, 0x14, 0x06, 0x14, 0x07, 0x14, 0x08,
            0x15, 0x03, 0x15, 0x04, 0x15, 0x05, 0x15, 0x06, 0x15, 0x07, 0x15, 0x08, 0x18, 0x02, 0x19, 0x00,
            0x19, 0x01, 0x19, 0x02, 0x19, 0x03, 0x19, 0x04, 0x19, 0x05, 0x19, 0x06, 0x19, 0x07, 0x1A, 0x00,
            0x1A, 0x01, 0x1A, 0x02, 0x1A, 0x03, 0x1A, 0x04, 0x1A, 0x05, 0x1A, 0x06, 0x1A, 0x07, 0x1A, 0x08,
            0x1B, 0x02, 0x1B, 0x08, 0x1C, 0x02, 0x1C, 0x08, 0x1D, 0x02, 0x1D, 0x08, 0x20, 0x04, 0x21, 0x01,
            0x21, 0x02, 0x21, 0x03, 0x21, 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x22, 0x00,
            0x22, 0x01, 0x22, 0x02, 0x22, 0x03, 0x22, 0x04, 0x22, 0x05, 0x22, 0x06, 0x22, 0x07, 0x22, 0x08,
            0x23, 0x00, 0x23, 0x04, 0x24, 0x00, 0x24, 0x04, 0x25, 0x00, 0x25, 0x04, 0x28, 0x03, 0x28, 0x04,
            0x28, 0x05, 0x28, 0x06, 0x28, 0x07, 0x29, 0x02, 0x29, 0x03, 0x29, 0x04, 0x29, 0x05, 0x29, 0x06,
            0x29, 0x07, 0x29, 0x08, 0x2A, 0x02, 0x2A, 0x08, 0x2B, 0x02, 0x2B, 0x08, 0x2C, 0x02, 0x2C, 0x03,
            0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2D, 0x03, 0x2D, 0x04, 0x2D, 0x05,
            0x2D, 0x06, 0x2D, 0x07, 0x30, 0x02, 0x30, 0x03, 0x30, 0x04, 0x30, 0x05, 0x30, 0x06, 0x30, 0x07,
            0x30, 0x08, 0x31, 0x02, 0x31, 0x03, 0x31, 0x04, 0x31, 0x05, 0x31, 0x06, 0x31, 0x07, 0x31, 0x08,
            0x32, 0x04, 0x33, 0x03, 0x34, 0x02, 0x34, 0x03, 0x35, 0x02, 0x35, 0x03, 0x38, 0x02, 0x38, 0x03,
            0x38, 0x04, 0x38, 0x05, 0x38, 0x06, 0x38, 0x07, 0x38, 0x08, 0x39, 0x02, 0x39, 0x03, 0x39, 0x04,
            0x39, 0x05, 0x39, 0x06, 0x39, 0x07, 0x39, 0x08, 0x3A, 0x02, 0x3B, 0x02, 0x3B, 0x03, 0x3B, 0x04,
            0x3B, 0x05, 0x3B, 0x06, 0x3B, 0x07, 0x3C, 0x02, 0x3D, 0x02, 0x3D, 0x03, 0x3D, 0x04, 0x3D, 0x05,
            0x3D, 0x06, 0x3D, 0x07, 0x3D, 0x08, 0x3E, 0x03, 0x3E, 0x04, 0x3E, 0x05, 0x3E, 0x06, 0x3E, 0x07,
            0x3E, 0x08, 0x40, 0x01, 0x40, 0x02, 0x40, 0x03, 0x40, 0x04, 0x41, 0x00, 0x41, 0x01, 0x41, 0x02,
            0x41, 0x03, 0x41, 0x04, 0x41, 0x05, 0x41, 0x08, 0x42, 0x00, 0x42, 0x05, 0x42, 0x07, 0x42, 0x08,
            0x43, 0x00, 0x43, 0x05, 0x43, 0x06, 0x43, 0x07, 0x43, 0x08, 0x44, 0x00, 0x44, 0x01, 0x44, 0x02,
            0x44, 0x03, 0x44, 0x04, 0x44, 0x05, 0x44, 0x06, 0x45, 0x01, 0x45, 0x02, 0x45, 0x03, 0x45, 0x04,
            0x45, 0x05, 0x48, 0x01, 0x48, 0x02, 0x48, 0x06, 0x48, 0x07, 0x49, 0x00, 0x49, 0x01, 0x49, 0x02,
            0x49, 0x06, 0x49, 0x07, 0x49, 0x08, 0x4A, 0x00, 0x4A, 0x04, 0x4A, 0x08, 0x4B, 0x00, 0x4B, 0x04,
            0x4B, 0x08, 0x4C, 0x00, 0x4C, 0x01, 0x4C, 0x02, 0x4C, 0x03, 0x4C, 0x04, 0x4C, 0x05, 0x4C, 0x06,
            0x4C, 0x07, 0x4C, 0x08, 0x4D, 0x01, 0x4D, 0x02, 0x4D, 0x03, 0x4D, 0x05, 0x4D, 0x06, 0x4D, 0x07,
            0x50, 0x08, 0x50, 0x09, 0x51, 0x06, 0x51, 0x07, 0x51, 0x08, 0x51, 0x09, 0x52, 0x04, 0x52, 0x05,
            0x52, 0x06, 0x52, 0x07, 0x53, 0x02, 0x53, 0x03, 0x53, 0x04, 0x53, 0x05, 0x54, 0x00, 0x54, 0x01,
            0x54, 0x02, 0x54, 0x03, 0x55, 0x00, 0x55, 0x01, 0x58, 0x05, 0x58, 0x06, 0x59, 0x00, 0x59, 0x01,
            0x59, 0x02, 0x59, 0x03, 0x59, 0x04, 0x59, 0x05, 0x59, 0x06, 0x5A, 0x00, 0x5A, 0x01, 0x5A, 0x02,
            0x5A, 0x03, 0x5A, 0x04, 0x5A, 0x06, 0x5B, 0x06, 0x5C, 0x02, 0x5C, 0x03, 0x5C, 0x04, 0x5C, 0x05,
            0x5C, 0x06, 0x5C, 0x07, 0x5C, 0x08, 0x5D, 0x02, 0x5D, 0x03, 0x5D, 0x04, 0x5D, 0x05, 0x5D, 0x06,
            0x5D, 0x07, 0x5D, 0x08, 0x5E, 0x06);

$objGD = imagecreatetruecolor(120, 30);

$color = imagecolorallocate($objGD, 0xFF, 0xFF, 0xFF);

for ($i = 0; $i < count($XY); $i+=2) {
    imagesetpixel($objGD, $XY[$i]+10, $XY[$i+1]+10, $color);
}

imagepng($objGD, "answer.png");

imagedestroy($objGD);

Well, plan didn't go as intended, but still, I was able to solve the challenge.

Application Challenge #16 (Windows)


Target: app16win.exe
Requirement: None given
Difficulty: medium

This seems to be the first Delphi compiled challenge:


And it's a console application:


IDA can handle Delphi applications, but to resolve its function calls it needs the appropriate definitions for the current Delphi version.
However, IDR is exactly built for Delphi reverse engineering, so what I always do is to load the app in IDR, dump a map file and then load the map in IDA.
I did the same here too:
start(), IDA decompile + IDR mappingvoid __noreturn start() {
    *off_41B5E4 = 1;
    SysInit__InitExe();
    v59 = &savedregs;
    v58 = (unsigned int)&loc_41971F;
    v57 = __readfsdword(0);
    __writefsdword(0, (unsigned int)&v57);
    *off_41B45C = 0;
    _Unit23_sub_00418CC4(v57);
    system__LStrAsg(v0, v76);
    _Unit23_sub_00418C40(v1, &v75);
    system__LStrAsg(v2, v75);
    system__LStrSetLength(v3, 256);
    v55 = system__LStrToPChar();
    v4 = (const CHAR *)system__LStrToPChar();
    v5 = GetShortPathNameA(v4, (LPSTR)v55, 0x100u);
    system__LStrSetLength(v6, v5);
    system__LStrAsg(v7, dword_41E8D8);
    _Unit25_sub_00418F4C(v57);
    system__LStrAsg(v8, v74);
// trimmed

I now have a easy to read code, but where should I start?
Usually looking for the error message is a good starting point, because that way I can back trace the code and get to the place where the code is verified.
Let's see the strings:
strings, IDACODE:00417248 00000011 C File is corrupt.          
CODE:00417EB8 0000001B C Compressed file is corrupt
CODE:00418D9C 00000005 C TEMP                      
CODE:00418DAC 0000000C C USERPROFILE               
CODE:00419734 00000008 C MYFILES                   
CODE:00419744 0000001A C Quick Batch File Compiler 
CODE:00419774 00000006 C FILES                     
CODE:004197FC 00000010 C command.com /c
Hey, hello...

Quick Batch File Compiler sounds interesting. Seems the challenge is a "compiled" BATCH code.
These XToEXE programs were quite popular some time ago, and all of them were usually a loader + script that gets executed by the loader, and judging by the "command.com /c" string, that seems to be quite possible scenario here.

So, let's see where this string is used:
command.com string, IDA disassemblyCODE:00419599                 push    offset aCommand_comC ; "command.com /c "
CODE:0041959E                 push    ds:dword_41E8D4
CODE:004195A4                 push    ds:dword_41E8DC
CODE:004195AA                 push    ds:dword_41E8D0
CODE:004195B0                 mov     eax, offset dword_41E8D0
CODE:004195B5                 mov     edx, 4
CODE:004195BA                 call    system_@LStrCatN
CODE:004195BF                 push    offset hProcess ; lpProcessInformation
CODE:004195C4                 push    offset StartupInfo ; lpStartupInfo
CODE:004195C9                 push    0               ; lpCurrentDirectory
CODE:004195CB                 push    0               ; lpEnvironment
CODE:004195CD                 push    20h             ; dwCreationFlags
CODE:004195CF                 push    0               ; bInheritHandles
CODE:004195D1                 push    0               ; lpThreadAttributes
CODE:004195D3                 push    0               ; lpProcessAttributes
CODE:004195D5                 mov     eax, ds:dword_41E8D0
CODE:004195DA                 call    system_@LStrToPChar
CODE:004195DF                 push    eax             ; lpCommandLine
CODE:004195E0                 push    0               ; lpApplicationName
CODE:004195E2                 call    CreateProcessA

Alright, time to start the debugger, set a breakpoint to CreateProcessA() and see what file is executed.
Turns out the file is %TEMP%/bt8570.bat (different file name every time the app is started) and its contents is:
bt8570.bat@shift 1
@echo off
echo Enter Password:
set pass=
set /p pass=
if "%pass%"=="speech" goto pass2
Echo wrong password!
pause
exit
:pass2
Echo Congrats! HTS password is freedom.
pause
exit
Well, that couldn't be easier.

Reverse engineering Quick Batch File Compiler is out of the scope of this article, so that's all you need to know about App16.


Application Challenge #17 (Windows)


Target: app17win.exe
Requirement: Create a serial unique to your HTS username.
Difficulty: hard

According to DiE, App17 is written in C or C++:


It's also a console application:


Let's get the IDA decompiled main():
main(), IDA decompileint __cdecl main(int argc, const char **argv, const char **envp) {
    operator new(0x200);
    operator new(0x200);
    v484 = (char *)operator new(0x200);

    // Encrypted "Username:"
    v13 = {0x154, 0x1CC, 0x194, 0x1C8, 0x1B8, 0x184, 0x1B4, 0x194, 0xE8, 0x80};
    memset(&v23, 0, 0x1FD8u);

    // Encrypted "Password:"
    v24 = {0x140, 0x184, 0x1CC, 0x1CC, 0x1DC, 0x1BC, 0x1C8, 0x190, 0xE8, 0x80};
    memset(&v34, 0, 0x1FD8u);

    // Encrypted welcome screen
    v35 = { 0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,\
            0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,\
            0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0x28,  0xA8,  0x80, 0x120, 0x184, 0x18C,\
           0x1AC, 0x150, 0x1A0, 0x1A4, 0x1CC, 0x14C, 0x1A4, 0x1D0, 0x194,  0x80, 0x104, 0x1C0, 0x1C0, 0x1B0, 0x1A4, 0x18C,\
           0x184, 0x1D0, 0x1A4, 0x1BC, 0x1B8,  0x80, 0x10C, 0x1A0, 0x184, 0x1B0, 0x1B0, 0x194, 0x1B8, 0x19C, 0x194,  0x80,\
            0x8C,  0xC4,  0xDC,  0x80,  0xA8,  0x28,  0xA8,  0x80,  0x80,  0x80,  0x80, 0x10C, 0x1BC, 0x190, 0x194, 0x190,\
            0x80, 0x188, 0x1E4,  0x80, 0x138, 0x1A4, 0x19C, 0x1A0, 0x1D0, 0x144, 0x1D4, 0x194, 0x1CC, 0x1D0,  0x80,  0xB4,\
            0x80,  0xC0,  0xC8,  0xB4,  0xC4,  0xD0,  0xB4,  0xC8,  0xC0,  0xC0,  0xE4,  0x80,  0x80,  0x80,  0x80,  0xA8,\
            0x28,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,\
            0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,\
            0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0xA8,  0x28, 0x13C, 0x188, 0x1A8, 0x194,\
           0x18C, 0x1D0, 0x1A4, 0x1D8, 0x194,  0xE8,  0x28, 0x164, 0x1BC, 0x1D4,  0x80, 0x1B8, 0x194, 0x194, 0x190,  0x80,\
           0x1D0, 0x1BC,  0x80, 0x18C, 0x1C8, 0x194, 0x184, 0x1D0, 0x194,  0x80, 0x184,  0x80, 0x1AC, 0x194, 0x1E4,  0x80,\
           0x1D0, 0x1A0, 0x184, 0x1D0,  0x80, 0x1A4, 0x1CC,  0x80, 0x1D4, 0x1B8, 0x1A4, 0x1C4, 0x1D4, 0x194,  0x80, 0x1D0,\
           0x1BC,  0x80, 0x1E4, 0x1BC, 0x1D4, 0x1C8,  0x80, 0x120, 0x184, 0x18C, 0x1AC, 0x150, 0x1A0, 0x1A4, 0x1CC, 0x14C,\
           0x1A4, 0x1D0, 0x194,  0x80, 0x1D4, 0x1CC, 0x194, 0x1C8, 0x1B8, 0x184, 0x1B4, 0x194,  0xB8,  0x28, 0x150, 0x1A0,\
           0x194,  0x80, 0x1A4, 0x190, 0x194, 0x184,  0x80, 0x1A4, 0x1CC,  0x80, 0x1D0, 0x1BC,  0x80, 0x18C, 0x1C8, 0x194,\
           0x184, 0x1D0, 0x194,  0x80, 0x184,  0x80, 0x1AC, 0x194, 0x1E4, 0x19C, 0x194, 0x1B8,  0xB0,  0x80, 0x188, 0x1D4,\
           0x1D0,  0x80, 0x184, 0x1B8, 0x1E4,  0x80, 0x1B4, 0x194, 0x1D0, 0x1A0, 0x1BC, 0x190,  0x80, 0x1A4, 0x1CC,  0x80,\
           0x184, 0x1B0, 0x1B0, 0x1BC, 0x1DC, 0x194, 0x190,  0xB8,  0x28, 0x104, 0x1B8,  0x80, 0x194, 0x1E0, 0x184, 0x1B4,\
           0x1C0, 0x1B0, 0x194,  0x80, 0x1DC, 0x1BC, 0x1D4, 0x1B0, 0x190,  0x80, 0x188, 0x194,  0xE8,  0x28, 0x154, 0x1CC,\
           0x194, 0x1C8, 0x1B8, 0x184, 0x1B4, 0x194,  0xE8,  0x80, 0x14C, 0x1BC, 0x1B4, 0x194, 0x154, 0x1CC, 0x194, 0x1C8,\
           0x138, 0x184, 0x1B4, 0x194,  0x28, 0x140, 0x184, 0x1CC, 0x1CC, 0x1DC, 0x1BC, 0x1C8, 0x190,  0xE8,  0x80, 0x120,\
           0x150, 0x14C,  0xB4,  0xC4,  0xC8,  0xCC,  0xD0,  0xB4,  0xD4,  0xD8,  0xDC,  0xE0,  0xB4,  0xE4,  0xC0,  0xC4,\
            0xC8,  0xB4,  0xCC,  0xD0,  0xD4,  0xD8,  0x28,  0x28};
    memset(&v427, 0, 0x19E0u);

    // Encrypted success message
    v428 = {0x10C, 0x1BC, 0x1B8, 0x19C, 0x1C8, 0x184, 0x1D0, 0x1D4, 0x1B0, 0x184, 0x1D0, 0x1A4, 0x1BC, 0x1B8, 0x1CC,  0x84,\
             0x80, 0x114, 0x1B8, 0x1D0, 0x194, 0x1C8,  0x80, 0x1D0, 0x1A0, 0x184, 0x1D0,  0x80, 0x1C0, 0x184, 0x1CC, 0x1CC,\
            0x1DC, 0x1BC, 0x1C8, 0x190,  0x80, 0x1BC, 0x1B8,  0x80, 0x120, 0x184, 0x18C, 0x1AC, 0x150, 0x1A0, 0x1A4, 0x1CC,\
            0x14C, 0x1A4, 0x1D0, 0x194,  0xB8,  0x28};
    v3 = 0;
    memset(&v482, 0, 0x1F28u);
    v4 = v484;
    *v484 = 0;

    // Decrypt and print the welcome message
    do {
        v5 = *(&v35 + v3);
        if (!v5)
            break;
        sprintf(v4, "%s%c", v4, (char)(v5 >> 2));
        ++v3;
    } while (v3 < 2048);
    printf(v4);

    // Start the prompt loop
    do {
        *v4 = 0;
        v6 = 0;

        // Decrypt and print the "Username:" label
        do {
            v7 = *(&v13 + v6);
            if ( !v7 )
                break;
            sprintf(v4, "%s%c", v4, (char)(v7 >> 2));
            ++v6;
        } while (v6 < 2048);
        printf(v4);

        v8 = 0;
        // Prompt the user for his Username
        v484 = (char*)sub_401000(0);
		
        // Decrypt and print the "Password:" label
        *v4 = 0;
        do {
            v9 = *(&v24 + v8);
            if (!v9)
                break;
            sprintf(v4, "%s%c", v4, (char)(v9 >> 2));
            ++v8;
        } while (v8 < 2048);
        printf(v4);

        // Prompt the user for his Password
        v483 = (void *)sub_401000(1);
    // The user/pass loop will run until the user enters a valid username and password
    } while (sub_40109F(v484, (char *)v483));
    *v4 = 0;
    v10 = 0;

    // Decrypt and print the success message
    do {
        v11 = *(&v428 + v10);
        if ( !v11 )
            break;
        sprintf(v4, "%s%c", v4, (char)(v11 >> 2));
        ++v10;
    } while (v10 < 2048);
    printf(v4);

    _getch();
    operator delete(v4);
    operator delete(v484);
    operator delete(v483);
    return 0;
}

So, the strings are encrypted by a shift right by 2, that is basically divide by 4.
Knowing that I can decrypt the success message:
String decryptor#include <windows.h>
#include <stdio.h>

int main() {
    DWORD data[] = {0x10C, 0x1BC, 0x1B8, 0x19C, 0x1C8, 0x184, 0x1D0, 0x1D4, 0x1B0, 0x184, 0x1D0, 0x1A4, 0x1BC, 0x1B8, 0x1CC,  0x84,\
                     0x80, 0x114, 0x1B8, 0x1D0, 0x194, 0x1C8,  0x80, 0x1D0, 0x1A0, 0x184, 0x1D0,  0x80, 0x1C0, 0x184, 0x1CC, 0x1CC,\
                    0x1DC, 0x1BC, 0x1C8, 0x190,  0x80, 0x1BC, 0x1B8,  0x80, 0x120, 0x184, 0x18C, 0x1AC, 0x150, 0x1A0, 0x1A4, 0x1CC,\
                    0x14C, 0x1A4, 0x1D0, 0x194,  0xB8,  0x28};
    for(int i = 0; i < sizeof(data)/sizeof(DWORD); i++) {
        printf("%c", data[i]/4);
    }
    printf("\n");

    return 0;
}

This gives me the message "Congratulations! Enter that password on HackThisSite."

Alright, so my next stop will be the sub_40109F() where the correct password is calculated.
The first passed parameter is my Username, and the second is my Password.
Unfortunately, IDA's decompile was complete mess, so I'll have to manhandle this routine in assembler, like a grown up. :)
sub_40109F() password validator, step one, x86dbg disassembly00DE1101 | 33 C0                   | xor eax,eax
00DE1103 | 89 45 AC                | mov dword ptr ss:[ebp-54],eax
00DE1106 | 89 45 94                | mov dword ptr ss:[ebp-6C],eax
00DE1109 | C7 45 80 20 01 00 00    | mov dword ptr ss:[ebp-80],120          ; Encrypted data
00DE1110 | C7 45 84 50 01 00 00    | mov dword ptr ss:[ebp-7C],150
00DE1117 | C7 45 88 4C 01 00 00    | mov dword ptr ss:[ebp-78],14C
00DE111E | C7 45 8C B4 00 00 00    | mov dword ptr ss:[ebp-74],B4
00DE1125 | 88 45 AA                | mov byte ptr ss:[ebp-56],al
00DE1128 | 88 45 B0                | mov byte ptr ss:[ebp-50],al
00DE112B | 0F BE 0C 03             | movsx ecx,byte ptr ds:[ebx+eax]       
00DE112F | C1 E1 02                | shl ecx,2                              ; character of user's password is shifted (multiplied by 4)
00DE1132 | 3B 4C 85 80             | cmp ecx,dword ptr ss:[ebp+eax*4-80]    ; compare the result with the encrypted data from above
00DE1136 | 0F 85 76 01 00 00       | jne app17win.DE12B2                    ; Jump to exit
00DE113C | 40                      | inc eax
00DE113D | 83 F8 04                | cmp eax,4                              ; Run the loop 4 times so only the first four bytes
                                                                            ; of the user's password are checked
00DE1140 | 72 E9                   | jb app17win.DE112B

Decrypting the four DWORDS 0x120, 0x150, 0x14C and 0xB4 gives me the string "HTS-", so my password should always start with these four bytes.

Next, the code verifies if the dashes are in the right place:
sub_40109F() password validator, step two, x86dbg disassembly00DE1142 | 6A 03                   | push 3                           ;/ init the iterator to 3
00DE1144 | 5F                      | pop edi                          ;\
00DE1145 | 53                      | push ebx
00DE1146 | E8 D5 15 00 00          | call <app17win._strlen>          ; take user's password length
00DE114B | 83 E8 04                | sub eax,4                        ; and subtract 4 from it
00DE114E | 59                      | pop ecx
00DE114F | 3B C7                   | cmp eax,edi                      ; jump if the length is less than 4
00DE1151 | 76 1B                   | jbe app17win.DE116E
00DE1153 | 80 3C 3B 2D             | cmp byte ptr ds:[ebx+edi],'-'    ; check if the current character is a dash
00DE1157 | 0F 85 55 01 00 00       | jne app17win.DE12B2              ; jump to exit
00DE115D | 53                      | push ebx
00DE115E | 83 C7 05                | add edi,5                        ; add 5 to the iterator
00DE1161 | E8 BA 15 00 00          | call <app17win._strlen>
00DE1166 | 83 E8 04                | sub eax,4
00DE1169 | 59                      | pop ecx
00DE116A | 3B F8                   | cmp edi,eax
00DE116C | 72 E5                   | jb app17win.DE1153               ; loop till the end of the user's password
00DE116E | 6A 03                   | push 3
So, the correct code has dashes after every 4th character, excluding the starting "HTS", that had dash after its 3rd character.

Moving to the next step, here the "HTS-" string and the dash characters are skipped, and the rest is converted into a uninterrupted string:
sub_40109F() password validator, step three, x86dbg disassembly00DE116E | 6A 03                   | push 3                           ;/ min length
00DE1170 | 5F                      | pop edi                          ;|
00DE1171 | 53                      | push ebx                         ;|
00DE1172 | E8 A9 15 00 00          | call <app17win._strlen>          ;| get user's password length
00DE1177 | 59                      | pop ecx                          ;|
00DE1178 | 3B C7                   | cmp eax,edi                      ;|
00DE117A | 76 29                   | jbe app17win.DE11A5              ;\ jump if length is less than 4
00DE117C | 8A 04 3B                | mov al,byte ptr ds:[ebx+edi]     ; iterate through the user's password
00DE117F | 3C 2D                   | cmp al,'-'                       ; check if the current character is a dash
00DE1181 | 74 16                   | je app17win.DE1199               ; jump if the character is dash
00DE1183 | 0F BE C0                | movsx eax,al                     ;/ concatenate the character to the result buffer
00DE1186 | 50                      | push eax                         ;|
00DE1187 | 8D 45 B0                | lea eax,dword ptr ss:[ebp-50]    ;|
00DE118A | 50                      | push eax                         ;|
00DE118B | 68 64 D1 DE 00          | push <app17win.aSC>              ;| "%s%c"
00DE1190 | 50                      | push eax                         ;|
00DE1191 | E8 3A 14 00 00          | call <app17win._sprintf>         ;\
00DE1196 | 83 C4 10                | add esp,10
00DE1199 | 53                      | push ebx
00DE119A | 47                      | inc edi
00DE119B | E8 80 15 00 00          | call <app17win._strlen>
00DE11A0 | 59                      | pop ecx
00DE11A1 | 3B F8                   | cmp edi,eax
00DE11A3 | 72 D7                   | jb app17win.DE117C
Basically my fake password HTS-1234-5678 is turned into 12345678.

Next validation verifies the username and password lengths:
sub_40109F() password validator, step four, x86dbg disassembly00DE11A5 | 56                      | push esi                         ;/ username
00DE11A6 | E8 75 15 00 00          | call <app17win._strlen>          ;| get the username length
00DE11AB | 8B F8                   | mov edi,eax                      ;\
00DE11AD | 8D 45 B0                | lea eax,dword ptr ss:[ebp-50]    ;/ password, after stripping the "HTS" and dashes from it
00DE11B0 | 50                      | push eax                         ;|
00DE11B1 | 03 FF                   | add edi,edi                      ;| double the length of the username
00DE11B3 | E8 68 15 00 00          | call <app17win._strlen>          ;\ get the password length
00DE11B8 | 59                      | pop ecx
00DE11B9 | 59                      | pop ecx
00DE11BA | 3B F8                   | cmp edi,eax                      ; the password length must equal to username's length times 2
00DE11BC | 0F 85 F0 00 00 00       | jne app17win.DE12B2              ; jump to exit
00DE11C2 | 83 65 9C 00             | and dword ptr ss:[ebp-64],0
00DE11C6 | 56                      | push esi                         ;/ username
00DE11C7 | E8 54 15 00 00          | call <app17win._strlen>          ;|
00DE11CC | 59                      | pop ecx                          ;|
00DE11CD | 85 C0                   | test eax,eax                     ;\ the username length must be longer than 0
00DE11CF | 0F 86 D9 00 00 00       | jbe app17win.DE12AE

The final step is both generator and validator of the correct password.
So far I know the format of a possibly correct password, so I'll be using "HTS-1122-3344-5566" as password for this step.
sub_40109F() password validator, final step, x86dbg disassembly00DE11D5 | 8D 7D B1                | lea edi,dword ptr ss:[ebp-4F]
00DE11D8 | EB 03                   | jmp app17win.DE11DD
; main loop start
00DE11DA | 8B 75 A4                | mov esi,dword ptr ss:[ebp-5C]
00DE11DD | 8A 47 FF                | mov al,byte ptr ds:[edi-1]          ; take second character of my password
00DE11E0 | 88 45 A8                | mov byte ptr ss:[ebp-58],al
00DE11E3 | 8A 07                   | mov al,byte ptr ds:[edi]            ; take first character of my password
00DE11E5 | 88 45 A9                | mov byte ptr ss:[ebp-57],al
00DE11E8 | 8D 45 94                | lea eax,dword ptr ss:[ebp-6C]       ;/ result
00DE11EB | 50                      | push eax                            ;|
00DE11EC | 8D 45 A8                | lea eax,dword ptr ss:[ebp-58]       ;| first two bytes of my password
00DE11EF | 68 78 D1 DE 00          | push <app17win.a02x>                ;| scanf format "%02X"
00DE11F4 | 50                      | push eax                            ;|
00DE11F5 | E8 EA 16 00 00          | call <app17win._sscanf>             ;\ convert the two bytes to HEX
00DE11FA | 8B 45 9C                | mov eax,dword ptr ss:[ebp-64]       ; iterator
00DE11FD | 0F BE 04 06             | movsx eax,byte ptr ds:[esi+eax]     ; iterate through my username - "XpoZed"
00DE1201 | 8B 4D AC                | mov ecx,dword ptr ss:[ebp-54]       ; initialized to 0 in the start
00DE1204 | 83 65 A0 00             | and dword ptr ss:[ebp-60],0
00DE1208 | 8B F0                   | mov esi,eax                         ; ESI = EAX = 'X'
00DE120A | D3 E0                   | shl eax,cl                          ; EAX <<= CL
00DE120C | 2B F1                   | sub esi,ecx                         ; ESI -= ECX
00DE120E | D1 FE                   | sar esi,1                           ; ESI >>= 0x01
00DE1210 | 53                      | push ebx                            ; my fully formatted password - "HTS-1122-3344-5566"
00DE1211 | F7 D0                   | not eax                             ; ~EAX
00DE1213 | 23 F0                   | and esi,eax                         ; ESI &= EAX
00DE1215 | 8B 45 94                | mov eax,dword ptr ss:[ebp-6C]       ; byte of my fake password - 0x11
00DE1218 | 89 75 AC                | mov dword ptr ss:[ebp-54],esi       ; ESI = ((ESI - ECX) >> 0x01) & ~(EAX << CL)
                                                                         ; ESI = (('X' - 0) >> 0x01) & ~('X' << 0) = 0x00000024
                                                                         ; this seems to be the correct code generator!!
00DE121B | 89 45 90                | mov dword ptr ss:[ebp-70],eax       ; store my password byte here
00DE121E | E8 FD 14 00 00          | call <app17win._strlen>             ;/ check the length of the fully formatted password
00DE1223 | 83 C4 10                | add esp,10                          ;|
00DE1226 | 85 C0                   | test eax,eax                        ;|
00DE1228 | 76 62                   | jbe app17win.DE128C                 ;\ and jump if it's entirely iterated through
00DE122A | FF 75 A4                | push dword ptr ss:[ebp-5C]          ;/ my username - "XpoZed"
00DE122D | 83 65 98 00             | and dword ptr ss:[ebp-68],0         ;|
00DE1231 | E8 EA 14 00 00          | call <app17win._strlen>             ;|
00DE1236 | 59                      | pop ecx                             ;|
00DE1237 | 85 C0                   | test eax,eax                        ;|
00DE1239 | 76 42                   | jbe app17win.DE127D                 ;\ break the loop if the username iteration is complete
00DE123B | 83 7D AC 00             | cmp dword ptr ss:[ebp-54],0         ;/ if the value calculated above is zero
00DE123F | 75 07                   | jne app17win.DE1248                 ;|
00DE1241 | C7 45 AC 50 00 00 00    | mov dword ptr ss:[ebp-54],50        ;\ update it to 0x50
00DE1248 | 8B 4D 98                | mov ecx,dword ptr ss:[ebp-68]       ; username iterator (zeroed few lines ago)
00DE124B | 8B 55 A0                | mov edx,dword ptr ss:[ebp-60]       ; password iterator (zeroed few lines ago)
00DE124E | 0F BE 14 13             | movsx edx,byte ptr ds:[ebx+edx]     ; iterate through my fake password - "HTS-1122-3344-5566"
00DE1252 | 8B 45 A4                | mov eax,dword ptr ss:[ebp-5C]       ; my username - "XpoZed"
00DE1255 | 0F BE 04 08             | movsx eax,byte ptr ds:[eax+ecx]     ; iterate through my username - "XpoZed"
00DE1259 | 8B 4D AC                | mov ecx,dword ptr ss:[ebp-54]       ; password_base - 0x00000024
00DE125C | FF 75 A4                | push dword ptr ss:[ebp-5C]          ; my username again, used later for the strlen
00DE125F | D3 E2                   | shl edx,cl                          ; password[i] - 'H'
00DE1261 | 2B C1                   | sub eax,ecx                         ; username[i] - password_base : 'X' - 0x00000024
00DE1263 | D1 F8                   | sar eax,1                           ; ('X' - 0x00000024) >> 0x01
00DE1265 | F7 D2                   | not edx                             ; ~'H'
00DE1267 | 23 C2                   | and eax,edx                         ; ('X' - 0x00000024) >> 0x01 & ~'H'
00DE1269 | 23 C1                   | and eax,ecx                         ; (('X' - 0x00000024) >> 0x01 & ~'H') & 0x00000024
00DE126B | 40                      | inc eax                             ; ((('X' - 0x00000024) >> 0x01 & ~'H') & 0x00000024) + 0x01
00DE126C | FF 45 98                | inc dword ptr ss:[ebp-68]           ; username iterator + 1
00DE126F | 89 45 AC                | mov dword ptr ss:[ebp-54],eax
00DE1272 | E8 A9 14 00 00          | call <app17win._strlen>             ;/ username length
00DE1277 | 59                      | pop ecx                             ;|
00DE1278 | 39 45 98                | cmp dword ptr ss:[ebp-68],eax       ;| username loop limit
00DE127B | 72 BE                   | jb app17win.DE123B                  ;\
00DE127D | FF 45 A0                | inc dword ptr ss:[ebp-60]           ; password iterator + 1
00DE1280 | 53                      | push ebx
00DE1281 | E8 9A 14 00 00          | call <app17win._strlen>             ;/ password length
00DE1286 | 59                      | pop ecx                             ;|
00DE1287 | 39 45 A0                | cmp dword ptr ss:[ebp-60],eax       ;| password loop limit
00DE128A | 72 9E                   | jb app17win.DE122A                  ;\
00DE128C | 3B 75 94                | cmp esi,dword ptr ss:[ebp-6C]       ;/ this is what I'm looking for
                                                                         ;| ESI is the calculation from the username at 00DE1218
                                                                         ;| ebp-6C is the first byte of my password, converted to HEX
00DE128F | 75 21                   | jne app17win.DE12B2                 ;\ jump to exit
00DE1291 | 8B 45 90                | mov eax,dword ptr ss:[ebp-70]
00DE1294 | FF 75 A4                | push dword ptr ss:[ebp-5C]
00DE1297 | 47                      | inc edi
00DE1298 | 47                      | inc edi
00DE1299 | FF 45 9C                | inc dword ptr ss:[ebp-64]
00DE129C | 89 45 AC                | mov dword ptr ss:[ebp-54],eax
00DE129F | E8 7C 14 00 00          | call <app17win._strlen>
00DE12A4 | 59                      | pop ecx
00DE12A5 | 39 45 9C                | cmp dword ptr ss:[ebp-64],eax
00DE12A8 | 0F 82 2C FF FF FF       | jb app17win.DE11DA                  ; jump to main loop start, to continue the password validation
00DE12AE | 32 C0                   | xor al,al
00DE12B0 | EB 02                   | jmp app17win.DE12B4

Alright! Most of this code is doing the validation, so I can take the first part and write a keygen now:
app17 keygen, C// HackThisSite / application challenge #17
// Keygen
// XpoZed, nullsecurity.org / 17 Nov, 2017

#include <windows.h>
#include <stdio.h>

int main(DWORD argc, char *argv[]) {
    char     *username = argv[1];
    char     password[MAX_PATH];
    DWORD    key_byte;

    if (argc != 2) {
        printf("Usage: %s <username>", strrchr(argv[0], '\\')+1);
        return 0;
    }

    strcpy(password, "HTS");

    key_byte = 0;
    for (int i = 0; i < strlen(username); i++) {
        key_byte = ((username[i] - key_byte) >> 0x01) & ~(username[i] << LOBYTE(key_byte));
        if (i%2 == 0) {
            strcat(password, "-");
        }
        wsprintf(password+strlen(password), "%02X", key_byte);
    }
    printf("Username: %s\n", username);
    printf("Password: %s\n", password);

    return 0;
}

And finally, trying my keygen worked just fine:


That's it. Not hard at all.


Application Challenge #17 (UNIX)


Target: app17unix
Requirement: Create a serial unique to your HTS username.
Difficulty: hard

The code used for the UNIX version of this challenge is almost identical with the one for the Windows version, so refer to the Windows version for complete solution.
There's no changes in the password generation algorithm either, so my Windows keygen works here as well:



Application Challenge #18 (Windows)


Target: app18win.exe
Requirement: Yet another unique key mission ;)
Difficulty: hard

This is (currently) the last application challenge on HTS.
It's written in MSVC++:


Executing it gives the following message:


It looks like a real life scenario of a trial period shareware application, so let's load it in IDA:
WinMain(), IDA decompileint __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
    nShowCmda = sub_401DFA(hInstance, nShowCmd);
    if (nShowCmda) {
        sub_4010E2();
        v5 = LoadAcceleratorsA(hInstance, (LPCSTR)0x6A);
        while (GetMessageA(&Msg, 0, 0, 0)) {
            if (!TranslateAcceleratorA(nShowCmda, v5, &Msg)) {
                TranslateMessage(&Msg);
                DispatchMessageA(&Msg);
            }
        }
        result = Msg.wParam;
    } else {
        result = 0;
    }
    return result;
}
So there's a message loop, but it wont even execute, because of the message box I get.

Obviously I'll have to look in the first procedure sub_401DFA():
sub_401DFA(), IDA decompileHWND __cdecl sub_401DFA(HINSTANCE hInstance, int nCmdShow) {
    v9.cbSize = 48;
    v9.style = 0;
    v9.lpfnWndProc = (WNDPROC)sub_401C6B;
    v9.cbClsExtra = 0;
    v9.cbWndExtra = 0;
    v9.hInstance = hInstance;
    v9.hIcon = LoadIconA(hInstance, (LPCSTR)0x68);
    v9.hCursor = LoadCursorA(0, (LPCSTR)0x7F00);
    v9.hbrBackground = (HBRUSH)6;
    v9.lpszMenuName = (LPCSTR)103;
    v9.lpszClassName = lpClassName;
    v9.hIconSm = LoadIconA(hInstance, (LPCSTR)0x68);
    if (!RegisterClassExA(&v9))
        return 0;
    sub_401510();
    if (!flag_trial_expired) { // this variable is important
        MessageBoxA(0,
            "Your trial has expired!\nPlease purchase a license to continue using this product",
            "Trial has expired.",
            0x10u);
        return 0;
    }
    v3 = GetSystemMetrics(1) / 2;
    v4 = GetSystemMetrics(0) / 2;
    v5 = GetSystemMetrics(1) / 4;
    v6 = GetSystemMetrics(0);
    v7 = CreateWindowExA(0x200u, lpClassName, lpWindowName, 0xCF0000u, v6 / 4, v5, v4, v3, 0, 0, hInstance, 0);
    v8 = v7;
    if ( !v7 )
        return 0;
    hWnd = v7;
    ShowWindow(v7, nCmdShow);
    UpdateWindow(v8);
    return v8;
}

The message is here, and depending on the flag_trial_expired variable it will either show up or not.
There are two places of interest here - the window procedure sub_401C6B(), and the procedure sub_401510() right before the message.
The window procedure will be my second step to solve this challenge, but the first one is the sub_401510().
Unfortunately, the IDA decompiler produced absolute abomination of a code, so I'll continue in assembly (addresses doesn't match IDA, because of the ASLR enabled and I didn't bother disabling it):
sub_401510() / construct the license file name, x86dbg disassembly01391510 | 55                      | push ebp
01391511 | 8D AC 24 38 FF FF FF    | lea ebp,dword ptr ss:[esp-C8]
01391518 | 81 EC 48 01 00 00       | sub esp,148
0139151E | A1 18 50 39 01          | mov eax,dword ptr ds:[1395018]
01391523 | 33 C5                   | xor eax,ebp
01391525 | 89 85 C4 00 00 00       | mov dword ptr ss:[ebp+C4],eax
0139152B | 53                      | push ebx
0139152C | 56                      | push esi
0139152D | 57                      | push edi
0139152E | 6A 44                   | push 44                                                      ;/ allocate 0x44 bytes
01391530 | E8 5D 0B 00 00          | call <app18win.void * __cdecl operator new(unsigned int)>    ;|
01391535 | 8B F0                   | mov esi,eax                                                  ;\
01391537 | C7 04 24 04 01 00 00    | mov dword ptr ss:[esp],104                    ;/ MAX_PATH
0139153E | 8D 45 B4                | lea eax,dword ptr ss:[ebp-4C]                 ;|/ module filename buffer
01391541 | 50                      | push eax                                      ;|\
01391542 | 6A 00                   | push 0                                        ;|
01391544 | FF 15 48 30 39 01       | call dword ptr ds:[<&GetModuleHandleA>]       ;|
0139154A | 50                      | push eax                                      ;|
0139154B | FF 15 4C 30 39 01       | call dword ptr ds:[<&GetModuleFileNameA>]     ;\ grab the executable file name
01391551 | 85 C0                   | test eax,eax
01391553 | 0F 86 99 03 00 00       | jbe app18win.13918F2                          ; exit
01391559 | 83 EC 1C                | sub esp,1C
0139155C | 8D 45 B4                | lea eax,dword ptr ss:[ebp-4C]
0139155F | 8B CC                   | mov ecx,esp
01391561 | 89 65 A0                | mov dword ptr ss:[ebp-60],esp
01391564 | 50                      | push eax
01391565 | FF 15 7C 30 39 01       | call dword ptr ds:[<&std::allocator>]
0139156B | 8D 45 80                | lea eax,dword ptr ss:[ebp-80]
0139156E | 50                      | push eax
0139156F | E8 D8 FA FF FF          | call app18win.139104C                         ; extract the filepath from the fullpath
01391574 | 83 C4 20                | add esp,20
01391577 | 83 78 18 10             | cmp dword ptr ds:[eax+18],10
0139157B | 72 05                   | jb app18win.1391582
0139157D | 8B 40 04                | mov eax,dword ptr ds:[eax+4]
01391580 | EB 03                   | jmp app18win.1391585
01391582 | 83 C0 04                | add eax,4
01391585 | 50                      | push eax
01391586 | 8D 45 B4                | lea eax,dword ptr ss:[ebp-4C]
01391589 | 50                      | push eax
0139158A | E8 FD 0A 00 00          | call <app18win.strcpy>                        ; save the filename string
0139158F | 59                      | pop ecx
01391590 | 59                      | pop ecx
01391591 | 8D 4D 80                | lea ecx,dword ptr ss:[ebp-80]
01391594 | FF 15 88 30 39 01       | call dword ptr ds:[<&std::allocator>]
0139159A | 8D 45 B4                | lea eax,dword ptr ss:[ebp-4C]                 ;/ filename
0139159D | 68 4C 33 39 01          | push app18win.139334C                         ;| pattern "exe" 
013915A2 | 50                      | push eax                                      ;|
013915A3 | FF 15 D8 30 39 01       | call dword ptr ds:[<&strstr>]                 ;\ search for the "exe" extension in the filename
013915A9 | 6A 03                   | push 3                                        ;/
013915AB | 68 48 33 39 01          | push app18win.1393348                         ;| new extension - "lic"
013915B0 | 50                      | push eax                                      ;|
013915B1 | FF 15 D4 30 39 01       | call dword ptr ds:[<&strncpy>]                ;\ replace the "exe" with "lic"
013915B7 | 8D 45 B4                | lea eax,dword ptr ss:[ebp-4C]                 ;/ filename - "app18win.lic"
013915BA | 68 44 33 39 01          | push app18win.1393344                         ;| "rb" - open for binary read
013915BF | 50                      | push eax                                      ;|
013915C0 | FF 15 F4 30 39 01       | call dword ptr ds:[<&fopen>]                  ;| open the "lic" file
013915C6 | 83 C4 1C                | add esp,1C                                    ;|
013915C9 | 89 45 A4                | mov dword ptr ss:[ebp-5C],eax                 ;\ "lic" file handle
013915CC | 85 C0                   | test eax,eax
013915CE | 0F 84 25 03 00 00       | je app18win.13918F9

The license file is named app18win.lic and it's opened for binary reading.
Let's see what follows that:
sub_401510() / basic read and validation of the license file, x86dbg disassembly013915D4 | 50                      | push eax                        ;/
013915D5 | 6A 20                   | push 20                         ;| number of bytes to read 0x20
013915D7 | 6A 01                   | push 1                          ;|
013915D9 | 56                      | push esi                        ;| bufLIC - read buffer, previously allocated to 0x44 bytes
013915DA | FF 15 14 31 39 01       | call dword ptr ds:[<&fread>]    ;| read the lic file
013915E0 | 83 C4 10                | add esp,10                      ;|
013915E3 | 83 F8 20                | cmp eax,20                      ;|
013915E6 | 0F 85 FA 02 00 00       | jne app18win.13918E6            ;\ bad jump (cannot read file)
013915EC | 80 3E 00                | cmp byte ptr ds:[esi],0         ;/ check if bufLIC[0x00] is 0
013915EF | 0F 84 F1 02 00 00       | je app18win.13918E6             ;\ bad jump (bufLIC[0x00] is 0)
013915F5 | 80 7E 04 00             | cmp byte ptr ds:[esi+4],0       ;/ check if bufLIC[0x04] is 0
013915F9 | 0F 84 E7 02 00 00       | je app18win.13918E6             ;\ bad jump (bufLIC[0x04] is 0)
013915FF | 8D 7E 08                | lea edi,dword ptr ds:[esi+8]    ;/ pointer to bufLIC[0x08]
01391602 | 80 3F 00                | cmp byte ptr ds:[edi],0         ;| check if bufLIC[0x08] is 0
01391605 | 0F 84 DB 02 00 00       | je app18win.13918E6             ;\ bad jump (bufLIC[0x08] is 0)
0139160B | 57                      | push edi                        ;/ bufLIC+0x08
0139160C | E8 69 0A 00 00          | call <app18win.strlen>          ;| get the length of bufLIC+0x08
01391611 | 59                      | pop ecx                         ;|
01391612 | 83 F8 09                | cmp eax,9                       ;| check if bufLIC+0x08 length is 9
01391615 | 0F 85 CB 02 00 00       | jne app18win.13918E6            ;\ bad jump (length of bufLIC+0x08 is not 9)
0139161B | 8D 46 12                | lea eax,dword ptr ds:[esi+12]   ;/ pointer to bufLIC[0x12]
0139161E | 80 38 00                | cmp byte ptr ds:[eax],0         ;| check if bufLIC[0x12] is 0
01391621 | 0F 84 BF 02 00 00       | je app18win.13918E6             ;\ bad jump (bufLIC[0x12] is 0)
01391627 | 50                      | push eax                        ;/ bufLIC+0x12
01391628 | E8 4D 0A 00 00          | call <app18win.strlen>          ;| get the length of bufLIC+0x12
0139162D | 59                      | pop ecx                         ;|
0139162E | 83 F8 09                | cmp eax,9                       ;| check if bufLIC+0x12 length is 9
01391631 | 0F 85 AF 02 00 00       | jne app18win.13918E6            ;\ bad jump (length of bufLIC+0x12 is not 9)
01391637 | 8D 5E 1C                | lea ebx,dword ptr ds:[esi+1C]   ;/ pointer to bufLIC[0x1C]
0139163A | 80 3B 00                | cmp byte ptr ds:[ebx],0         ;| check if bufLIC[0x1C] is 0
0139163D | 0F 84 A3 02 00 00       | je app18win.13918E6             ;\ bad jump (bufLIC[0x1C] is 0)
01391643 | 68 40 33 39 01          | push app18win.1393340           ;/ str2 = "LIC"
01391648 | 56                      | push esi                        ;| str1 = bufLIC+0x00
01391649 | E8 38 0A 00 00          | call <app18win.strcmp>          ;| compare the two strings
0139164E | 59                      | pop ecx                         ;|
0139164F | 59                      | pop ecx                         ;|
01391650 | 85 C0                   | test eax,eax                    ;| are they equal?
01391652 | 0F 85 8E 02 00 00       | jne app18win.13918E6            ;\ bad jump - bufLIC+0x00 is not "LIC"
01391658 | 8D 46 04                | lea eax,dword ptr ds:[esi+4]    ;/
0139165B | 68 3C 33 39 01          | push app18win.139333C           ;| str2 = "1.8"
01391660 | 50                      | push eax                        ;| str1 = bufLIC+0x04
01391661 | E8 20 0A 00 00          | call <app18win.strcmp>          ;| compare the two strings
01391666 | 59                      | pop ecx                         ;|
01391667 | 59                      | pop ecx                         ;|
01391668 | 85 C0                   | test eax,eax                    ;| are they equal?
0139166A | 0F 85 76 02 00 00       | jne app18win.13918E6            ;\ bad jump bufLIC+0x04 is not "1.8"
01391670 | 68 38 33 39 01          | push app18win.1393338           ;/ str2 = "HTS"
01391675 | 53                      | push ebx                        ;| str1 = bufLIC+0x1C
01391676 | E8 0B 0A 00 00          | call <app18win.strcmp>          ;| compare the two strings
0139167B | 59                      | pop ecx                         ;|
0139167C | 59                      | pop ecx                         ;|
0139167D | 85 C0                   | test eax,eax                    ;| are they equal?
0139167F | 0F 85 61 02 00 00       | jne app18win.13918E6            ;\ bad jump bufLIC+0x1C is not "HTS"

Knowing how the license file is parsed, I can now build my initial data:
app18win.lic, HxDOffset   00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000 4C 49 43 00 31 2E 38 00 61 61 61 61 61 61 61 61    LIC.1.8.aaaaaaaa
00000010 61 00 62 62 62 62 62 62 62 62 62 00 48 54 53 00    a.bbbbbbbbb.HTS.

Of course that's not everything, but it's a start, so let's keep up with the code:
sub_401510() / parsing the 9 bytes strings, x86dbg disassembly01391685 | 6A 0A                   | push A                          ;/ base = 10
01391687 | 50                      | push eax                        ;|
01391688 | 57                      | push edi                        ;| lic_str = "aaaaaaaaa"
01391689 | 8B 3D 0C 31 39 01       | mov edi,dword ptr ds:[<&strtoul>]
0139168F | FF D7                   | call edi                        ;| convert the string to unsigned integer - val_A
01391691 | 83 C4 0C                | add esp,C                       ;\
01391694 | 83 F8 15                | cmp eax,15                      ;/ the following code validates the minimum value for val_A
01391697 | 89 45 AC                | mov dword ptr ss:[ebp-54],eax   ;| 
0139169A | 7F 0B                   | jg app18win.13916A7             ;| every value over 0x15 is taken as-is
0139169C | 6A 04                   | push 4                          ;|
0139169E | 58                      | pop eax                         ;|
0139169F | 39 45 AC                | cmp dword ptr ss:[ebp-54],eax   ;|
013916A2 | 7D 03                   | jge app18win.13916A7            ;| every value under 4 is set to 4
013916A4 | 89 45 AC                | mov dword ptr ss:[ebp-54],eax   ;\ so, the minimum value for val_A is 4
013916A7 | 6A 0A                   | push A                          ;/ base = 10
013916A9 | 8D 46 12                | lea eax,dword ptr ds:[esi+12]   ;|
013916AC | 6A 00                   | push 0                          ;|
013916AE | 50                      | push eax                        ;| lic_str = "bbbbbbbbb"
013916AF | FF D7                   | call edi                        ;| convert the string to unsigned integer - val_B
013916B1 | 83 C4 0C                | add esp,C                       ;\
013916B4 | 83 F8 14                | cmp eax,14                      ;/ the following code validates the maximum value for val_B
013916B7 | 89 45 A8                | mov dword ptr ss:[ebp-58],eax   ;|
013916BA | 7C 0D                   | jl app18win.13916C9             ;| every value under 0x14 is taken as-is
013916BC | B8 00 08 00 00          | mov eax,800                     ;|
013916C1 | 39 45 A8                | cmp dword ptr ss:[ebp-58],eax   ;|
013916C4 | 7E 03                   | jle app18win.13916C9            ;| every value over 0x800 is set to 0x800
013916C6 | 89 45 A8                | mov dword ptr ss:[ebp-58],eax   ;\ therefore, the maximum value of val_B is 0x800

Since these two strings "aaaaaaaaa" and "bbbbbbbbb" are converted to integers, I'll have to tweak my lic file a bit:
app18win.lic, HxDOffset   00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000 4C 49 43 00 31 2E 38 00 30 30 30 30 30 30 30 31    LIC.1.8.00000001
00000010 36 00 30 30 30 30 30 30 30 33 32 00 48 54 53 00    6.000000032.HTS.

I picked these numbers for clarity. Because 16 is 0x10 and 32 is 0x20 it's easer for me to follow these values in the debugger.
So back to the code:
sub_401510() / second stage read and validation of the license file, x86dbg disassembly013916C9 | 8B 7D AC                | mov edi,dword ptr ss:[ebp-54]      ;/ val_A = 0x10
013916CC | 47                      | inc edi                            ;| val_A + 1 = 0x11
013916CD | 57                      | push edi                           ;|
013916CE | E8 B8 08 00 00          | call app18win.1391F8B              ;\ malloc() 0x11 bytes for mem1
013916D3 | 8B 5D A8                | mov ebx,dword ptr ss:[ebp-58]      ;/ val_B = 0x20
013916D6 | 43                      | inc ebx                            ;| val_B + 1 = 0x21
013916D7 | 53                      | push ebx                           ;| 
013916D8 | 89 46 20                | mov dword ptr ds:[esi+20],eax      ;| mem1
013916DB | E8 AB 08 00 00          | call app18win.1391F8B              ;\ malloc() 0x21 bytes for mem2
013916E0 | 57                      | push edi                           ;/ val_A + 1 = 0x11
013916E1 | 6A 00                   | push 0                             ;|
013916E3 | FF 76 20                | push dword ptr ds:[esi+20]         ;| mem1
013916E6 | 89 46 24                | mov dword ptr ds:[esi+24],eax      ; mem2
013916E9 | E8 80 09 00 00          | call <app18win.memset>             ;\ fill mem1 with zeroes
013916EE | 53                      | push ebx                           ;/ val_B + 1 = 0x21
013916EF | 6A 00                   | push 0                             ;|
013916F1 | FF 76 24                | push dword ptr ds:[esi+24]         ;| mem2
013916F4 | E8 75 09 00 00          | call <app18win.memset>             ;\ fill mem2 with zeroes
013916F9 | FF 75 A4                | push dword ptr ss:[ebp-5C]         ;/
013916FC | 57                      | push edi                           ;| val_A + 1 = 0x11
013916FD | 6A 01                   | push 1                             ;|
013916FF | FF 76 20                | push dword ptr ds:[esi+20]         ;| mem1
01391702 | FF 15 14 31 39 01       | call dword ptr ds:[<&fread>]       ;| read 0x11 bytes from the lic file
01391708 | 83 C4 30                | add esp,30                         ;|
0139170B | 3B C7                   | cmp eax,edi                        ;|
0139170D | 0F 85 C1 01 00 00       | jne app18win.13918D4               ;\ bad jump (not enough bytes read for mem1)
01391713 | FF 75 A4                | push dword ptr ss:[ebp-5C]         ;/
01391716 | 8B 3D 14 31 39 01       | mov edi,dword ptr ds:[<&fread>]    ;|
0139171C | 53                      | push ebx                           ;| val_B + 1 = 0x21
0139171D | 6A 01                   | push 1                             ;|
0139171F | FF 76 24                | push dword ptr ds:[esi+24]         ;| mem2
01391722 | FF D7                   | call edi                           ;| read 0x21 bytes from the lic file
01391724 | 83 C4 10                | add esp,10                         ;|
01391727 | 3B C3                   | cmp eax,ebx                        ;|
01391729 | 0F 85 A5 01 00 00       | jne app18win.13918D4               ;\ bad jump (not enough bytes read for mem2)
0139172F | 8B 46 20                | mov eax,dword ptr ds:[esi+20]      ;/ mem1
01391732 | 80 38 00                | cmp byte ptr ds:[eax],0            ;|
01391735 | 0F 84 99 01 00 00       | je app18win.13918D4                ;\ bad jump (mem1[0] is 0)
0139173B | 8B 4E 24                | mov ecx,dword ptr ds:[esi+24]      ;/ mem2
0139173E | 80 39 00                | cmp byte ptr ds:[ecx],0            ;|
01391741 | 0F 84 8D 01 00 00       | je app18win.13918D4                ;\ bad jump (mem2[0] is 0)
01391747 | 50                      | push eax                           ;/ mem1
01391748 | E8 2D 09 00 00          | call <app18win.strlen>             ;|
0139174D | 59                      | pop ecx                            ;|
0139174E | 3B 45 AC                | cmp eax,dword ptr ss:[ebp-54]      ;|
01391751 | 0F 85 7D 01 00 00       | jne app18win.13918D4               ;\ bad jump (mem1 string length is not equal to val_A)
01391757 | FF 76 24                | push dword ptr ds:[esi+24]         ;/ mem2
0139175A | E8 1B 09 00 00          | call <app18win.strlen>             ;|
0139175F | 59                      | pop ecx                            ;|
01391760 | 3B 45 A8                | cmp eax,dword ptr ss:[ebp-58]      ;|
01391763 | 0F 85 6B 01 00 00       | jne app18win.13918D4               ;\ bad jump (mem2 string length is not equal to val_B)

Nice, I got few more details for my forged lic file, so I'll add them up:
app18win.lic, HxDOffset   00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000 4C 49 43 00 31 2E 38 00 30 30 30 30 30 30 30 31    LIC.1.8.00000001
00000010 36 00 30 30 30 30 30 30 30 33 32 00 48 54 53 00    6.000000032.HTS.
00000020 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61    aaaaaaaaaaaaaaaa
00000030 00 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62    .bbbbbbbbbbbbbbb
00000040 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62    bbbbbbbbbbbbbbbb
00000050 62 00                                              b.

Let's see what more can I fish out from the code:
sub_401510() / final read from the license file, x86dbg disassembly01391769 | FF 75 A4                | push dword ptr ss:[ebp-5C]        ;/
0139176C | 8D 5E 28                | lea ebx,dword ptr ds:[esi+28]     ;|
0139176F | 6A 1B                   | push 1B                           ;| bytes to read = 0x1B
01391771 | 6A 01                   | push 1                            ;|
01391773 | 53                      | push ebx                          ;| buffer = mem3
01391774 | FF D7                   | call edi                          ;| read file
01391776 | 83 C4 10                | add esp,10                        ;|
01391779 | 83 F8 1B                | cmp eax,1B                        ;|
0139177C | 0F 85 52 01 00 00       | jne app18win.13918D4              ;\ bad jump (not enough bytes read)
01391782 | 33 C0                   | xor eax,eax                       ;/ EAX is used as iterator
01391784 | 80 3C 03 00             | cmp byte ptr ds:[ebx+eax],0       ;| mem3[EAX] == 0?
01391788 | 0F 85 72 01 00 00       | jne app18win.1391900              ;| bad jump (mem3[EAX] is 0)
0139178E | 80 7C 30 39 00          | cmp byte ptr ds:[eax+esi+39],0    ;| mem3[EAX+0x11] == 0?
01391793 | 0F 85 67 01 00 00       | jne app18win.1391900              ;| bad jump (mem3[EAX+0x11] is 0)
01391799 | 40                      | inc eax                           ;| increment the iterator
0139179A | 83 F8 0A                | cmp eax,A                         ;| loop limit is set to 0x0A
0139179D | 72 E5                   | jb app18win.1391784               ;\ loop end
0139179F | 8D 46 32                | lea eax,dword ptr ds:[esi+32]     ;/ get a pointer to mem3+0x0A
013917A2 | 68 30 33 39 01          | push app18win.1393330             ;| str2 = "HTS_NQ"
013917A7 | 50                      | push eax                          ;| str1 = mem3+0x0A
013917A8 | E8 D9 08 00 00          | call <app18win.strcmp>            ;| compare "HTS_NQ" with mem3+0x0A
013917AD | 59                      | pop ecx                           ;|
013917AE | 59                      | pop ecx                           ;|
013917AF | 85 C0                   | test eax,eax                      ;|
013917B1 | 0F 85 1D 01 00 00       | jne app18win.13918D4              ;\ bad jump (comparision failed)

Alright, I got the final part of my forged license file:
app18win.lic, HxDOffset   00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000 4C 49 43 00 31 2E 38 00 30 30 30 30 30 30 30 31    LIC.1.8.00000001
00000010 36 00 30 30 30 30 30 30 30 33 32 00 48 54 53 00    6.000000032.HTS.
00000020 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61    aaaaaaaaaaaaaaaa
00000030 00 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62    .bbbbbbbbbbbbbbb
00000040 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62    bbbbbbbbbbbbbbbb
00000050 62 00 00 00 00 00 00 00 00 00 00 00 48 54 53 5F    b...........HTS_
00000060 4E 51 00 00 00 00 00 00 00 00 00 00 00             NQ...........

Having everything a license file should hold, I can analyse the last part, starting with the calculation loop:
sub_401510() / calculation, x86dbg disassembly013917B7 | 88 85 B8 00 00 00       | mov byte ptr ss:[ebp+B8],al        ;/ initialize the loop
013917BD | 8D BD B9 00 00 00       | lea edi,dword ptr ss:[ebp+B9]      ;|
013917C3 | AB                      | stosd dword ptr es:[edi],eax       ;|
013917C4 | AB                      | stosd dword ptr es:[edi],eax       ;|
013917C5 | AA                      | stosb byte ptr es:[edi],al         ;|
013917C6 | 8B 46 24                | mov eax,dword ptr ds:[esi+24]      ;|
013917C9 | 33 FF                   | xor edi,edi                        ;|
013917CB | 89 7D A0                | mov dword ptr ss:[ebp-60],edi      ;|
013917CE | 89 45 9C                | mov dword ptr ss:[ebp-64],eax      ;|
013917D1 | 89 7D AC                | mov dword ptr ss:[ebp-54],edi      ;|
; parent loop start
013917D4 | FF 76 20                | push dword ptr ds:[esi+20]         ;| mem1 = "aaaaaaaaaaaaaaaa"
013917D7 | 33 DB                   | xor ebx,ebx                        ;| EBX is the iterator
013917D9 | E8 9C 08 00 00          | call <app18win.strlen>             ;|
013917DE | 59                      | pop ecx                            ;|
013917DF | 85 C0                   | test eax,eax                       ;|
013917E1 | 76 44                   | jbe app18win.1391827               ;\
; child loop (calculator) start
013917E3 | 0F BE 4E 1C             | movsx ecx,byte ptr ds:[esi+1C]     ; 'H', taken from the lic file
013917E7 | 0F BE 46 1D             | movsx eax,byte ptr ds:[esi+1D]     ; 'T', taken from the lic file
013917EB | 03 C1                   | add eax,ecx                        ; 'H' + 'T'
013917ED | 0F BE 4E 1E             | movsx ecx,byte ptr ds:[esi+1E]     ; 'S', taken from the lic file
013917F1 | 03 C1                   | add eax,ecx                        ; 'H' + 'T' + 'S'
013917F3 | 2B F8                   | sub edi,eax                        ; EDI is originally initialized to 0
013917F5 | 8D 46 04                | lea eax,dword ptr ds:[esi+4]       ;/ "1.8", taken from the lic file
013917F8 | 50                      | push eax                           ;|
013917F9 | FF 15 08 31 39 01       | call dword ptr ds:[<&atoi>]        ;\ "1.8" == 1
013917FF | 8B 4D AC                | mov ecx,dword ptr ss:[ebp-54]      ; initialized to 0
01391802 | FF 76 20                | push dword ptr ds:[esi+20]         ; mem1 = "aaaaaaaaaaaaaaaa"
01391805 | 03 F8                   | add edi,eax                        ; 0 - 'H' + 'T' + 'S' + 1
01391807 | 8B 46 20                | mov eax,dword ptr ds:[esi+20]      ; mem1 = "aaaaaaaaaaaaaaaa"
0139180A | 0F BE 04 18             | movsx eax,byte ptr ds:[eax+ebx]    ; mem1[EBX]
0139180E | 0B F8                   | or edi,eax                         ; EDI = (0 - 'H' + 'T' + 'S' + 1) | mem1[EBX]
01391810 | 8B C7                   | mov eax,edi                        ; EAX = EDI
01391812 | D3 E8                   | shr eax,cl                         ; EAX >> CL
01391814 | 0B C3                   | or eax,ebx                         ; EAX | EBX
01391816 | 33 F8                   | xor edi,eax                        ; EDI ^ EAX
01391818 | 0F AF FF                | imul edi,edi                       ; EDI * EDI
0139181B | 43                      | inc ebx                            ; increment the iterator
0139181C | E8 59 08 00 00          | call <app18win.strlen>             ;/
01391821 | 59                      | pop ecx                            ;|
01391822 | 59                      | pop ecx                            ;|
01391823 | 3B D8                   | cmp ebx,eax                        ;\ the loop will iterate through the mem1 string
01391825 | 72 BC                   | jb app18win.13917E3                ; loop end
; child loop (calculator) end
01391827 | 33 DB                   | xor ebx,ebx                        ;/ skip the following code if this is the first iteration
01391829 | 39 5D AC                | cmp dword ptr ss:[ebp-54],ebx      ;|
0139182C | 0F 8E 80 00 00 00       | jle app18win.13918B2               ;\
01391832 | 6A 10                   | push 10                            ;/ base of 0x10
01391834 | 8D 85 B8 00 00 00       | lea eax,dword ptr ss:[ebp+B8]      ;|
0139183A | 50                      | push eax                           ;|
0139183B | 57                      | push edi                           ;| result from the loop above
0139183C | FF 15 04 31 39 01       | call dword ptr ds:[<&_itoa>]       ;\ convert EDI to HEX string - pass_chunk
01391842 | 8D 85 B8 00 00 00       | lea eax,dword ptr ss:[ebp+B8]
01391848 | 50                      | push eax
01391849 | 88 9D C0 00 00 00       | mov byte ptr ss:[ebp+C0],bl
0139184F | 88 5D B3                | mov byte ptr ss:[ebp-4D],bl
01391852 | 89 5D A8                | mov dword ptr ss:[ebp-58],ebx
01391855 | E8 20 08 00 00          | call <app18win.strlen>
0139185A | 83 C4 10                | add esp,10
0139185D | 85 C0                   | test eax,eax
0139185F | 76 51                   | jbe app18win.13918B2
01391861 | EB 03                   | jmp app18win.1391866
; children loop (validation) start
01391863 | 8B 5D A8                | mov ebx,dword ptr ss:[ebp-58]      ; loop iterator, initialized to 0
01391866 | 80 7D B3 00             | cmp byte ptr ss:[ebp-4D],0         ;/ this part will skip any starting zeroes from pass_chunk
0139186A | 75 0A                   | jne app18win.1391876               ;|
0139186C | 80 BC 1D B8 00 00 00 30 | cmp byte ptr ss:[ebp+ebx+B8],30    ;|
01391874 | 74 3C                   | je app18win.13918B2                ;\
01391876 | 8D 9C 1D B8 00 00 00    | lea ebx,dword ptr ss:[ebp+ebx+B8]  ;/ pass_chunk[EBX]
0139187D | 0F BE 03                | movsx eax,byte ptr ds:[ebx]        ;|
01391880 | 50                      | push eax                           ;|
01391881 | C6 45 B3 01             | mov byte ptr ss:[ebp-4D],1         ;|
01391885 | FF 15 00 31 39 01       | call dword ptr ds:[<&toupper>]     ;\ convert the pass_chunk to uppercase
0139188B | 59                      | pop ecx
0139188C | 8B 4D 9C                | mov ecx,dword ptr ss:[ebp-64]      ; mem2 - "bbbbbbbbb..."
0139188F | FF 45 9C                | inc dword ptr ss:[ebp-64]
01391892 | 88 03                   | mov byte ptr ds:[ebx],al
01391894 | 8A 09                   | mov cl,byte ptr ds:[ecx]           ;/ compare byte of mem2 with a byte of pass_chunk
01391896 | 3A C8                   | cmp cl,al                          ;|
01391898 | 75 66                   | jne app18win.1391900               ;\ this is the validation check
0139189A | FF 45 A8                | inc dword ptr ss:[ebp-58]
0139189D | FF 45 A0                | inc dword ptr ss:[ebp-60]
013918A0 | 8D 85 B8 00 00 00       | lea eax,dword ptr ss:[ebp+B8]      ;/ the validation loop will run
013918A6 | 50                      | push eax                           ;| until all the pass_chunk are checked
013918A7 | E8 CE 07 00 00          | call <app18win.strlen>             ;|
013918AC | 59                      | pop ecx                            ;|
013918AD | 39 45 A8                | cmp dword ptr ss:[ebp-58],eax      ;|
013918B0 | 72 B1                   | jb app18win.1391863                ;\
; children loop (validation) end
013918B2 | FF 45 AC                | inc dword ptr ss:[ebp-54]
013918B5 | 83 7D AC 32             | cmp dword ptr ss:[ebp-54],32       ; parent loop limit is set to 0x32
013918B9 | 0F 8C 15 FF FF FF       | jl app18win.13917D4                ; end 
; parent loop end

So, here's how the algorithm works:
- generates a serial "chunk" by looping through the username (mem1)
- convert the chunk to HEX string in uppercase
- verifies the string chunk against the password (mem2) at each iteration
- loops and executes that algorithm 0x32 times

Good, I have everything I need to know to build a keygen, so let's do that:
app18win.exe keyfile generator, C// HackThisSite / application challenge #18
// Keyfile generator
// XpoZed, nullsecurity.org / 19 Nov, 2017

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

int main(DWORD argc, char *argv[]) {
    byte *keyfile;
    DWORD keyfile_len;
    char *username;
    char password[MAX_PATH*2];
    DWORD chunk;

    if (argc != 2) {
        printf("Usage: %s <username>", strrchr(argv[0], '\\')+1);
        return 0;
    }

    // calculate the password string for given username
    username = argv[1];
    memset(password, 0, MAX_PATH);
    chunk = 0;
    for(int i = 0; i < 0x32; i++) {
        for(int j = 0; j < strlen(username); j++) {
            chunk = (chunk - ('H' + 'T' + 'S') + 1) | username[j];
            chunk ^= (chunk >> i) | j;
            chunk *= chunk;
        }
        if (i > 0 && chunk > 0) {
            // append chunk to the final password
            sprintf(password+strlen(password), "%X", chunk);
        }
    }

    // calculate the length of the key file
    keyfile_len = 0x20; // header
    keyfile_len += strlen(username) + 1; // username plus termination zero
    keyfile_len += strlen(password) + 1; // password plus termination zero
    keyfile_len += 0x1B; // tail

    keyfile = malloc(keyfile_len);
    memset(keyfile, 0, keyfile_len);

    // populate the key file
    strcpy(keyfile, "LIC");
    strcpy(keyfile+0x04, "1.8");
    sprintf(keyfile+0x08, "%09d", strlen(username));
    sprintf(keyfile+0x12, "%09d", strlen(password));
    strcpy(keyfile+0x1C, "HTS");
    strcpy(keyfile+0x20, username);
    strcpy(keyfile+0x21+strlen(username), password);
    strcpy(keyfile+0x2C+strlen(username)+strlen(password), "HTS_NQ");

    printf("username: %s\n", username);
    printf("app18win.lic:\n");
    for(int i = 0; i < keyfile_len; i++) {
        printf("%02X ", keyfile[i]);
        if ((i+1)%0x10 == 0) { printf("\n"); }
    }
    free(keyfile);

    return 0;
}

Trying a generated license file gives the following result:


And that's app18! By the way, there are reports that the validation on the website doesn't work well, so keep that in mind.

Comments

* You have an opinion? Let us all hear it!

There's no comments on this article yet. Be the first!
© nullsecurity.org 2011-2017 | legal | terms & rules | contacts
www.000webhost.com