脱壳
查壳
DIE是查不出来滴,使用Exeinfope查壳。
是个强壳,有点难搞。但是我们还是一个目标:找到OEP,给他dump下来。
只修改标志符是骗不了Themida的,在PEB中的BeingDebugged被设为True后,还会有一些其他的变化产生。
加载时会将PEB中的NtGlobalFlag中的一个位改变,使其值为0x70,而正常状态下不是
在WRK中有一个宏也会随着NtGlobalFlag的改变而在RtlCreateHeap中用RtlDebugCreateHeap创建调试堆。这个调试堆中含有大量的标志(例如0xBAAD0F0D和0xFEEEFEEE等),而正常情况下这个地址中却没有有意义的数据。
单纯的修改BeingDebugged标志位而忘记清除其他痕迹时,经常会被有所防备的壳、反调等程序捕捉到,例如Themida
脱壳
0x01 使用scyllahide(x32dbg)
选择Themida,插件能直接过掉反调试。
找到OEP,用Scylla直接dump下来,Fix Dump。
但是有部分IAT表未修复。
IDA解析后会出现__24等怪异的函数
0x02 使用SharpOD(Ollydbg/x32dbg)
记得勾上Protect Drx
若如果想彻底恢复IAT表,可以看P.Z的Blog
开始逆向分析
第一段输入
一开始输入长度为352位的shellcode
后面的函数进入查看,发现是base64的解码函数(8位变6位)
左移3位,后面右移三位还原即可
XXTEA小魔改(z>>6),key值已知,熟悉XXTEA的都知道,0x61C88647值是特征值(0x9e3779b)的取反值,所以此处是-delta。
密文也已知
直接提取66个Dw(352/4*3/4)
第二段输入
现在明白为什么题目叫shellcode了。
我们先写脚本,得到shellcode Base64解密后的值。
#include <stdio.h>
#include <stdlib.h>
#define delta 0x9e3779b9
char basetable[]={
0x42,0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x40,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x3E, 0x42, 0x42, 0x42, 0x3F, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x42, 0x42, 0x42,
0x41, 0x42, 0x42, 0x42, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x1A, 0x1B, 0x1C, 0x1D,
0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31,
0x32, 0x33, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42
};
int FindIndex(int x)
{
int i;
for ( i = 0; i < 256; i++ )
if ( basetable[i] == x )
return i;
}
void BaseDecode(unsigned char * flag, int len, unsigned char * input)
{
int i, j;
for ( i = 0, j = 0; i < len; i += 3, j += 4 )
{
input[j] = (flag[i] >> 2) & 0x3F;
input[j + 1] = ((flag[i] & 0x3) << 4) | (flag[i + 1] & 0xF0 ) >> 4;
input[j + 2] = ((flag[i + 1] & 0xF ) << 2) | (flag[i + 2] & 0xC0) >> 6;
input[j + 3] = flag[i + 2] & 0x3F;
}
for ( i = 0; i < len / 3 * 4; i++ )
input[i] = FindIndex(input[i]);
}
template<class T> T ROL(T value, int count)
{
const unsigned int nbits = sizeof(T) * 8;
if ( count > 0 )
{
count %= nbits;
T high = value >> (nbits - count);
if ( T(-1) < 0 ) // signed value
high &= ~((T(-1) << count));
value <<= count;
value |= high;
}
else
{
count = -count % nbits;
T low = value << (nbits - count);
value >>= count;
value |= low;
}
return value;
}
int main()
{
unsigned int v[66] = { 0x4B6B89A1, 0x74C15453, 0x4092A06E, 0x429B0C07, 0x40281E84, 0x8B5B44C9, 0x66FEB37B, 0x3C77A603,
0x79C5892D, 0x0D7ADA97, 0x1D51AA56, 0x02D4D703, 0x4FA526BA, 0x32FAD64A, 0x0C0F6091, 0x562B7593,
0xDB9ADD67, 0x76165563, 0xA5F79315, 0x3AEB991D, 0x1AB721D4, 0xAACD9D2C, 0x825C2B27, 0x76A7761A,
0xB4005F18, 0x117F3763, 0x512CC540, 0xC594A16F, 0xD0E24F8C, 0x9CA3E2E9, 0x0A9CC2D5, 0x4629E61D,
0x637129E3, 0xCA4E8AD7, 0xF5DFAF71, 0x474E68AB, 0x542FBC3A, 0xD6741617, 0xAD0DBBE5, 0x62F7BBE3,
0xC8D68C07, 0x880E950E, 0xF80F25BA, 0x767A264C, 0x9A7CE014, 0x5C8BC9EE, 0x5D9EF7D4, 0xB999ACDE,
0xB2EC8E13, 0xEE68232D, 0x927C5FCE, 0xC9E3A85D, 0xAC74B56B, 0x42B6E712, 0xCD2898DA, 0xFCF11C58,
0xF57075EE, 0x5076E678, 0xD4D66A35, 0x95105AB9, 0x1BB04403, 0xB240B959, 0x7B4E261A, 0x23D129D8,
0xF5E752CD, 0x4EA78F70};
unsigned int key[4] = {116,111,114,97};
unsigned int sum = 0;
unsigned int y,z,p,rounds,e;
int n = 66;
int i = 0;
rounds = 6 + 52/n;
y = v[0];
sum = rounds*delta;
do
{
e = sum >> 2 & 3;
for(p=n-1;p>0;p--)
{
z = v[p-1];
v[p] -= ((((z>>6)^(y<<2))+((y>>3)^(z<<4))) ^ ((key[(p&3)^e]^z)+(y ^ sum)));
y = v[p];
}
z = v[n-1];
v[0] -= (((key[(p^e)&3]^z)+(y ^ sum)) ^ (((y<<2)^(z>>6))+((z<<4)^(y>>3))));
y = v[0];
sum = sum-delta;
}while(--rounds);
unsigned char *t=(unsigned char*)v;
unsigned char basecode[66*4];
for (int i = 0; i < n*4; i++)
{
basecode[i]=ROL(t[i],-3);
printf("%x ",basecode[i]);
}
unsigned char input[352];
BaseDecode(basecode, 66 * 4, input);
for ( i = 0; i < 352; i++ )
printf("%c", input[i]);
return 0;
}
得到的数据,使用16进制编辑工具写入,再用IDA打开,最后解析。
加密函数的第二个参数是flag,加密过程过于冗余,直接看含flag的代码段。
逻辑并不复杂,n1,n2分别运算后,最后进行比较.
因为n1,n2相等,我们可以化简这两个等式(设v19为data1,v14为data2)
n2=data1[i]
n1=flag[i]-data2][i]%5
即flag[i]=data1[i]+data2[i]%5
我们可以通过动态调试来拿到data1和data2的数据。
找到加密函数的call指令对应的偏移,加上OD中的基址
F7步入,IDA中查看data1,data2的偏移(0X7A,0x96),在OD找到对应地址,观察寄存器的值
编写脚本
char data2[]="LoadLibraryExA";
char data1[]="is program can";
char flag[]={};
puts("\n");
for (int i = 0; i < sizeof(data1); i++)
{
printf("%c",data1[i]+data2[i]%5);
}
//jt"psojvcq!gan
jt"psojvcq!gan加上shellcode的值,最后MD5,就是flag了。
评论区