文章目录
异或
Base64 编码逆向
Base64 编码是一种用于将二进制数据转换为文本字符串的编码方式。它常用于在文本协议中传输二进制数据,或者用于存储二进制数据在文本文件中。
当遇到某些很复杂的函数时,可以寻找函数中使用到的常量,然后去搜索引擎上搜索,有可能能够找到相关源码,然后进行比对
/*base64.c*/
#include "base64.h"
unsigned char *base64_encode(unsigned char *str)
{
long len;
long str_len;
unsigned char *res;
int i,j;
//定义base64编码表
unsigned char *base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//计算经过base64编码后的字符串长度
str_len=strlen(str);
if(str_len % 3 == 0)
len=str_len/3*4;
else
len=(str_len/3+1)*4;
res=malloc(sizeof(unsigned char)*len+1);
res[len]='\0';
//以3个8位字符为一组进行编码
for(i=0,j=0;i<len-2;j+=3,i+=4)
{
res[i]=base64_table[str[j]>>2]; //取出第一个字符的前6位并找出对应的结果字符
res[i+1]=base64_table[(str[j]&0x3)<<4 | (str[j+1]>>4)]; //将第一个字符的后位与第二个字符的前4位进行组合并找到对应的结果字符
res[i+2]=base64_table[(str[j+1]&0xf)<<2 | (str[j+2]>>6)]; //将第二个字符的后4位与第三个字符的前2位组合并找出对应的结果字符
res[i+3]=base64_table[str[j+2]&0x3f]; //取出第三个字符的后6位并找出结果字符
}
switch(str_len % 3)
{
case 1:
res[i-2]='=';
res[i-1]='=';
break;
case 2:
res[i-1]='=';
break;
}
return res;
}
unsigned char *base64_decode(unsigned char *code)
{
//根据base64表,以字符找到对应的十进制数据
int table[]={0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,62,0,0,0,
63,52,53,54,55,56,57,58,
59,60,61,0,0,0,0,0,0,0,0,
1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20,21,
22,23,24,25,0,0,0,0,0,0,26,
27,28,29,30,31,32,33,34,35,
36,37,38,39,40,41,42,43,44,
45,46,47,48,49,50,51
};
long len;
long str_len;
unsigned char *res;
int i,j;
//计算解码后的字符串长度
len=strlen(code);
//判断编码后的字符串后是否有=
if(strstr(code,"=="))
str_len=len/4*3-2;
else if(strstr(code,"="))
str_len=len/4*3-1;
else
str_len=len/4*3;
res=malloc(sizeof(unsigned char)*str_len+1);
res[str_len]='\0';
//以4个字符为一位进行解码
for(i=0,j=0;i < len-2;j+=3,i+=4)
{
res[j]=((unsigned char)table[code[i]])<<2 | (((unsigned char)table[code[i+1]])>>4); //取出第一个字符对应base64表的十进制数的前6位与第二个字符对应base64表的十进制数的后2位进行组合
res[j+1]=(((unsigned char)table[code[i+1]])<<4) | (((unsigned char)table[code[i+2]])>>2); //取出第二个字符对应base64表的十进制数的后4位与第三个字符对应bas464表的十进制数的后4位进行组合
res[j+2]=(((unsigned char)table[code[i+2]])<<6) | ((unsigned char)table[code[i+3]]); //取出第三个字符对应base64表的十进制数的后2位与第4个字符进行组合
}
return res;
}
Base64 变表逆向
IDA 动态调试 IDA 8.3
我们可以通过动态调试知道某些值或者其变化,也可以通过观察函数前后相关值的变化进而猜测其功能
在Windows机器上运行IDA可以直接动态调试exe文件,但不可以调试elf文件。
如果需要调试elf文件需要选择远程调试
将Windows上的IDA的服务器组件移动到Ubuntu上去
linux_server:在Linux计算机上执行的、用于调试32位Linux应用程序的服务器组件。
linux_server64:在64位Linux计算机上执行的、用于调试64位Linux应用程序的服务器组件。
运行组件
根据要调试的elf文件类型运行不同组件
IDA选择调试器
选择设置
OK后
相关动态调试功能
run to cursor:运行到光标
快捷键A:尝试将一段数据制定为字符串字面量(String Literal)
IDA 动态调试解RC4
也是根据输入输出来猜测
绕过反调试
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
int j; // [esp+31Ch] [ebp-69Ch]
int i; // [esp+328h] [ebp-690h]
size_t v6; // [esp+334h] [ebp-684h]
char v7[55]; // [esp+340h] [ebp-678h]
char v8; // [esp+377h] [ebp-641h]
unsigned int v9; // [esp+380h] [ebp-638h]
int v10; // [esp+38Ch] [ebp-62Ch]
char Str[520]; // [esp+398h] [ebp-620h] BYREF
char v12[264]; // [esp+5A0h] [ebp-418h] BYREF
char v13[264]; // [esp+6A8h] [ebp-310h] BYREF
char v14[516]; // [esp+7B0h] [ebp-208h] BYREF
__CheckForDebuggerJustMyCode(&unk_49C00F);
j__memset(v14, 0, 0x200u);
j__memset(v13, 0, 0x100u);
j__memset(v12, 0, 0x100u);
j__memset(Str, 0, 0x200u);
v10 = 0;
v9 = 0;
v7[0] = -28;
v7[1] = 21;
v7[2] = -60;
v7[3] = -19;
v7[4] = -90;
v7[5] = 47;
v7[6] = 86;
v7[7] = 16;
v7[8] = -69;
v7[9] = 19;
v7[10] = -21;
v7[11] = -83;
v7[12] = 117;
v7[13] = 86;
v7[14] = -57;
v7[15] = -69;
v7[16] = -69;
v7[17] = -23;
v7[18] = -71;
v7[19] = -52;
v7[20] = 2;
v7[21] = 58;
v7[22] = 80;
v7[23] = -97;
v7[24] = 54;
v7[25] = -112;
v7[26] = 105;
v7[27] = -66;
v7[28] = 124;
v7[29] = 66;
v7[30] = 68;
v7[31] = -54;
v7[32] = -58;
v7[33] = -44;
v7[34] = 36;
v7[35] = 92;
v7[36] = -46;
v7[37] = -71;
v7[38] = 36;
v7[39] = -63;
v7[40] = 24;
v7[41] = -109;
v7[42] = -77;
v7[43] = -22;
sub_42F057(v14);
sub_42C40B("Welcome!! give me your flag:\n");
do
{
v8 = j_j_j___fgetchar();
if ( v8 == 10 )
break;
Str[v9++] = v8;
}
while ( (int)v9 < 44 );
if ( v9 >= 0x200 )
j____report_rangecheckfailure();
Str[v9] = 0;
v6 = j__strlen(Str);
sub_42CEFB(v13, v14, v6);
for ( i = 0; i < 256; ++i )
v12[i] = v13[i];
sub_42D5B8(v13, Str, v6);
for ( j = 0; j < 44; ++j )
{
if ( v7[j] == Str[j] )
++v10;
}
if ( v10 == 44 )
sub_42C40B("Yes, u right!\n");
else
sub_42C40B("no no no\n");
sub_42D0CC("pause");
return 0;
}
反调试函数
__CheckForDebuggerJustMyCode(&unk_49C00F);
………
………
sub_42F057(v14);
sub_42f057()
_BYTE *__cdecl sub_4325D0(_BYTE *a1)
{
HWND ForegroundWindow; // eax
unsigned __int64 v2; // rax
unsigned __int64 v3; // rax
int v5; // [esp+25Ch] [ebp-438h]
CHAR String[1028]; // [esp+28Ch] [ebp-408h] BYREF
if ( IsDebuggerPresent() )
*a1 = 89;
else
*a1 = 88;
ForegroundWindow = GetForegroundWindow();
GetWindowTextA(ForegroundWindow, String, 1023);
if ( j__strstr(String, "WinDbg")
|| j__strstr(String, "x64dbg")
|| j__strstr(String, "x32dbg")
|| j__strstr(String, "OllyICE")
|| j__strstr(String, "OllyDBG")
|| j__strstr(String, "Immunity") )
{
a1[1] = 111;
}
else
{
a1[1] = 48;
}
SetLastError((DWORD)"12345");
OutputDebugStringW("Test for debugger!");
if ( (char *)GetLastError() == "12345" )
a1[2] = 110;
else
a1[2] = 117;
if ( !CloseHandle((HANDLE)0x1234) && GetLastError() == 6 )
a1[3] = 66;
else
a1[3] = 65;
if ( NtCurrentPeb()->BeingDebugged )
a1[4] = 97;
else
a1[4] = 64;
v2 = __rdtsc();
v5 = v2;
v3 = __rdtsc();
if ( (unsigned int)(v3 - v5) >= 0xFF )
a1[5] = 100;
else
a1[5] = 68;
a1[6] = 0;
return a1;
}
此时需要使用atttach调试来绕过反调试
核心就是使得程序在非调试条件下正常运行过调试函数后再介入调试
先用IDA打开对应文件,在反调试函数后同时是exe此时还没运行的地方下断点
选择调试器
直接运行exe文件
选择对应运行的exe文件
此时程序会固定停在此处
然后continue
然后输入
停在之前所设断点处
此时即可调试了
IDA 代码修复 & 数组识别
根据某些暗示将数组合并为一个大数组
可以先根据熟悉函数判断相关变量类型进而确定某些函数的参数类型
根据陌生函数实际用处和定义来修改参数类型和返回值类型