AutoHotkey1
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
AuthKey = un_md5(DecryptKey) + " " + un_md5(EXE's Key)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Ex:)
DecryptKey = 1dfb6b98aef3416e03d50fd2fb525600
EXE's Key = c944634550c698febdd9c868db908d9d
=> AuthKey = visual studio
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
By Pyutic
需要在二进制文件中找到两个MD5值
查壳发现有UPX, 脱掉之后运行会报错, 估计是有自校验机制, 所以采用动调, 一路跟踪定位到弹窗函数
int __thiscall sub_4508C7(FILE **this, int a2, char *Str)
{
FILE *v4; // eax
int i; // eax
signed int v7; // eax
signed int j; // edx
int v9; // eax
int k; // eax
int l; // eax
int v12; // edi
int v13; // edi
int v14; // ecx
FILE *v15; // [esp-24h] [ebp-570h]
int v16; // [esp-4h] [ebp-550h]
char v17[1028]; // [esp+Ch] [ebp-540h] BYREF
CHAR Filename[264]; // [esp+410h] [ebp-13Ch] BYREF
char v19[32]; // [esp+518h] [ebp-34h] BYREF
int v20; // [esp+538h] [ebp-14h] BYREF
int Buffer; // [esp+53Ch] [ebp-10h] BYREF
int v22; // [esp+540h] [ebp-Ch] BYREF
int v23; // [esp+544h] [ebp-8h]
char v24; // [esp+54Bh] [ebp-1h] BYREF
char *Stra; // [esp+558h] [ebp+Ch]
sub_450F56(v17);
GetModuleFileNameA(0, Filename, 0x104u);
v4 = fopen(Filename, aRb);
*this = v4;
if ( !v4 )
return 1;
for ( i = 0; i < 8; ++i )
{
v19[i + 16] = byte_466514[i];
v19[i + 24] = byte_46650C[i];
}
v7 = strlen(Str);
this[68] = 0;
for ( j = 0; j < v7; ++j )
this[68] = (FILE *)((char *)this[68] + Str[j]);
fseek(*this, -8, 2);
fread(this + 1, 4u, 1u, *this);
v9 = ftell(*this);
v15 = *this;
v23 = v9;
fread(&Buffer, 4u, 1u, v15);
fseek(*this, 0, 0);
Stra = 0;
for ( k = 0; (int)Stra < v23; ++Stra )
{
if ( ((*this)->_flag & 0x10) != 0 )
break;
fread(&v20, 1u, 1u, *this);
k = sub_450F95(v20);
}
if ( Buffer != (k ^ 0xAAAAAAAA) )
goto LABEL_16;
fseek(*this, (int)this[1], 0);
if ( !fread(v19, 0x10u, 1u, *this) )
goto LABEL_16;
for ( l = 0; l < 16; ++l )
{
if ( v19[l] != v19[l + 16] )
break;
}
if ( l != 16 )
{
LABEL_16:
v16 = 3;
LABEL_19:
v12 = v16;
fclose(*this);
return v12;
}
fread(&v24, 1u, 1u, *this);
if ( v24 != 3 )
{
v16 = 4;
goto LABEL_19;
}
fread(&v22, 4u, 1u, *this);
v13 = v22 ^ 0xFAC1;
fread(this + 3, 1u, v22 ^ 0xFAC1, *this);
sub_450ABA(this + 3, v13, v13 + 50130);
*((_BYTE *)this + v13 + 12) = 0;
v14 = 0;
for ( this[68] = 0; v14 < v13; ++v14 )
this[68] = (FILE *)((char *)this[68] + *((char *)this + v14 + 12));
this[2] = (FILE *)ftell(*this);
return 0;
}
函数会经过一堆复杂操作, 一连串文件读写懒得逆, 主要是观察到函数最后会产生MD5值, 直接用不脱壳的原程序调试确定sub_450ABA
返回的MD5值, 这里必须用硬件断点, 因为解密过程会涉及代码自身运算, 如果用内存断点会修改数据导致解密出错, hardware breakpoint下断以后直接F9就到了(懒人逆向法
第一个MD5是220226394582d7117410e3c021748c2a
第二个MD5也有懒人法得到, 结合Autohotkey是一种脚本引擎, 可以把脚本转化为exe, 也自然有将exe逆回脚本的工具: https://www.autohotkey.com/download/Exe2Ahk.exe
Exe2Ahk.exe ahk.exe <password>
解出来就能得到第二个MD5, 两个解密一下拼接就是flag
CSHOP
打开什么都没有…
看题目CSHOP和C#谐音, 暗示.net
逆向, 用dnspy打一套
private void InitializeComponent()
{
ComponentResourceManager componentResourceManager = new ComponentResourceManager(typeof(FrmMain));
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD = new Button();
this.lbl\u007F\u000A = new Label();
this.lbl\u007F\u000D = new Label();
this.lbl\u007F\u0014 = new Label();
this.lbl\u007F\u0015 = new Label();
this.lbl\u007F\u0011 = new Label();
this.lbl\uFFFD\u0014 = new Label();
this.lbl\u007F\u0019 = new Label();
this.lbl\u007F\u0001 = new Label();
this.lbl\u007F\u0003 = new Label();
this.lbl\u007F\u0002 = new Label();
base.SuspendLayout();
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Location = new Point(165, 62);
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Name = "btnStart";
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Size = new Size(0, 0);
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.TabIndex = 0;
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.UseVisualStyleBackColor = true;
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Click += this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD_Click;
this.lbl\u007F\u000A.Location = new Point(43, 123);
this.lbl\u007F\u000A.Name = "lblGu";
this.lbl\u007F\u000A.Size = new Size(53, 23);
this.lbl\u007F\u000A.TabIndex = 1;
this.lbl\u007F\u000A.Text = "label1";
this.lbl\u007F\u000D.Location = new Point(90, 123);
this.lbl\u007F\u000D.Name = "lblNu";
this.lbl\u007F\u000D.Size = new Size(53, 23);
this.lbl\u007F\u000D.TabIndex = 2;
this.lbl\u007F\u000D.Text = "label2";
this.lbl\u007F\u0014.Location = new Point(135, 123);
this.lbl\u007F\u0014.Name = "lblSu";
this.lbl\u007F\u0014.Size = new Size(53, 23);
this.lbl\u007F\u0014.TabIndex = 3;
this.lbl\u007F\u0014.Text = "label3";
this.lbl\u007F\u0015.Location = new Point(182, 123);
this.lbl\u007F\u0015.Name = "lblTu";
this.lbl\u007F\u0015.Size = new Size(53, 23);
this.lbl\u007F\u0015.TabIndex = 4;
this.lbl\u007F\u0015.Text = "label4";
this.lbl\u007F\u0011.Location = new Point(228, 123);
this.lbl\u007F\u0011.Name = "lblKu";
this.lbl\u007F\u0011.Size = new Size(53, 23);
this.lbl\u007F\u0011.TabIndex = 5;
this.lbl\u007F\u0011.Text = "label4";
this.lbl\uFFFD\u0014.Location = new Point(278, 123);
this.lbl\uFFFD\u0014.Name = "ppppp";
this.lbl\uFFFD\u0014.Size = new Size(53, 23);
this.lbl\uFFFD\u0014.TabIndex = 6;
this.lbl\uFFFD\u0014.Text = "label4";
this.lbl\u007F\u0019.Location = new Point(324, 123);
this.lbl\u007F\u0019.Name = "lblMu";
this.lbl\u007F\u0019.Size = new Size(53, 23);
this.lbl\u007F\u0019.TabIndex = 7;
this.lbl\u007F\u0019.Text = "label4";
this.lbl\u007F\u0001.Location = new Point(369, 123);
this.lbl\u007F\u0001.Name = "lblXu";
this.lbl\u007F\u0001.Size = new Size(53, 23);
this.lbl\u007F\u0001.TabIndex = 8;
this.lbl\u007F\u0001.Text = "label4";
this.lbl\u007F\u0003.Location = new Point(413, 123);
this.lbl\u007F\u0003.Name = "lblZu";
this.lbl\u007F\u0003.Size = new Size(53, 23);
this.lbl\u007F\u0003.TabIndex = 9;
this.lbl\u007F\u0003.Text = "label4";
this.lbl\u007F\u0002.Location = new Point(457, 123);
this.lbl\u007F\u0002.Name = "lblQu";
this.lbl\u007F\u0002.Size = new Size(53, 23);
this.lbl\u007F\u0002.TabIndex = 10;
this.lbl\u007F\u0002.Text = "label4";
base.AutoScaleDimensions = new SizeF(7f, 12f);
base.AutoScaleMode = AutoScaleMode.Font;
base.ClientSize = new Size(626, 316);
base.Controls.Add(this.lbl\u007F\u0002);
base.Controls.Add(this.lbl\u007F\u0003);
base.Controls.Add(this.lbl\u007F\u0001);
base.Controls.Add(this.lbl\u007F\u0019);
base.Controls.Add(this.lbl\uFFFD\u0014);
base.Controls.Add(this.lbl\u007F\u0011);
base.Controls.Add(this.lbl\u007F\u0015);
base.Controls.Add(this.lbl\u007F\u0014);
base.Controls.Add(this.lbl\u007F\u000D);
base.Controls.Add(this.lbl\u007F\u000A);
base.Controls.Add(this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD);
base.FormBorderStyle = FormBorderStyle.FixedSingle;
base.Icon = (Icon)componentResourceManager.GetObject("$this.Icon");
base.MaximizeBox = false;
base.Name = "FrmMain";
base.StartPosition = FormStartPosition.CenterScreen;
this.Text = "CSHOP";
base.Load += this.Form1_Load;
base.ResumeLayout(false);
}
找到基本模块, 发现有button, 但是实际运行时没有, 观察属性, 发现
this.\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD.Size = new Size(0, 0);
可能是button被改没了, 手动修改size保存
得到exe, 运行
PEPassword
original.exe
里是虚晃的password, 先不管, 拿packed.exe
查壳
调试定位主体逻辑, 这里是调用库函数dispatchMessageA
处理输入的字符
在IDA中查看对应的cmp
指令的交叉引用
OD中查看0x004091A8
地址, 提前到基本块入口下断调试发现ESI
中会保存输入的字符串
调整EAX == 0xE98F842A
越过判断条件继续跟, 发现会进入EndDialog
终止程序, 返回去看可以确定核心校验函数就是sub_004091D8
, 对比一下original.exe
的401000地址是入口, packed之后应该是加密了二进制程序, 所以综合起来看整个题目的意思就是找到packed.exe
的正确输入然后解密出入口地址开始的函数, 对应到original.exe
即输出password的函数打印出flag
分析解密函数
__int64 __usercall sub_409200@<edx:eax>(__int64 a1@<edx:eax>)
{
__int64 v1; // rdi
int v2; // eax
int v3; // ebx
int v4; // eax
unsigned int v5; // ecx
unsigned int v6; // edx
int v7; // ebx
__int64 v9; // [esp-20h] [ebp-20h]
v9 = a1;
v1 = a1;
LODWORD(a1) = 'HASH';
LOBYTE(a1) = *(_BYTE *)HIDWORD(a1) ^ 'H';
v2 = sub_4091DA(a1, SHIDWORD(a1));
v3 = v2;
BYTE1(v2) ^= *(_BYTE *)HIDWORD(v1);
v4 = sub_4091DA(v2, SHIDWORD(v1));
v6 = v5 >> 2;
do
{
*(_DWORD *)v1 ^= v4;
LODWORD(v1) = v1 + 4;
v7 = __ROL4__(v3, v4);
v4 = __ROR4__(v7 ^ v4, SBYTE1(v7));
v3 = v4 + v7;
--v6;
}
while ( v6 );
return v9;
}
根据401000地址处已知的原数据, 和加密后的packed的数据, 可以得到第一轮v4的值, 对于v3的值可以爆破32bits, 结合第二轮v4的值为0x5a5a7e05
#include <stdio.h>
unsigned int rol(unsigned int data, char bits) {
return (unsigned int)(data<<bits)|(data>>(32-bits));
}
unsigned int ror(unsigned int data, char bits) {
return (unsigned int)(data>>bits)|(data<<(32-bits));
}
int main() {
unsigned int original = 0x014cec81 ^ 0x0B6E62E17;
unsigned int target = 0x5a5a7e05;
unsigned int brut = 0;
for(brut = 0; brut <= 0xffffffff; ++brut){
unsigned int temp = rol(brut, original & 0xff);
if(target == ror(temp ^ original, (temp>>8) & 0xff)){
printf("%x\n", brut);
}
}
return 0;
}
得到0xa1beee22
和0xc263a2cb
两种可能, 验证后可知是0xc263a2cb
, IDA里写解密脚本
from idaapi import *
from idc_bc695 import *
def rol(data, bits):
for i in range(bits):
data = ((data<<1)|((data>>31)&1))&((1<<32)-1)
return data
def ror(data, bits):
for i in range(bits):
data = ((data>>1)|((data&1)<<31))&((1<<32)-1)
return data
address= 0x401000
v4 = 0xb7aac296
v3 = 0xc263a2cb
for i in range(0x1000):
PatchDword(address, Dword(address)^v4)
v7 = rol(v3, v4&0xff)
v4 = ror(v7 ^ v4, (v7>>8)&0xff)
v3 = (v4 + v7)&((1<<32)-1)
address+=4
print("Done!")
解密后在401000地址处c
一下转码, 然后p
创建函数入口, 再F5反编译得到真正的password函数
int __stdcall sub_401000(int a1, int a2, int a3, int a4)
{
int v4; // eax
char v5; // cl
char v7[16]; // [esp+0h] [ebp-14Ch]
char v8[16]; // [esp+10h] [ebp-13Ch]
char v9[300]; // [esp+20h] [ebp-12Ch] BYREF
strcpy(v9, "Congratulation!\r\n\r\nPassword is ");
v7[0] = 86;
v7[2] = 95;
v4 = 0;
v8[0] = 16;
v8[1] = 32;
v8[2] = 48;
v8[3] = 64;
v8[4] = 80;
v8[5] = 96;
v8[6] = 112;
v8[7] = 0x80;
v8[8] = -112;
v8[9] = -96;
v8[10] = -80;
v8[11] = -64;
v8[12] = -48;
v7[1] = 82;
v7[3] = 45;
v7[4] = 15;
v7[5] = 39;
v7[6] = 56;
v7[7] = -52;
v7[8] = -94;
v7[9] = -1;
v7[10] = -111;
v7[11] = -31;
v7[12] = -48;
do
{
v5 = v8[v4] ^ v7[v4];
v7[v4] = v5;
v9[v4++ + 31] = v5;
}
while ( v4 < 13 );
dword_40509C(0, v9, &unk_4084E0, 64);
return 0;
}
执行一下即可得到flag
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
int main () {
int v4;
char v5;
char v7[16];
char v8[16];
char v9[300];
strcpy(v9, "Congratulation!\r\n\r\nPassword is ");
v7[0] = 86;
v7[2] = 95;
v4 = 0;
v8[0] = 16;
v8[1] = 32;
v8[2] = 48;
v8[3] = 64;
v8[4] = 80;
v8[5] = 96;
v8[6] = 112;
v8[7] = 0x80;
v8[8] = -112;
v8[9] = -96;
v8[10] = -80;
v8[11] = -64;
v8[12] = -48;
v7[1] = 82;
v7[3] = 45;
v7[4] = 15;
v7[5] = 39;
v7[6] = 56;
v7[7] = -52;
v7[8] = -94;
v7[9] = -1;
v7[10] = -111;
v7[11] = -31;
v7[12] = -48;
do {
v5 = v8[v4] ^ v7[v4];
v7[v4] = v5;
v9[v4++ + 31] = v5;
} while ( v4 < 13 );
printf("%s\n", v9);
return 0;
}