<-- Flare-On 2015 Index / FLARE-On 2015 Challenge #9 
FLARE-On 2015 Challenge #9

Date: Aug 21, 2015


filename:    you_are_very_good_at_this    DOWNLOAD
size4 k (4,608 bytes)
typeWin32 Console App
Original FLARE AuthorNick Harbour
tool:    OllyDbg 2.01 / Debugger    Visit Website
tool:    Your favorite C/C++ compiler

The original console app from challenge #1 and #2 is back with a vengeance. When you finish, you'll be surprised the code fits within this 5 k executable!

A hex dump of the first few bytes of the supplied file reveals that it is a Windows PE executable image. Renaming to EXE and launching from the command line begins the challenge with a taunting password prompt. Typing the wrong password results in the familiar "You are failure" message:

Flare-On 2015 Challenge #9 - Bring it on baby...

Lets see what IDA can tell us about the executable. Right away, IDA gives us "The imports segment seems to be destroyed..." warning. This has never stopped us before, so we continue to the "Exports" tab to follow the entry point labeled "start". The entry point begins with a "JMP start_0" instruction. Following the target, we are taken to a chunk of code with the "sp-analysis failed" error in red a few lines down. The code makes room for a DWORD on the stack, and sets it to 401091 in a non-straightforward way, just before executing a RET instruction. Because RET is designed to pop the value off at ESP and jump to it, we jump to address 401091. Right off the bat, IDA is confused, probably because the only path into this block was a JMP, not a CALL, so the RET has confused it. This is one of the many anti-disassembly tricks you'll see in this challenge.

Flare-On 2015 Challenge #9 - IDA sp-analysis failed

If you scroll to the top of the disassembly window, you'll notice IDA has found a familiar-looking function that invokes GetStdHandle() twice, then WriteFile(), and finally ReadFile(). Following the call to ReadFile() is a call to what would seem to be the validation function. Upon return from the validation function are the branches to output either the success or failure messages. This entire function is a decoy. One clue is the password prompt only displays "I have evolved since the first challenge." without the rest. The function is not referenced by the program during an actual run and the program crashes after the validation function is called if you force the debugger to run the code by manually setting EIP. The validation function self-destructs the remainder of calling function by writing some invalid opcodes where the return address is pointing that cause an an "Illegal Instruction" exception when the validation function returns. While it may have been used during development to get the opcodes needed to plug into different parts of the program (we'll get to that in a minute), the author cleverly left it in to send you down the wrong path.

IDA isn't "the best tool for the job" for this challenge. You'll soon see why manually fixing up the program in IDA each time an anti-disassembly technique is reached would be a lost cause. More mileage can be made in the debugger. Fire up the program in OllyDbg and you should find yourself at the entry point 401C48:

00401C48 E9 32F8FFFF JMP 0040147F

Since this program intentionally avoids making proper use of CALL/RET pair as one of its anti-disassembly techniques, you will almost exclusively be using the step-into (F7 key) feature to the step through the code. Stepping-over a CALL that never returns is like running the app from its current position.

Step past the initial JMP and down to the RET in the next block of code. This unconventional use of RET is anti-disassembly trick #1. It is only used to transfer control to 401091, but had the author used a "JMP 401091", static analysis tools like IDA would have marked the target address as the beginning of code and would traverse it automatically. Because static disassemblers don't emulate code, but rather they focus mostly on paths made through code via the branch control instructions like JMP, Jxx, matching CALL/RET, LOOP and the like, they can't handle this construct. This causes the majority of the program to be marked as data and not code.

Since the CALL instruction is designed to push onto the stack the address of the next instruction prior to jumping to the target address, the RET within the called block was designed to pop the return address pointed at by ESP into EIP to resume execution just past the CALL. In this case, the code manually builds the jump target address on the stack so it is the first to be pulled off and jumped to when the RET is executed, just like an unconditional jump.

Flare-On 2015 Challenge #9 - Anti-Disassembly Trick #1 - Unconventional use of RET

Stepping to 401091 appears to show us that we are executing raw data, rather than code instructions. The processor is still executing instructions whether or not OllyDbg is confused as to whether we are in a code or data block. OllyDbg thinks this is data because it scans the entry points automatically during module load just like IDA does, and like IDA it didn't find a path into the code currently being executed. Because OllyDbg thinks this is data, it uses a common assembler DB (Define Byte) syntax to display the raw hex bytes.

Flare-On 2015 Challenge #9 - OllyDbg's analysis sometimes needs massaging

We just need to override the default analysis by forcing OllyDbg to "un-analyze" it. Start a multi-line selection from 401091 down to the RETN instruction at 4010AA to contain all the "DB" byte lines. Right-click the selection and choose "Analysis" -> "Remove analysis from selection" (or press CTRL-Bkspc). You will now see the data turn into assembly instructions based from the selection's start. Always "un-analyze" code based from the current EIP to ensure the disassembler's instruction interpretation is in sync. If you make a mistake, its not a huge deal as the processor will still execute the proper instructions and OllyDbg automatically re-synchronizes its disassembler display at each step (more on this below). As you step through instructions in this challenge, you will frequently need to inform OllyDbg to interpret the bytes as code rather than data, so perform this step as often as necessary.

Flare-On 2015 Challenge #9 - OllyDbg's unanalyze restores disassembly

Considering that "XOR EAX,EAX" executing prior to the JE instruction essentially hardcodes the processor flags necessary to ensure the jump is always taken and is thus semantically equivalent to a JMP instruction. This is anti-disassembly trick #2. When disassemblers encounter conditional jump instructions, they will analyze both the jump-taken path and the jump-not-taken path (the next instruction). Code employing this technique cause the disassembler to analyze code paths that have no chance in executing, such as the decoy function described above. The reverse engineer can also waste time deciphering the purpose for the condition of the bogus branch, adding to the overall complexity of the program. These false branches often contain invalid instructions, or instructions that don't make any sense in the current context such as privileges instructions in a user-mode program. A variation on this trick is a CALL that is never returned to. Because the purpose of a CALL implies it will be returned-to, the code path that follows will be analyzed automatically by the disassembler (and human) leading to the same problems as those caused by the unconditional jumps made to look conditional.

Flare-On 2015 Challenge #9 - Anti-Disassembly trick #3

Just when we've determined the JE instruction at 401093 is really an unconditional jump, this program wastes no time in employing another trick. Notice that the target address of 401096 isn't listed anywhere in the address column, as its value falls between those addresses shown. The program appears to be jumping in the middle of another instruction based on how the disassembly window has aligned with the current flow of the opcode bytes, but the processor only cares that the bytes at the target address represent a valid instruction. In a normal program, jumping into the middle of opcode bytes meant for other instructions will likely lead to a crash because the processor will misinterpret the instruction and every instructions that follows. For a specially crafted program like this, the instructions being jumped into are actually valid, but are out of sync with the disassembler display as the disassembler can only interpret any given opcode bytes as belonging to one instruction at a time. If the disassembler has been tricked into displaying invalid instructions at a particular address and then the program later jumps between them, those instructions will automatically turn into completely different and valid instructions when the disassembler re-synchronizes with the current EIP. You'll know OllyDbg re-synchronizes when the current disassembly instruction jumps to the top of the window for seemingly no other reason. As a result, the previously executing instructions disappear and the new ones appear. As long as the program doesn't generate an exception, the processor will happily execute any instructions wherever you point it to. For obvious reasons, this adds to complexity and confusion of a program. This is anti-disassembly trick #3.

OllyDbg normally draws a nice line with a slight curve to show the target of a branching instruction, but this target's line looks like it was cut off (see red arrow above). This is because a jump is targeting bytes currently occupied by other instructions. Step past the JE instruction and you'll see the disassembly window automatically re-synchronize to reflect the current EIP instruction at a "MOV EDX,EAX" which was not there before. The opcodes have not changed, only the interpretation has. The CALL instruction at 401095 is made up of opcode bytes E8 89 C2 74 09. When we jump 1 byte into this address, the processor interprets instructions starting with 89 C2 74 09 and so on. 89 C2 translates to the "MOV EDX, EAX" leaving 74 09 to be interpreted as a JE instruction. Since the program intended on the execution path containing valid instructions, we can ascertain that the originally viewed CALL instruction at 401095 was a bogus instruction (FYI: it actually points to invalid memory and would crash the program if executed).

There are even certain instruction combinations that can can be cleverly crafted such that the same byte can be used in two valid and working instructions as EIP runs through them. One simple example where a "JMP -1" jumps back inside of itself to the address occupied by the FF byte. This alters the interpretation for successive instructions producing the "INC EAX" from the "FF C0" combination.
        v  \
   JMP   -1 |
    EB | FF | C0
       | INC EAX
Tip: While a program is paused, you may need to scroll around, follow jump links or explicitly visit other addresses. If you lose track of where the current instruction is or the current instruction is no longer in sync based on the current view, you can return to the current instruction by double-clicking the address shown to the right of the EIP register in the CPU registers window.

A module employing one or more anti-disassembly tricks is usually known as obfuscated code. Other tricks that don't fall under the anti-disassembly category can still further obfuscate the program. These tricks include "dead" code: useless instructions that don't affect the algorithm or output of the program. Another trick is constant unfolding: using more instructions than necessary to assign or adjust a value by a constant. An example of this was shown near the entry point of this challenge where 3 instructions were used to assign a constant value to a memory location, rather than a single MOV instruction.

When manually stepping through obfuscated code in the debugger, or even code that is difficult to understand due to an optimizing compiler or any number of other reasons, I recommend making a "map" of the disassembly. This will serve as a makeshift static analysis worksheet since traditional static analysis is probably not an option. If there is a specialized tool designed for the specific obfuscation method in a module you are analyzing, and you have access to such a tool, use it by all means.

When I make a map, I open up a text editor like notepad. In the debugger, when I arrive at the beginning of the code I'm trying to understand, I copy and paste the disassembly for the current code block. When a branch instruction (JMP, Jxx, CALL, etc.) jumps to another code block I make a little separator line below the current block (such as '---') and paste a copy of the disassembly for the new block below it. Continue doing this until you have all of the blocks for the code being analyzed. If the branch is unconditional but made to look conditional, I add a comment stating that fact. I also comment CALL's that never return or RET's used only to jump instead of return from a subroutine. When branching instructions jump "between" instructions as described earlier, no matter how close the new block of code is to the one performing the jump, you still treat it as a separate block the same as any other block. Also when jumping "between" instructions, since the debugger's disassembly of previous instructions will disappear as the disassembler re-synchronizes, you can easily look backwards by referring to your map. When making your first pass, I recommend focusing primarily on building the different blocks of code as a result of the branching instructions. For code that jumps several times between high and low addresses as a layer of obfuscation, your map automatically removes this layer as it naturally represents the order of execution. Because the map will contain memory addresses, you can quickly navigate to any spot in your code and easily refer to the address for setting debugger breakpoints. At this point you can make as many passes as necessary to add comments, manually fold constants, turn conditional jumps that are in fact unconditional into plain JMPs, remove boundaries between adjacent blocks that are related (such as when code jumps around multiple consecutive times effectively doing nothing but trying to be confusing, you remove the series of jumps), and any other algorithm distilling methods you deem necessary such as converting assembly into pseudocode. Keep refining until you have a grasp for what is going on.

One final anti-disassembly trick employed by this challenge is building "other code" directly on the stack and executing them to further obscure the "sensitive" key functionality. This is anti-disassembly trick #4. No static analysis tool I know of supports displaying the dynamic code built on the stack, but the map you build will (I like to annotate this fact). Unless the code is self-modifying, the map will help reduce the complexity of understanding the dynamic stack-built code.

Following the basic principles described above, below is a basic map from the the program's entry point up to the ReadFile() API call where user input is gathered. This map only connects one block of code to the next and has very little distillation going on. You can think of it as only pass #1. We won't distill this code further because it is only getting us from the entry point to the ReadFile() call, and whatever obfuscated steps it does, has nothing to do with the password algorithm. It is included here for educational purposes as a real example of a map.

00401C48 E9 32F8FFFF JMP 0040147F ;entry point 0040147F 83C4 FC ADD ESP, -4 00401482 C70424 010000 MOV DWORD PTR SS:[ESP], 1 00401489 C10424 16 ROL DWORD PTR SS:[ESP], 16 0040148D 812C24 6FEFFF SUB DWORD PTR SS:[ESP], -1091 00401494 C3 RETN ;jumps to 401091 00401091 31C0 XOR EAX, EAX 00401093 74 01 JE SHORT 00401096 ;always taken 00401096 89C2 MOV EDX, EAX 00401098 74 09 JE SHORT 004010A3 ;always 004010A3 E8 F9FFFFFF CALL 004010A1 ;never returns 004010A1 EB 06 JMP SHORT 004010A9 004010A9 89C3 MOV EBX, EAX 004010AB 31EB XOR EBX, EBP 004010AD 891C04 MOV DWORD PTR SS:[EAX+ESP], EBX 004010B0 85E4 TEST ESP, ESP 004010B2 75 01 JNE SHORT 004010B5 ;always taken 004010B5 89C1 MOV ECX, EAX 004010B7 87E5 XCHG EBP, ESP ;ESP originally has 12FFC0 004010B9 89C4 MOV ESP, EAX ;ESP blown away here 004010BB F7D4 NOT ESP ; and here 004010BD 0F44E9 CMOVE EBP, ECX 004010C0 0F45E1 CMOVNE ESP, ECX 004010C3 09EC OR ESP, EBP ;ESP gets back its original value of 12FFC0 004010C5 75 02 JNE SHORT 004010C9 ;always taken 004010C9 BE 01000000 MOV ESI, 1 004010CE 31FF XOR EDI, EDI 004010D0 74 01 JE SHORT 004010D3 ;always taken 004010D3 F7D7 NOT EDI 004010D5 B9 06000000 MOV ECX, 6 004010DA 52 PUSH EDX 004010DB EB FF JMP SHORT 004010DC 004010DC FFC0 INC EAX 004010DE 48 DEC EAX 004010DF 58 POP EAX 004010E0 85C0 TEST EAX, EAX 004010E2 75 03 JNE SHORT 004010E7 ;always jump 004010E4 74 01 JE SHORT 004010E7 ; with one or the other 004010E7 0F45C2 CMOVNE EAX, EDX 004010EA 85FC TEST ESP, EDI 004010EC 50 PUSH EAX 004010ED E2 09 LOOP SHORT 004010F8 ;used as an unconditional jump, decrementing ecx from 6 to 5 004010F8 91 XCHG EAX, ECX 004010F9 0F45CE CMOVNE ECX, ESI 004010FC C1C1 16 ROL ECX, 16 004010FF 81C1 F2100000 ADD ECX, 10F2 ;ecx appears to be loaded with a code address 00401105 75 01 JNE SHORT 00401108 ;always taken 00401108 51 PUSH ECX ;code address 4010F2 stored on stack 00401109 91 XCHG EAX, ECX 0040110A EB FF JMP SHORT 0040110B 0040110B FFC0 INC EAX 0040110D 8B0414 MOV EAX, DWORD PTR SS:[EDX+ESP] 00401110 E8 BE0A0000 CALL 00401BD3 ;never returns

The last instruction above jumps into the repeating portion below (401BD3) that makes use of the LOOP instruction. When dealing with obfuscated code, sometimes the LOOP instruction is used as an unconditional jump, but this is not the case here. The loop executes 6 times (the initial value of ECX). When you step through the code and confirm a LOOP instruction is actually performing a loop (that you aren't interested in), set a breakpoint on the loop exit so you don't waste your time. The map helps you recognize places you've already been to, such as a giant loop that you might otherwise step through for hours.

00401BD3 50 PUSH EAX 00401BD4 66:B8 EB05 MOV AX, 5EB 00401BD8 31C0 XOR EAX, EAX 00401BDA 74 FA JE SHORT 00401BD6 ;always taken 00401BD6 EB 05 JMP SHORT 00401BDD 00401BDD 8B4424 08 MOV EAX, DWORD PTR SS:[ESP+8] 00401BE1 74 01 JE SHORT 00401BE4 ;always taken 00401BE4 894424 04 MOV DWORD PTR SS:[ESP+4], EAX 00401BE8 58 POP EAX 00401BE9 C2 0400 RETN 4 ;jumps to 4010F2 004010F2 E8 E4FFFFFF CALL 004010DB ;never returns 004010DB EB FF JMP SHORT 004010DC 004010DC FFC0 INC EAX 004010DE 48 DEC EAX 004010DF 58 POP EAX 004010E0 85C0 TEST EAX, EAX 004010E2 75 03 JNE SHORT 004010E7 ;always jump 004010E4 74 01 JE SHORT 004010E7 ; with one or the other 004010E7 0F45C2 CMOVNE EAX, EDX 004010EA 85FC TEST ESP, EDI 004010EC 50 PUSH EAX 004010ED E2 09 LOOP SHORT 004010F8 ;loop control - we loop 6 times until ECX becomes zero! 004010EF E3 33 JECXZ SHORT 00401124 ;always taken; loop exits here 004010F8 91 XCHG EAX, ECX 004010F9 0F45CE CMOVNE ECX, ESI 004010FC C1C1 16 ROL ECX, 16 004010FF 81C1 F2100000 ADD ECX, 10F2 00401105 75 01 JNE SHORT 00401108 ;always taken 00401108 51 PUSH ECX 00401109 91 XCHG EAX, ECX 0040110A EB FF JMP SHORT 0040110B 0040110B FFC0 INC EAX 0040110D 8B0414 MOV EAX, DWORD PTR SS:[EDX+ESP] 00401110 E8 BE0A0000 CALL 00401BD3 ;bottom of loop

Now that we're out of that loop, the remainder of the code bounces around until you make it to the eventual ReadFile() API call. You'll notice in the map comments that the system API calls were manually-built with code as opposed to using a traditional import to make it harder to spot where and which APIs are being used.

00401124 66:B8 EB05 MOV AX, 5EB 00401128 31C0 XOR EAX, EAX 0040112A 74 FA JE SHORT 00401126 ;always taken 00401126 EB 05 JMP SHORT 0040112D 0040112D 31D2 XOR EDX, EDX 0040112F 74 02 JE SHORT 00401133 00401131 83C4 42 ADD ESP, 42 00401134 81C4 E0FEFFFF ADD ESP, -120 0040113A 85E4 TEST ESP, ESP 0040113C 74 14 JE SHORT 00401152 ;never taken 0040113E 89D7 MOV EDI, EDX 00401140 C1E7 16 SHL EDI, 16 00401143 B9 28000000 MOV ECX, 28 00401148 89C8 MOV EAX, ECX ;get pointer to 0040114A C1E0 02 SHL EAX, 2 ; next stack 0040114D 01E0 ADD EAX, ESP ; location 0040114F 83E8 A8 SUB EAX, -58 ; 00401152 8910 MOV DWORD PTR DS:[EAX], EDX ;init current 00401154 F710 NOT DWORD PTR DS:[EAX] ; stack location 00401156 FF00 INC DWORD PTR DS:[EAX] ; with module base (i.e. 0x400000) 00401158 2138 AND DWORD PTR DS:[EAX], EDI ; copied in roundabout way from EDI 0040115A 74 03 JE SHORT 0040115F ;one or the other will 0040115C 75 01 JNE SHORT 0040115F ; always jump 0040115F E2 E7 LOOP SHORT 00401148 ;loops 28 times back to a couple lines above 00401161 89BC24 D40000 MOV DWORD PTR SS:[ESP+0D4], EDI 00401168 31C0 XOR EAX, EAX 0040116A 74 01 JE SHORT 0040116D ;always taken 0040116D 898424 080100 MOV DWORD PTR SS:[ARG.66], EAX 00401174 8D45 F8 LEA EAX, [EBP-8] 00401177 8D5C24 F0 LEA EBX, [LOCAL.3] 0040117B C703 89842410 MOV DWORD PTR DS:[EBX], 10248489 00401181 C74424 F4 010 MOV DWORD PTR SS:[LOCAL.2], C3000001 ; ASCII "NDX(" 00401189 895C24 F8 MOV DWORD PTR SS:[LOCAL.1], EBX 0040118D C74424 FC 9D1 MOV DWORD PTR SS:[LOCAL.0], 119D 00401195 017C24 FC ADD DWORD PTR SS:[LOCAL.0], EDI ;adds PE base 400000 to 119D to get 40119D 00401199 83EC 08 SUB ESP, 8 0040119C C3 RETN ;jumps to code on stack at 12FE78, xfering control to next instruction 0012FE78 898424 10010000 MOV DWORD PTR SS:[ESP+110], EAX ;this code is on the stack 0012FE7F C3 RETN ;jumps to 40119D 0040119D 81B424 080100 XOR DWORD PTR SS:[ESP+108], 00004000 004011A8 818424 C00000 ADD DWORD PTR SS:[ESP+0C0], 1736 004011B3 81AC24 B80000 SUB DWORD PTR SS:[ESP+0B8], -1736 004011BE EB FF JMP SHORT 004011BF 004011BF FFC0 INC EAX 004011C1 89F8 MOV EAX, EDI 004011C3 0D 68200000 OR EAX, 00002068 004011C8 8B00 MOV EAX, DWORD PTR DS:[EAX] ;EAX contains the address of WriteFile() 004011CA 838C24 FC0000 OR DWORD PTR SS:[ESP+0FC], FFFFFFFF 004011D2 C74424 FC 010 MOV DWORD PTR SS:[ESP-4], 1 004011DA C74424 F0 898 MOV DWORD PTR SS:[ESP-10], 8248489 004011E2 C16424 FC 16 SHL DWORD PTR SS:[ESP-4], 16 004011E7 75 02 JNE SHORT 004011EB ;always taken 004011EB C74424 F4 010 MOV DWORD PTR SS:[ESP-0C], C3000001 ; ASCII "NDX(" 004011F3 818424 D40000 ADD DWORD PTR SS:[ESP+0D4], 1736 004011FE 895C24 F8 MOV DWORD PTR SS:[ESP-8], EBX 00401202 814424 FC 0E1 ADD DWORD PTR SS:[ESP-4], 120E 0040120A 83C4 F8 ADD ESP, -8 0040120D C3 RETN ;jumps to stack at 12FE78 0012FE78 898424 08010000 MOV DWORD PTR SS:[ESP+108], EAX ;this code is on the stack 0012FE7F C3 RETN ;jumps to 40120E 0040120E 89BC24 000100 MOV DWORD PTR SS:[ESP+100], EDI 00401215 21BC24 FC0000 AND DWORD PTR SS:[ESP+0FC], EDI 0040121C 66:B8 EB05 MOV AX, 5EB 00401220 31C0 XOR EAX, EAX 00401222 74 FA JE SHORT 0040121E ;always taken 0040121E EB 05 JMP SHORT 00401225 00401225 C18424 080100 ROL DWORD PTR SS:[ESP+108], 8 0040122D C74424 3C 000 MOV DWORD PTR SS:[ESP+3C], 0 00401235 818424 F80000 ADD DWORD PTR SS:[ESP+0F8], 1736 00401240 818C24 F40000 OR DWORD PTR SS:[ESP+0F4], 00001736 0040124B 31C0 XOR EAX, EAX 0040124D 75 C7 JNE SHORT 00401216 0040124F 894424 30 MOV DWORD PTR SS:[ESP+30], EAX 00401253 818424 EC0000 ADD DWORD PTR SS:[ESP+0EC], 1736 0040125E 81AC24 E80000 SUB DWORD PTR SS:[ESP+0E8], -1736 00401269 EB FF JMP SHORT 0040126A 0040126A FFC0 INC EAX 0040126C 48 DEC EAX 0040126D 814C24 6C 361 OR DWORD PTR SS:[ESP+6C], 00001736 00401275 818424 E40000 ADD DWORD PTR SS:[ESP+0E4], 1736 00401280 C74424 4C 040 MOV DWORD PTR SS:[ESP+4C], 4 00401288 81AC24 DC0000 SUB DWORD PTR SS:[ESP+0DC], -1736 00401293 818C24 D80000 OR DWORD PTR SS:[ESP+0D8], 00001736 0040129E 818C24 FC0000 OR DWORD PTR SS:[ESP+0FC], 00001C27 004012A9 89F8 MOV EAX, EDI 004012AB 74 03 JE SHORT 004012B0 ;one or the other always 004012AD 75 01 JNE SHORT 004012B0 ; results in a jump 004012B0 0D 5C200000 OR EAX, 0000205C 004012B5 8B00 MOV EAX, DWORD PTR DS:[EAX] ;EAX now contains address to GetStdHandle() 004012B7 890424 MOV DWORD PTR SS:[ESP], EAX 004012BA 81AC24 D00000 SUB DWORD PTR SS:[ESP+0D0], -1736 004012C5 818424 C80000 ADD DWORD PTR SS:[ESP+0C8], 1736 004012D0 818C24 B40000 OR DWORD PTR SS:[ESP+0B4], 00001736 004012DB FF4424 30 INC DWORD PTR SS:[ESP+30] 004012DF 75 01 JNE SHORT 004012E2 ;always taken 004012E2 C14C24 30 0A ROR DWORD PTR SS:[ESP+30], 0A 004012E7 818424 B00000 ADD DWORD PTR SS:[ESP+0B0], 1736 004012F2 C78424 1C0100 MOV DWORD PTR SS:[ESP+11C], 0 004012FD 81AC24 940000 SUB DWORD PTR SS:[ESP+94], -1736 00401308 818C24 AC0000 OR DWORD PTR SS:[ESP+0AC], 00001736 00401313 75 01 JNE SHORT 00401316 ;always taken 00401316 C14424 4C 14 ROL DWORD PTR SS:[ESP+4C], 14 0040131B 818424 A40000 ADD DWORD PTR SS:[ESP+0A4], 1736 00401326 81AC24 000100 SUB DWORD PTR SS:[ESP+100], -1C02 00401331 EB FF JMP SHORT 00401332 00401332 FFC0 INC EAX 00401334 EB FF JMP SHORT 00401335 00401335 FFC0 INC EAX 00401337 48 DEC EAX 00401338 EB FF JMP SHORT 00401339 00401339 FFC0 INC EAX 0040133B EB FF JMP SHORT 0040133C 0040133C FFC0 INC EAX 0040133E 48 DEC EAX 0040133F 81B424 080100 XOR DWORD PTR SS:[ESP+108], 0000172D 0040134A 8D45 FC LEA EAX, [EBP-4] 0040134D 75 01 JNE SHORT 00401350 ;always taken 00401350 8D5C24 F0 LEA EBX, [LOCAL.3] 00401354 C703 8984243C MOV DWORD PTR DS:[EBX], 3C248489 0040135A C74424 F4 000 MOV DWORD PTR SS:[LOCAL.2], C3000000 ; ASCII "INDX(" 00401362 895C24 F8 MOV DWORD PTR SS:[LOCAL.1], EBX 00401366 897C24 FC MOV DWORD PTR SS:[LOCAL.0], EDI 0040136A 814C24 FC 761 OR DWORD PTR SS:[LOCAL.0], 00001376 00401372 83C4 F8 ADD ESP, -8 00401375 C3 RETN ;jumps to code on stack at 12FE80 0012FE78 898424 3C000000 MOV DWORD PTR SS:[ESP+3C], EAX ;this code is on the stack 0012FE7F C3 RETN ;jumps to 401376 00401376 818424 980000 ADD DWORD PTR SS:[ESP+98], 1736 00401381 818C24 A80000 OR DWORD PTR SS:[ESP+0A8], 00001736 0040138C 75 01 JNE SHORT 0040138F ;always taken 0040138F 81AC24 900000 SUB DWORD PTR SS:[ESP+90], -1736 0040139A C78424 100100 MOV DWORD PTR SS:[ESP+110], 0 004013A5 81AC24 8C0000 SUB DWORD PTR SS:[ESP+8C], -1736 004013B0 818424 880000 ADD DWORD PTR SS:[ESP+88], 1736 004013BB 66:B8 EB06 MOV AX, 6EB 004013BF 31C0 XOR EAX, EAX 004013C1 74 FA JE SHORT 004013BD 004013BD EB 06 JMP SHORT 004013C5 004013C5 8D45 FC LEA EAX, [EBP-4] 004013C8 898424 180100 MOV DWORD PTR SS:[ESP+118], EAX 004013CF 818C24 840000 OR DWORD PTR SS:[ESP+84], 00001736 004013DA 818424 800000 ADD DWORD PTR SS:[ESP+80], 1736 004013E5 89F8 MOV EAX, EDI 004013E7 0D 5C200000 OR EAX, 0000205C 004013EC 8B00 MOV EAX, DWORD PTR DS:[EAX] 004013EE 8D5C24 F0 LEA EBX, [ESP-10] 004013F2 897C24 FC MOV DWORD PTR SS:[ESP-4], EDI 004013F6 C703 89842410 MOV DWORD PTR DS:[EBX], 10248489 004013FC C74424 F4 000 MOV DWORD PTR SS:[ESP-0C], C3000000 ; ASCII "INDX(" 00401404 895C24 F8 MOV DWORD PTR SS:[ESP-8], EBX 00401408 814424 FC 141 ADD DWORD PTR SS:[ESP-4], 1414 00401410 83EC 08 SUB ESP, 8 00401413 C3 RETN ;jumps to stack at 12FE78 0012FE78 898424 10000000 MOV DWORD PTR SS:[ESP+10], EAX ;this code is on the stack 0012FE7F C3 RETN ;jumps to 401414 00401414 897C24 44 MOV DWORD PTR SS:[ESP+44], EDI 00401418 814C24 78 361 OR DWORD PTR SS:[ESP+78], 00001736 00401420 81AC24 F00000 SUB DWORD PTR SS:[ESP+0F0], -1736 0040142B 66:B8 EB05 MOV AX, 5EB 0040142F 31C0 XOR EAX, EAX 00401431 74 FA JE SHORT 0040142D ;always taken 0040142D EB 05 JMP SHORT 00401434 00401434 814424 74 361 ADD DWORD PTR SS:[ESP+74], 1736 0040143C C78424 140100 MOV DWORD PTR SS:[ESP+114], 11 00401447 814424 30 0B2 ADD DWORD PTR SS:[ESP+30], 210B ;stack has pointer to 40210B string "I have evolved since..." 0040144F 814C24 70 361 OR DWORD PTR SS:[ESP+70], 00001736 00401457 816C24 68 CAE SUB DWORD PTR SS:[ESP+68], -1736 0040145F 814424 64 361 ADD DWORD PTR SS:[ESP+64], 1736 00401467 814C24 5C 361 OR DWORD PTR SS:[ESP+5C], 00001736 0040146F 75 01 JNE SHORT 00401472 ;always taken 00401472 C74424 58 000 MOV DWORD PTR SS:[ESP+58], 0 0040147A E9 F3000000 JMP 00401572 00401572 897C24 28 MOV DWORD PTR SS:[ESP+28], EDI 00401576 8D45 FC LEA EAX, [EBP-4] 00401579 894424 54 MOV DWORD PTR SS:[ESP+54], EAX 0040157D 817424 4C 862 XOR DWORD PTR SS:[ESP+4C], 00002186 00401585 81AC24 CC0000 SUB DWORD PTR SS:[ESP+0CC], -1736 00401590 897C24 04 MOV DWORD PTR SS:[ESP+4], EDI 00401594 C74424 50 320 MOV DWORD PTR SS:[ESP+50], 32 0040159C 818C24 BC0000 OR DWORD PTR SS:[ESP+0BC], 00001736 004015A7 8D45 F4 LEA EAX, [EBP-0C] 004015AA 8D5C24 F0 LEA EBX, [ESP-10] 004015AE C703 8984244C MOV DWORD PTR DS:[EBX], 4C248489 004015B4 897C24 FC MOV DWORD PTR SS:[ESP-4], EDI 004015B8 75 04 JNE SHORT 004015BE ;we will jump one 004015BA 74 02 JE SHORT 004015BE ; way or the other 004015BE C74424 F4 000 MOV DWORD PTR SS:[ESP-0C], C3000000 ; ASCII "INDX(" 004015C6 895C24 F8 MOV DWORD PTR SS:[ESP-8], EBX 004015CA 816C24 FC 26E SUB DWORD PTR SS:[ESP-4], -15DA 004015D2 75 02 JNE SHORT 004015D6 ;always taken 004015D6 83EC 08 SUB ESP, 8 004015D9 C3 RETN ;jumps to stack at 12FE78 0012FE78 898424 4C000000 MOV DWORD PTR SS:[ESP+4C], EAX ;this is code on the stack 0012FE7F C3 RETN ;jumps to 4015DA 004015DA 816C24 60 CAE SUB DWORD PTR SS:[ESP+60], -1736 004015E2 817424 44 361 XOR DWORD PTR SS:[ESP+44], 00001736 004015EA 75 01 JNE SHORT 004015ED ;always taken 004015ED 818424 9C0000 ADD DWORD PTR SS:[ESP+9C], 1736 004015F8 89F8 MOV EAX, EDI 004015FA 0D 6C200000 OR EAX, 0000206C 004015FF 8B00 MOV EAX, DWORD PTR DS:[EAX] ;EAX now has address of ReadFile() 00401601 894424 40 MOV DWORD PTR SS:[ESP+40], EAX 00401605 C74424 34 570 MOV DWORD PTR SS:[ESP+34], 57 0040160D 81AC24 C40000 SUB DWORD PTR SS:[ESP+0C4], -1736 00401618 EB FF JMP SHORT 00401619 00401619 FFC0 INC EAX 0040161B 75 01 JNE SHORT 0040161E ;always taken 0040161E 818C24 A00000 OR DWORD PTR SS:[ESP+0A0], 00001736 00401629 897C24 14 MOV DWORD PTR SS:[ESP+14], EDI 0040162D 66:B8 EB05 MOV AX, 5EB 00401631 31C0 XOR EAX, EAX 00401633 74 FA JE SHORT 0040162F ;always taken 0040162F EB 05 JMP SHORT 00401636 00401636 8D45 F8 LEA EAX, [EBP-8] 00401639 8D5C24 F0 LEA EBX, [ESP-10] 0040163D 85DB TEST EBX, EBX 0040163F 75 02 JNE SHORT 00401643 00401643 C703 89842430 MOV DWORD PTR DS:[EBX], 30248489 00401649 C74424 F4 000 MOV DWORD PTR SS:[ESP-0C], C3000000 ; ASCII "INDX(" 00401651 895C24 F8 MOV DWORD PTR SS:[ESP-8], EBX 00401655 C74424 FC 020 MOV DWORD PTR SS:[ESP-4], 2 0040165D C16424 FC 15 SHL DWORD PTR SS:[ESP-4], 15 00401662 75 01 JNE SHORT 00401665 ;always taken 00401665 814C24 FC 711 OR DWORD PTR SS:[ESP-4], 00001671 0040166D 83EC 08 SUB ESP, 8 00401670 C3 RETN /jumps on stack at 12FE78 0012FE78 898424 30000000 MOV DWORD PTR SS:[ESP+30], EAX ;this is code on stack 0012FE7F C3 RETN ;jumps to 491671 00401671 817424 28 021 XOR DWORD PTR SS:[ESP+28], 00001C02 00401679 EB FF JMP SHORT 0040167A 0040167A FFC0 INC EAX 0040167C 89F8 MOV EAX, EDI 0040167E 0D 68200000 OR EAX, 00002068 00401683 8B00 MOV EAX, DWORD PTR DS:[EAX] ;EAX now points to WriteFile() 00401685 894424 24 MOV DWORD PTR SS:[ESP+24], EAX 00401689 66:B8 EB06 MOV AX, 6EB 0040168D 31C0 XOR EAX, EAX 0040168F 74 FA JE SHORT 0040168B ;always taken 0040168B EB 06 JMP SHORT 00401693 00401693 8D45 F8 LEA EAX, [EBP-8] 00401696 894424 20 MOV DWORD PTR SS:[ESP+20], EAX 0040169A C74424 18 F5F MOV DWORD PTR SS:[ESP+18], -0B 004016A2 818424 E00000 ADD DWORD PTR SS:[ESP+0E0], 1736 004016AD 816C24 14 14E SUB DWORD PTR SS:[ESP+14], -1BEC 004016B5 66:B8 EB05 MOV AX, 5EB 004016B9 31C0 XOR EAX, EAX 004016BB 74 FA JE SHORT 004016B7 ;always taken 004016B7 EB 05 JMP SHORT 004016BE 004016BE 74 02 JE SHORT 004016C2 ;always taken 004016C2 74 03 JE SHORT 004016C7 004016C7 8D45 F4 LEA EAX, [EBP-0C] 004016CA 8D5C24 F0 LEA EBX, [ESP-10] 004016CE 74 04 JE SHORT 004016D4 ;we will always jump 004016D0 75 02 JNE SHORT 004016D4 ; to 4016D4 004016D4 C703 89442414 MOV DWORD PTR DS:[EBX], 14244489 004016DA C74424 F4 C30 MOV DWORD PTR SS:[ESP-0C], 0C3 004016E2 895C24 F8 MOV DWORD PTR SS:[ESP-8], EBX 004016E6 895424 FC MOV DWORD PTR SS:[ESP-4], EDX 004016EA C14424 FC 16 ROL DWORD PTR SS:[ESP-4], 16 004016EF 75 03 JNE SHORT 004016F4 ;we will always jump 004016F1 74 01 JE SHORT 004016F4 ; to 4016F4 004016F4 814424 FC 001 ADD DWORD PTR SS:[ESP-4], 1700 004016FC 83C4 F8 ADD ESP, -8 004016FF C3 RETN ;jumps to stack at 12FE78 0012FE78 894424 14 MOV DWORD PTR SS:[ESP+14], EAX ;this is code on stack 0012FE7C C3 RETN ;jumps to 401700 00401700 C74424 08 F6F MOV DWORD PTR SS:[ESP+8], -0A 00401708 895424 1C MOV DWORD PTR SS:[ESP+1C], EDX 0040170C C16424 1C 16 SHL DWORD PTR SS:[ESP+1C], 16 00401711 75 01 JNE SHORT 00401714 ;always taken 00401714 814424 1C 021 ADD DWORD PTR SS:[ESP+1C], 1C02 0040171C 814C24 04 EC1 OR DWORD PTR SS:[ESP+4], 00001BEC 00401724 816C24 7C CAE SUB DWORD PTR SS:[ESP+7C], -1736 0040172C C3 RETN ;jumps to GetStdHandle(), and that API returns to 401BEC 00401BEC 53 PUSH EBX 00401BED 75 04 JNE SHORT 00401BF3 ;nomatter what we 00401BEF 74 02 JE SHORT 00401BF3 ; jump to 401BF3 00401BF3 8B5C24 08 MOV EBX, DWORD PTR SS:[ESP+8] 00401BF7 8903 MOV DWORD PTR DS:[EBX], EAX 00401BF9 5B POP EBX 00401BFA 74 03 JE SHORT 00401BFF ;nomatter what 00401BFC 75 01 JNE SHORT 00401BFF ; we jump to 401BFF 00401BFF C2 0400 RETN 4 ;jumps to GetStdHandle, the API returns to 401BEC 00401BEC 53 PUSH EBX 00401BED 75 04 JNE SHORT 00401BF3 ;nomatter what 00401BEF 74 02 JE SHORT 00401BF3 ; we jump to 401BF3 00401BF3 8B5C24 08 MOV EBX, DWORD PTR SS:[ESP+8] 00401BF7 8903 MOV DWORD PTR DS:[EBX], EAX 00401BF9 5B POP EBX 00401BFA 74 03 JE SHORT 00401BFF ;nomatter what 00401BFC 75 01 JNE SHORT 00401BFF ; we jump 401BFF 00401BFF C2 0400 RETN 4 ;jumps to 401C02, which drops to next line 00401C02 53 PUSH EBX 00401C03 74 03 JE SHORT 00401C08 ;nomatter what 00401C05 75 01 JNE SHORT 00401C08 ; we jump to 401C08 00401C08 8B5C24 0C MOV EBX, DWORD PTR SS:[ESP+0C] 00401C0C 8B1B MOV EBX, DWORD PTR DS:[EBX] 00401C0E 895C24 0C MOV DWORD PTR SS:[ESP+0C], EBX 00401C12 75 03 JNE SHORT 00401C17 ;nomatter what 00401C14 74 01 JE SHORT 00401C17 ; we jump to 401C17 00401C17 5B POP EBX 00401C18 C3 RETN ;jumps to WriteFile which returns to 401C02 (first output to console is "I have evolved...") 00401C02 53 PUSH EBX 00401C03 74 03 JE SHORT 00401C08 ;nomatter what 00401C05 75 01 JNE SHORT 00401C08 ; we jump to 401C08 00401C08 8B5C24 0C MOV EBX, DWORD PTR SS:[ESP+0C] 00401C0C 8B1B MOV EBX, DWORD PTR DS:[EBX] 00401C0E 895C24 0C MOV DWORD PTR SS:[ESP+0C], EBX 00401C12 75 03 JNE SHORT 00401C17 ;nomatter what 00401C14 74 01 JE SHORT 00401C17 ; we jump to 401C17 00401C17 5B POP EBX 00401C18 C3 RETN ;jumps to ReadFile which returns to 401736

The code above doesn't need to be analyzed too much because we were simply using it to understand making a map of obfuscated code. To skip directly to the ReadFile() API call, as this is usually "nearer" to the validation code we need to understand, we need to set an API breakpoint. In OllyDbg, go to the "View" menu -> "Executable modules" (or ALT-e). In the modules window, right click on the DLL that contains the desired API call. In this case, ReadFile() is in kernel32, so right-click kernel32 -> "Show names" (or CTRL+n). Sort the column by name (click the header button) and/or begin typing "readfile". When the ReadFile export is selected, double-click it or hit <ENTER> to go to the function's prologue. Set your breakpoint here and run the program. Once hit, step-over instructions until you reach ReadConsoleA, where you must switch back to the debugged-console and enter the password before the debugger regains control. Continue stepping-over until you exit ReadFile() via the "RETN 14".

Now we want to set a breakpoint on whatever code starts reading your input (to skip past whatever bouncing around may happen between here and there). At this point you'll be back in the challenge module at 401736. Locate your "user input" data by scrolling the stack up a bit until you see you it (you did type something that will stand out, right?) Right click on this stack location and "follow" the pointer into the memory dump window. Right click on the first byte of your input and set a read breakpoint. Now run the program to the breakpoint. You should find yourself near the top of the algorithm loop at 401A9C. As explained above, you'll have to have OllyDbg unanalyze this code so you can view the instructions rather than the data representation.

Without including a larger map of the obfuscated code that comprises the password validation routine, I'll leave debugging that section of code as an exercise to the reader. Be patient with this one. I made 4 passes through the algorithm loop in the debugger before I had a clear picture of what was going on, taking and refining my notes with each pass. The code is intentionally complex so as to hide the relatively simple algorithm. The code employs all obfuscated techniques described thus far. It is a doable task if you just have some patience and expect that you might not understand all that is going on with the first few loop iterations. If you prefer not to make a map and solely use the debugger, use OllyDbg's "comment" feature to annotate instructions by pressing the ";" key. The comments will be kept across multiple debugging sessions, so when you see your comments re-appear, you'll know you are in a familiar part.

Early on in the loop, it is apparent a successful password is 41 characters. You'll soon find the "key/password" is divided amongst 4 different tables. The first table is what I called the offset table, indexed by the current character position by processing the user input from left to right. Once this offset retrieved for the current input character position, it is used as an index into each of the other 3 tables. The 2nd table contains XOR bytes, the 3rd shift bytes, and the 4th a "magic" DWORD value whose least-significant byte was the expected encrypted password character. The most difficult part for me was locating where the expected password characters were located, as the code performing this comparison was built on the fly and executed on the stack in a different position with each iteration of the loop. Making matters worse, the comparison avoided the use of CMP which would have stood out and instead used a "CMPXCHG BL, DL" instruction for an *implied* comparison between the destination operand and the AL register (where the expected password character was)! I missed this on multiple passes because the AL register wasn't listed as an operand and therefore wasn't on my "radar". This is why it is handy to have your Intel x86 assembly reference handy. On top of that, the result of the comparison that sent you to the "failure" branch was delayed until the loop body executed the number of times of the length of the expected password (regardless of the user input size)! If you didn't type in a full 41 characters, 41 bytes of whatever artifacts were in the user input buffer were compared regardless.

As previously mentioned, the important parts dealing with the key were executed by code built on the stack by other code. Setting breakpoints on the stack aren't very useful in general because the stack is constantly growing and shrinking causing those breakpoints to be constantly triggered by other code. This is why making a map is useful; the location of the currently executing code on the stack isn't important so much as the instructions, because they are mostly the same, just accessing different indexes into the same buffers. In the map snippet below, the stack addresses reflect those of the first loop iteration for the encrypted password comparison code. An understanding of the algorithm isn't usually affected by changing stack addresses for stack code unless the generated code is drastically different between iterations. In this case, the code was always a "CMPXCHG BL,DL" followed by a RETN. After you do multiple passes through the loop you'll be able to identify the pattern with the code being placed on the stack to distill the important algorithm pieces. Here's the snippet from my map where the encrypted character comparison was built and executed on the stack:

00401B46 88C4 MOV AH, AL ;AH = AL - a copy of the encrypted (XORed and SHIFTED) input char 00401B48 96 XCHG EAX, ESI ;ESI=0040C3C3 EAX=1 00401B49 75 04 JNE SHORT 00401B4F 00401B4B 74 02 JE SHORT 00401B4F 00401B4F C74424 F4 0FB MOV DWORD PTR SS:[ESP-0C], C3D3B00F ;place "0F B0 D3 C3" opcodes on stack (i.e. cmpxchg bl, dl; ret) 00401B57 68 7115845C PUSH 5C841571 ;portion of unfolded constant (see below) 00401B5C 54 PUSH ESP ;push current ESP; will later point at opcodes 00401B5D 816C24 04 07F SUB DWORD PTR SS:[ESP+4], 5C43FA07 ;unfolded constant now 401B6A (jump addres after opcodes execute) 00401B65 832C24 08 SUB DWORD PTR SS:[ESP], 8 ;adjust previous ESP to point at opcodes 00401B69 C3 RETN ;execute opcodes on stack 0012FDF8 0FB0D3 CMPXCHG BL, DL ;ENCRYPTED CHAR COMPARISON running on stack; compare AL to BL; if equal, assign BL = DL 0012FDFB C3 RETN ;jump to 401B6A 00401B6A 0F44C2 CMOVE EAX, EDX ;idx=0 EAX=0 EDX=1 00401B6D 85C0 TEST EAX, EAX

NOTE: Despite the limited usefulness of stack breakpoints, that doesn't mean there aren't instances where they are useful. In this challenge I did in fact set a memory write breakpoint at a specific stack location to figure out which routine was responsible for placing the "You are failure" message to be picked up by WriteFile().

Once I had an understanding of the algorithm, I used breakpoints to manually gather the values from the XOR, SHIFT and MAGIC tables for each position of the password. I hardcoded these tables into a small C++ program to run the algorithm in reverse on the encrypted password (stored in the low byte of the "magic" table). The program source is shown below:

// // flare2015crackme9_algo() // void flare2015crackme9_algo(void) { byte arXor[] = { 0x46,0x15,0xF4,0xBD,0xFF,0x4C,0xEF,0x46,0xEB,0xE6,0xB2,0xEB,0xF1,0xC4,0x34,0x67, 0x39,0xb5,0x8e,0xef,0x40,0x1b,0x74,0x0d,0x60,0x26,0x45,0xa8,0x4a,0x96,0xc9,0x65, 0xe2,0x32,0x60,0x64,0x8c,0x65,0xe3,0x8e,0x9f }; byte arRot[] = { 0x56,0xF5,0xAC,0x1B,0xB5,0x93,0x7E,0xB8,0x23,0xDA,0x0A,0xF2,0x01,0x61,0x5C,0xC8, 0x4C,0xD6,0x16,0x55,0x67,0xB8,0xC1,0xF8,0xBC,0x11,0xFA,0x9B,0x6B,0xF9,0xD4,0x75, 0x87,0xCA,0xCE,0xBE,0x4E,0x6E,0xF1,0xB9,0x6E }; dword arMagic[] = { 0x064EF4C3,0xF080B8CC,0xB8CCC2BA,0xBC42064E,0x30565DF2,0x6B5A16EB,0xF4C35D27,0x66535319,0x8E0EC3C6, 0x5DF2BC42,0xF2BC4206,0xC66B5A16,0x2E30565D,0xD2665353,0xE101DC55,0x5D278E0E,0x03F9D266,0x42064EF4, 0x55509AF9,0x53192E30,0xDC55509A,0x509AF977,0x192E3056,0x0EC3C66B,0xF97748F0,0xC35D278E,0xEBE101DC, 0x5353192E,0x01DC5550,0x5A16EBE1,0xC3C66B5A,0x7748F080,0x9AF97748,0x4EF4C35D,0xF9D26653,0x80B8CCC2, 0x48F080B8,0xCF03F9D2,0x16EBE101,0x278E0EC3,0x565DF2BC }; char szTemp[100]; char* p = szTemp; const char* pEnd = p + MAX_ARRAY_ITEMS(szTemp); //loop throught the number of entries in the array for (uint i=0; i<MAX_ARRAY_ITEMS(arMagic); ++i) { //current encrypted key is low byte of magic number byte chCur = *(byte*)(arMagic+i); byte uRot = arRot[i]; byte uXor = arXor[i]; __asm { mov al,chCur //load encrypted char mov cl,uRot //load rotate value mov dl,uXor //load xor value ror al,cl //perform rotate xor al,dl //perform xor mov chCur,al //load result into variable } //store decrypted character if (p>=pEnd-1) break; else { *p++ = *((char*)&chCur); } } //for *p = 0; printf("result is \"%s\"\n",szTemp); } //flare2015crackme9_algo()

After building the program, we run it to produce the decrypted password. The result is then double-checked in the challenge program.

Flare-On 2015 Challenge #9 - Running password decryptor


The official solution confirmed my findings about the decoy function at 401000 (the default entry point used by Microsoft linkers) . I suspect the decoy resembles the original unobfuscated version of the program. I didn't catch the use of the NtGlobalFlag "debugger present" check that was added to throw off the results when the program was running in a debugger. This didn't affect me because I wasn't using the debugger to test password characters, such as with a brute force method. The author ultimately developed a Python script using the same algorithm as in my C++ decoder.

Response from Is_th1s_3v3n_mai_finul_foarm@flare-on.com:
Subject: FLARE-On Challenge #9 Completed! From: Is_th1s_3v3n_mai_finul_foarm@flare-on.com To: <HIDDEN> Date: Fri, 21 Aug 2015 03:15:52 -0400 When you inevitably take over the world, just remember that I was one of the nice ones and I absolutely did not try to break you with difficult reversing challenges. Don't give up, you are so close to the prize! The password is once again "flare" -FLARE attachment_filename="DC682778F53E853B3188AC63EB376D8B.zip"

<< Flare-On 2015 Index  --  Go on to Challenge #10 >>