Problem: xctf
MFC program, use xspy to view window information.
The check button has an ID of 03e9, and the window has the function OnCommand: notifycode=0000 id=03e9,func= 0x00C72420(Junk_Instruction.exe+ 0x002420 )
.
Entering this function (sub_402420), you can see a conditional statement: if ( (unsigned __int8)sub_402600(v2 + 16) )
. The two branches are popping up the correct and incorrect dialogs, indicating that sub_402600
is the verification function.
This function contains a lot of junk instructions. It first uses a call to push the return address, then pops the return address from the stack, modifies it, and pushes it back. Snap, the return address changes.
.text:0040293F E8 00 00 00 00 call $+5
.text:0040293F
.text:00402944
.text:00402944 loc_402944: ; DATA XREF: sub_402600+398↓r
.text:00402944 58 pop eax
.text:00402945 89 85 C4 FD FF FF mov [ebp+var_23C], eax
.text:0040294B E8 03 00 00 00 call loc_402953
.text:0040294B
.text:0040294B ; ---------------------------------------------------------------------------
.text:00402950 EA db 0EAh
.text:00402951 ; ---------------------------------------------------------------------------
.text:00402951 EB 09 jmp short loc_40295C
.text:00402951
.text:00402953 ; ---------------------------------------------------------------------------
.text:00402953
.text:00402953 loc_402953: ; CODE XREF: sub_402600+34B↑j
.text:00402953 5B pop ebx
.text:00402954 43 inc ebx
.text:00402955 53 push ebx
.text:00402956 B8 11 11 11 11 mov eax, 11111111h
.text:0040295B C3 retn
.text:0040295C ; ---------------------------------------------------------------------------
.text:0040295C
.text:0040295C loc_40295C: ; CODE XREF: sub_402600+351↑j
.text:0040295C E8 07 00 00 00 call loc_402968
.text:0040295C
.text:00402961 BB 33 33 33 33 mov ebx, 33333333h
.text:00402966 EB 0D jmp short loc_402975
.text:00402966
.text:00402968 ; ---------------------------------------------------------------------------
.text:00402968
.text:00402968 loc_402968: ; CODE XREF: sub_402600:loc_40295C↑p
.text:00402968 BB 11 11 11 11 mov ebx, 11111111h
.text:0040296D 5B pop ebx
.text:0040296E BB 75 29 40 00 mov ebx, offset loc_402975
.text:00402973 53 push ebx
.text:00402974 C3 retn
These junk instructions do not create a complex branching structure. They simply jump to the next segment, so they can all be replaced with nop
instructions.
The functions 2AF0, 2CA0, and 2e80 are the same. After removing the obfuscation, it was found that their functions are to remove the flag and parentheses and reverse the string, initialize RC4, and encrypt with RC4. There is a loop below that compares the ciphertext for correctness.
The ciphertext is the long assignment starting with 2600 (5bD6D026C8DD197E6E3ECB16917DFFAFDD7664B0F7E58957829F0C009ED045FA
), and the password is qwertyuiop
.
Put it into CyberChef to get the result. Remember to reverse it. Remember to wrap it with flag{}.
PS:
At first, I was afraid to replace all the instructions with nop
because there were assignments to eax
and ebx
, and I was afraid they were important. As a result, I ended up with a bunch of jmp
instructions. IDA: 6. Later, I looked at the deobfuscation script from the expert and found that it directly replaced them with nop
instructions... That script is for Python 2, so I modified it to Python 3 to make it work.
from ida_bytes import get_bytes, patch_bytes
import re
addr = 0x402600
end = 0x402ae3
buf = get_bytes(addr, end-addr)
def handler1(s):
s = s.group(0)
print("".join(["%02x"%i for i in s]))
s = b"\x90"*len(s)
return s
p = b"\xe8\x00\x00\x00\x00.*?\xc3.*?\xc3"
buf = re.sub(p, handler1, buf, flags=re.I)
patch_bytes(addr, buf)
print("Done")
However, this matching seems to have some problems. The function for RC4 encryption will be replaced with nop
instructions multiple times, so the range written above is limited to sub_402600
only. (If you have already guessed that it is RC4 encryption, it won't have much impact).
I thought I knew MFC, but I was completely confused. Now I know about xspy.