Twist1
加了某种奇怪的壳, 应该是专有壳, 需要手动脱壳, ReadMe
说了要在x86下运行, 所以丢进52pojie的xp虚拟机里调试
一上来就加了反调试, 下断点后在call Twist1.00407059
处会出错, 确实是题如其名, 很扭曲, 上了专有壳, 反调试, SMC, 函数校验, 代码混淆, 配置跟实际的逆向保护软件比较接近了.
解法就是见招拆招, 单步跟踪对每一个加密保护逐一破解
单步跟踪不下断, 调试可以发现0x00407059
是关键函数, 跟进去, 发现下面这四条指令是SMC运行时解密代码, F4出来解密之后继续跟, 需要经过总共4个解密指令段, 最后可以恢复出函数主体逻辑
抵达OEP, 此时可以dump出来脱壳的程序方便之后重载
往下走, 发现call 0x00401270
时程序会执行输入操作, 输入后会卡主, 则跟进去, 发现有SetUnhandledExceptionFilter
异常捕获函数, 属于反调试机制, nop掉判断条件adc dl,byte ptr ds:[edx]
继续调试
接下来就定位到程序的关键逻辑
显然字符串处理函数在0x004012C6
, 跟进去又经过一段SMC解密代码, 最后来到处理函数, 这里可以dump出来用IDA分析, 直接读汇编也问题不大, 锻炼基本功
这里会校验16字节的数据来判断函数完整性, 是检测系统版本的一步
接下来有3个调试, nop掉失败条件继续跟
又来一个
getthreadcontext
反调试, nop掉失败跳转继续跟
接着是硬件断点反调试, 也是nop无脑通过
最后来到处理逻辑 (finally
而且还加了代码混淆, 控制流非常混乱, 需要每个call都跟进去分析一遍
一路跟一路分析:
arr[0]循环右移6位,检查与0x49相等
arr[2]异或0x77, 检查与0x35相等
arr[1]与0x20异或,检查与0x69相等
arr[3]与0x21异或,检查与0x64相等
arr[4]与0x46异或,检查与0x8相等
arr[5]循环左移4位,检查与0x14相等
写逆
#include <stdlib.h>
#include <stdio.h>
int main() {
char valid[] = {0x49, 0x69, 0x35, 0x64, 0x08, 0x14};
char arr[7] = {0};
arr[0] = valid[0] << 6 | valid[0] >> 2;
arr[1] = valid[1] ^ 0x20;
arr[2] = valid[2] ^ 0x77;
arr[3] = valid[3] ^ 0x21;
arr[4] = valid[4] ^ 0x46;
arr[5] = valid[5] >> 4 | valid[5] << 4;
for (int i = 0; i < 7; ++i) {
printf("%c", arr[i]);
}
printf("\n");
}
完成! 前后调试了3h, 真的够变态, 大量反调试, SMC, 还检测函数完整性, 最后在核心代码处加了控制流混淆和花指令, 虽说很折磨但是逆出来还是可以学到很多技术
(逆向, 最重要的还是耐心
Easy ELF
拿到一个zip文件, 解压发现出错, 怀疑是文件格式不对
放到kali, file一下
是ELF32位文件
拖入IDA, 反汇编后扫一遍各个函数, 找到一个可能是关键函数
_BOOL4 sub_8048451()
{
if ( byte_804A021 != 49 )
return 0;
byte_804A020 ^= 0x34u;
byte_804A022 ^= 0x32u;
byte_804A023 ^= 0x88u;
if ( byte_804A024 != 88 )
return 0;
if ( byte_804A025 )
return 0;
if ( byte_804A022 != 124 )
return 0;
if ( byte_804A020 == 120 )
return byte_804A023 == -35;
return 0;
}
读一下main函数汇编, 先后call了sub_8038434, sub_8048451之后是分支到wrong和correct, 说明这两个函数就是关键函数
写出脚本
Byte21 = 49
Byte20 = 120 ^ 0x34
Byte22 = 124 ^ 0x32
Byte23 = (-35 & 0xff) ^ 0x88
Byte24 = 88
Byte25 = 0
flag = chr(Byte20) + chr(Byte21) + chr(Byte22) + chr(Byte23) + chr(Byte24) + chr(Byte25)
print(flag)
WindowsKernel
无壳, 拖进IDA, 程序结构不是很复杂, 也没有应用加密, 可以看到sub_401110
函数中有关键字符串
HWND __thiscall sub_401110(HWND hDlg)
{
HWND result; // eax
HWND v3; // eax
HWND v4; // eax
HWND v5; // eax
WCHAR String[256]; // [esp+8h] [ebp-204h] BYREF
GetDlgItemTextW(hDlg, 1003, String, 512);
if ( lstrcmpW(String, L"Enable") )
{
result = (HWND)lstrcmpW(String, L"Check");
if ( !result )
{
if ( sub_401280(0x2000) == 1 )
MessageBoxW(hDlg, L"Correct!", L"Reversing.Kr", 0x40u);
else
MessageBoxW(hDlg, L"Wrong", L"Reversing.Kr", 0x10u);
SetDlgItemTextW(hDlg, 1002, &word_4021F0);
v5 = GetDlgItem(hDlg, 1002);
EnableWindow(v5, 0);
result = (HWND)SetDlgItemTextW(hDlg, 1003, L"Enable");
}
}
else if ( sub_401280(4096) )
{
v3 = GetDlgItem(hDlg, 1002);
EnableWindow(v3, 1);
SetDlgItemTextW(hDlg, 1003, L"Check");
SetDlgItemTextW(hDlg, 1002, &word_4021F0);
v4 = GetDlgItem(hDlg, 1002);
result = SetFocus(v4);
}
else
{
result = (HWND)MessageBoxW(hDlg, L"Device Error", L"Reversing.Kr", 0x10u);
}
return result;
}
进入sub_401280
分析
int __usercall sub_401280@<eax>(HWND a1@<edi>, DWORD dwIoControlCode)
{
HANDLE v2; // esi
int result; // eax
DWORD BytesReturned; // [esp+4h] [ebp-8h] BYREF
int OutBuffer; // [esp+8h] [ebp-4h] BYREF
v2 = CreateFileW(L"\\\\.\\RevKr", 0xC0000000, 0, 0, 3u, 0, 0);
if ( v2 == (HANDLE)-1 )
{
MessageBoxW(a1, L"[Error] CreateFile", L"Reversing.Kr", 0x10u);
result = 0;
}
else if ( DeviceIoControl(v2, dwIoControlCode, 0, 0, &OutBuffer, 4u, &BytesReturned, 0) )
{
CloseHandle(v2);
result = OutBuffer;
}
else
{
MessageBoxW(a1, L"[Error] DeviceIoControl", L"Reversing.Kr", 0x10u);
result = 0;
}
return result;
}
result结果与DeviceIoControl
函数相关, 这是一个WinKer.sys
的函数, 跟windows驱动相关, 分析sys文件
NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS result; // eax
int v3; // edi
PDEVICE_OBJECT v4; // ecx
char *v5; // et1
char *v6; // et1
char *v7; // et1
char v8; // al
struct _KDPC *v9; // esi
char *v10; // et1
struct _UNICODE_STRING DestinationString; // [esp+Ch] [ebp-134h] BYREF
union _LARGE_INTEGER Interval; // [esp+14h] [ebp-12Ch] BYREF
PDEVICE_OBJECT DeviceObject; // [esp+1Ch] [ebp-124h] BYREF
PVOID P; // [esp+20h] [ebp-120h]
CCHAR Number[4]; // [esp+24h] [ebp-11Ch]
struct _OSVERSIONINFOW VersionInformation; // [esp+28h] [ebp-118h] BYREF
DbgSetDebugFilterState(0x65u, 3u, 1u);
DbgPrint("Driver Load!! \n");
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_1131C;
dword_13030 = 0;
VersionInformation.dwOSVersionInfoSize = 276;
if ( RtlGetVersion(&VersionInformation) )
{
MajorVersion = VersionInformation.dwMajorVersion;
MinorVersion = VersionInformation.dwMinorVersion;
}
else
{
PsGetVersion(&MajorVersion, &MinorVersion, 0, 0);
}
RtlInitUnicodeString(&DestinationString, "\\");
P = (PVOID)IoCreateDevice(DriverObject, 4u, &DestinationString, 0x22u, 0, 0, &DeviceObject);
if ( (int)P >= 0 )
{
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\RevKr");
v3 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if ( v3 >= 0 )
{
v4 = DeviceObject;
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_11288;
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_112F8;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_112F8;
*(_DWORD *)v4->DeviceExtension = 0;
SystemArgument2 = DeviceObject->DeviceExtension;
*(_DWORD *)SystemArgument2 = DeviceObject;
v5 = *(char **)&KeNumberProcessors;
::P = ExAllocatePool(NonPagedPool, 4 * *v5);
KeInitializeDpc(&DeviceObject->Dpc, sub_11266, DeviceObject);
v6 = *(char **)&KeNumberProcessors;
P = ExAllocatePool(NonPagedPool, 32 * *v6);
if ( P )
{
v7 = *(char **)&KeNumberProcessors;
Interval.QuadPart = -10000000i64;
v8 = *v7;
Number[0] = 0;
if ( v8 > 0 )
{
do
{
v9 = (struct _KDPC *)((char *)P + 32 * Number[0]);
KeInitializeDpc(v9, sub_113E8, 0);
KeSetTargetProcessorDpc(v9, Number[0]);
KeInsertQueueDpc(v9, 0, 0);
KeDelayExecutionThread(0, 0, &Interval);
v10 = *(char **)&KeNumberProcessors;
++Number[0];
}
while ( Number[0] < *v10 );
}
ExFreePoolWithTag(P, 0);
}
result = 0;
}
else
{
IoDeleteDevice(DriverObject->DeviceObject);
result = v3;
}
}
else
{
DbgPrint("IoCreateDevice Error\n");
result = (NTSTATUS)P;
}
return result;
}
根据Windows系统编程经验, DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_11288;
以及KeInitializeDpc
会开启一个thread去执行目标函数, 跟进去分析sub_11288
int __stdcall sub_11288(int a1, PIRP Irp)
{
int v2; // edx
struct _IRP *v3; // eax
v2 = *(_DWORD *)(Irp->Tail.Overlay.PacketType + 12);
v3 = Irp->AssociatedIrp.MasterIrp;
if ( v2 == 4096 ) // 0x1000
{
*(_DWORD *)&v3->Type = 1;
dword_13030 = 1;
dword_13034 = 0;
dword_13024 = 0;
dword_1300C = 0;
}
else if ( v2 == 0x2000 )
{
dword_13030 = 0;
*(_DWORD *)&v3->Type = dword_13024;
}
Irp->IoStatus.Status = 0;
Irp->IoStatus.Information = 4;
IofCompleteRequest(Irp, 0);
return 0;
}
根据WindowsKernel.exe
的判断函数sub_401280(0x2000) == 1
, 所以这里传进来的参数是0x2000, 只考察v2 == 0x2000
这一个分支的逻辑, dword_13030
会设置为0, 以及设置lrp
. 接着看sub_11266
函数
void __stdcall sub_11266(struct _KDPC *Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
{
char v4; // al
v4 = READ_PORT_UCHAR((PUCHAR)0x60);
sub_111DC(v4);
}
继续根据Windows系统编程经验, 这个驱动函数是从端口0x60
也就是键盘读入参数保存在v4
, 分析sub_111DC
和里面的跟result相关的函数
int __stdcall sub_111DC(char a1)
{
int result; // eax
bool v2; // zf
result = 1;
if ( dword_1300C != 1 )
{
switch ( dword_13034 )
{
case 0:
case 2:
case 4:
case 6:
goto LABEL_3;
case 1:
v2 = a1 == -91;
goto LABEL_6;
case 3:
v2 = a1 == -110;
goto LABEL_6;
case 5:
v2 = a1 == -107;
LABEL_6:
if ( !v2 )
goto LABEL_7;
LABEL_3:
++dword_13034;
break;
case 7:
if ( a1 == -80 )
dword_13034 = 100;
else
LABEL_7:
dword_1300C = 1;
break;
default:
result = sub_11156(a1);
break;
}
}
return result;
}
// continue analysis sub_11156 and next function in it
int __stdcall sub_11156(char a1)
{
int result; // eax
bool v2; // zf
char v3; // [esp+8h] [ebp+8h]
v3 = a1 ^ 0x12;
result = dword_13034 - 100;
switch ( dword_13034 )
{
case 'd':
case 'f':
case 'h':
case 'j':
goto LABEL_2;
case 'e':
v2 = v3 == -78;
goto LABEL_4;
case 'g':
v2 = v3 == -123;
goto LABEL_4;
case 'i':
v2 = v3 == -93;
LABEL_4:
if ( !v2 )
goto LABEL_5;
LABEL_2:
++dword_13034;
break;
case 'k':
if ( v3 == -122 )
dword_13034 = 200;
else
LABEL_5:
dword_1300C = 1;
break;
default:
result = sub_110D0(v3);
break;
}
return result;
}
int __stdcall sub_110D0(char a1)
{
int result; // eax
char v2; // cl
bool v3; // zf
result = dword_13034 - 200;
v2 = a1 ^ 5;
switch ( dword_13034 )
{
case 200:
case 202:
case 204:
case 206:
goto LABEL_2;
case 201:
v3 = v2 == -76;
goto LABEL_4;
case 203:
case 205:
v3 = v2 == -113;
LABEL_4:
if ( v3 )
goto LABEL_2;
goto LABEL_10;
case 207:
if ( v2 != -78 )
goto LABEL_10;
dword_13024 = 1;
LABEL_2:
++dword_13034;
break;
case 208:
dword_13024 = 0;
LABEL_10:
dword_1300C = 1;
break;
default:
return result;
}
return result;
}
dword_13034 = 0
初值为0, 所以结合scan code对照表可以分析出使得满足判断条件时的键盘输入字符串
[0xA5, 0x92, 0x95, 0xB0]
[0xB2, 0x85, 0xA3, 0x86] xor 0x12
[0xB4, 0x8F, 0x8F, 0xB2] xor 0x12 xor 0x05