Rating:
# XPacker
## 1. Quick Peek
We get 2 PE files `evil.exe` and `mypacker.exe`.
Looking at `evil.exe`, we can follow the `invoke_main()` function up to
```C
undefined8 FUN_1400117f0(void)
{
__CheckForDebuggerJustMyCode(&DAT_1400210f2);
MessageBoxW(NULL,
L"Hi there, are you having fun with this CTF?, I hope so :), and BTW I\'m just a container, nothing useful here",
L"Hello World",0);
return 1;
}
```
which doesnt do anything besides greeting us with a popup.
opening `mypacker.exe` and following `invoke_main` again, we can see it reads a 3rd PE file, XOR it and store it in a PE resource:
```C
handle = CreateFileW(L"C:\\Users\\OT\\source\\repos\\mypacker\\evil.exe",0x80000000,0,NULL,3,0x80,NULL);
if (handle == (HANDLE)0xffffffffffffffff) {
DVar1 = GetLastError();
thunk_FUN_140016760("Cannot obtain a valid handle %d",(ulonglong)DVar1,uVar9,uVar10);
}
uVar3 = 0;
size = GetFileSize(handle,NULL);
if (size == 0) {
thunk_FUN_140016760("Cannot calculate fileszie",uVar3,uVar9,uVar10);
}
uVar10 = 0x40;
uVar9 = 0x3000;
uVar4 = (ulonglong)size;
file_content = (byte *)VirtualAlloc(NULL,uVar4,0x3000,0x40);
if (file_content == NULL) {
thunk_FUN_140016760("allocation failed",uVar4,uVar9,uVar10);
}
uVar9 = 0;
uVar4 = (ulonglong)size;
pbVar5 = file_content;
BVar2 = ReadFile(handle,file_content,size,NULL,NULL);
if (BVar2 != 0) {
thunk_FUN_140016760("reading the content succesfully..",pbVar5,uVar4,uVar9);
}
ptr_file_content = file_content;
```
the encryption is here:
```C
for (i = 0; i < size; i = i + 1) {
pwVar6 = L"PHALANX";
puVar8 = key;
for (n = 0x10; n != 0; n = n + -1) {
*(undefined *)puVar8 = *(undefined *)pwVar6;
pwVar6 = (wchar_t *)((longlong)pwVar6 + 1);
puVar8 = (ushort *)((longlong)puVar8 + 1);
}
byte = *ptr_file_content;
/* xor */
if ((byte != 0) && (local_24 = (uint)byte, (uint)byte != (uint)key[(ulonglong)i % 6])) {
local_24 = (uint)byte;
*ptr_file_content = byte ^ (byte)key[(ulonglong)i % 6];
}
ptr_file_content = ptr_file_content + 1;
}
```
then write the resource:
```C
lpflOldProtect = local_114;
VirtualProtect(file_content,8,0x40,lpflOldProtect);
local_f0 = BeginUpdateResourceW(L"C:\\Users\\OT\\source\\repos\\mypacker\\IThinkThisWhatYouWant.exe",0);
if ((local_f0 == NULL) || (local_f0 == (HANDLE)0xffffffffffffffff)) {
uVar9 = 0x10000;
uVar4 = (ulonglong)size;
VirtualFree(file_content,uVar4,0x10000);
thunk_FUN_140016760("BeginUpdateResourceA fails.\n",uVar4,uVar9,lpflOldProtect);
}
else {
uVar9 = 0;
BVar2 = UpdateResourceW(local_f0,L"UNKNOWN",(LPCWSTR)0x45,0,file_content,size);
if (BVar2 == 0) {
uVar10 = 0x10000;
uVar4 = (ulonglong)size;
VirtualFree(file_content,uVar4,0x10000);
thunk_FUN_140016760("UpdateResourceW fails\n",uVar4,uVar10,uVar9);
}
else {
BVar2 = EndUpdateResourceW(local_f0,0);
if (BVar2 == 0) {
uVar10 = 0x10000;
uVar4 = (ulonglong)size;
VirtualFree(file_content,uVar4,0x10000);
thunk_FUN_140016760("EndUpdateResourceA fails\n",uVar4,uVar10,uVar9);
}
}
}
```
## 2. Unpacking
Indeed `evil.exe` contains an extra resource as shown by pestudio or here, binref:
```
% emit evil.exe| perc -l
UNKNOWN/69/0 <<< here
MANIFEST/1/1033
```
we can use a simple script to extract and decrypt the resource:
```python
import pefile
pe = pefile.PE("evil.exe")
#key = b'PHALANX'
key = b'PHALAN'
def get_resource(pe):
for rsrc_type in pe.DIRECTORY_ENTRY_RESOURCE.entries:
for rsrc in rsrc_type.directory.entries:
rsrc = rsrc.directory.entries[0].data.struct
return pe.get_data(rsrc.OffsetToData, rsrc.Size)
def decrypt(data, key):
out = bytearray(len(data))
for n, b in enumerate(data):
if data[n] == 0:
continue
if data[n] == key[n % len(key)]:
out[n] = data[n]
continue
out[n] = data[n] ^ key[n % len(key)]
return out
data = get_resource(pe)
yo = decrypt(data, key)
with open("dumped.pe", "wb") as fp:
fp.write(yo)
```
The key string is "PHALANX", but there's a hardcoded length of 6 in the packer code, so only the first 6 chrs are used.
we're left with another executable:
```
% file dumped.pe
dumped.pe: PE32+ executable (console) x86-64, for MS Windows, 10 sections
```
## 3. Dumped bin
getting to the `main`we can see we're on the right track:
```C
undefined8 main(void)
{
int iVar1;
__CheckForDebuggerJustMyCode(&DAT_1400230a2);
iVar1 = do_something();
if (iVar1 == 0) {
MessageBoxA(NULL,"BAD!, Try harder PLZ...","Info",0);
}
else {
MessageBoxA(NULL,"Correct UserName!, YOUR Flag will be FLAG{USERNAME}","Info",0);
}
return 0;
}
```
further down, it get the user the binary runs as:
```C
int do_something(void)
{
//
GetUserNameA(username,size);
check_username(username);
//
}
```
the meat is in check_username:
it initialize a 4 int key:
```C
tea_key[0] = 0x2134;
tea_key[1] = 0x2121;
tea_key[2] = 0x2123;
tea_key[3] = 0x65f2;
```
then intialize an array:
```C
/* encrypted username */
enc_username[0] = 0x6d;
enc_username[1] = 0xe4;
enc_username[2] = 0x9e;
enc_username[3] = 0xff;
enc_username[4] = 0x82;
enc_username[5] = 0xa6;
enc_username[6] = 0xe;
enc_username[7] = 0x82;
```
and then use TEA to encrypt the current username (`GetUserNameA`):
```C
for (j = 0; j < len; j = j + 8) {
TEA_encrypt((uint *)(username + (longlong)j * 4),tea_key);
}
```
at the end it checks whether the result of the encryption == the enc_username.
Ghidra fails to shows it, but IDA Free does it just fine:
```C
key[0] = 8500;
key[1] = 8481;
key[2] = 8483;
key[3] = 26098;
memset(&key[4], 0, 0x30ui64);
len = j_strlen(username);
if ( len % 8 )
len += 8 - len % 8;
enc_username = (char (*)[8])0x820EA682FF9EE46Di64;
ptr_username = username;
for ( j = 0; j < len; j += 8 )
TEA((unsigned int *)&ptr_username[4 * j], key, v5);
v13 = 0;
return (char)enc_username == *username; // HERE
}
```
TEA is easy to identify:
```C
void TEA_encrypt(uint *param_1,int *key)
{
uint v0;
uint v1;
uint sum;
uint n;
__CheckForDebuggerJustMyCode(&DAT_1400230a2);
v0 = *param_1;
v1 = param_1[1];
sum = 0;
for (n = 0; n < 0x20; n = n + 1) {
sum = sum + 0x9e3779b9;
v0 = v0 + (v1 * 0x10 + *key ^ v1 + sum ^ (v1 >> 5) + key[1]);
v1 = v1 + (v0 * 0x10 + key[2] ^ v0 + sum ^ (v0 >> 5) + key[3]);
}
*param_1 = v0;
param_1[1] = v1;
return;
}
```
just google the constant `0x9e3779b9` and you'll find it (you'll also find XTEA, but the algorithm is slightly different).
## 4. Solving
We know the key: `[0x2134, 0x2121, 0x2123, 0x65f2]`, we know the encrypted result `b'm\xe4\x9e\xff\x82\xa6\x0e\x82'`, we know the algorithm `TEA`, so we can just decrypt:
```python
from pwn import *
from ctypes import c_uint32
# https://gist.github.com/twheys/4e83567942172f8ba85058fae6bfeef5
def decipher(msg, k):
y = c_uint32(u32(msg[:4]))
z = c_uint32(u32(msg[4:8]))
sum_ = c_uint32(0xC6EF3720)
for x in range(32):
z.value -= (y.value << 4) + k[2] ^ y.value + sum_.value ^ (y.value >> 5) + k[3]
y.value -= (z.value << 4) + k[0] ^ z.value + sum_.value ^ (z.value >> 5) + k[1]
sum_.value -= 0x9e3779b9
return p32(y.value) + p32(z.value)
k = [0x2134, 0x2121, 0x2123, 0x65f2]
enc = b'm\xe4\x9e\xff\x82\xa6\x0e\x82'
print(decipher(enc, k))
```
and get the flag:
```
% python meh.py
b'RYouLost'
```