某一天我像往常一樣在工位上躺平,就在我享受這愜意的躺平生活時,我的Boss直聘突然收到了火絨招聘人事的消息,簡單跟他聊了幾句之后,互相加了QQ,對方直接給了樣本,讓寫一個分析報告,要求是這樣的。
作為一名運維人員,還真沒寫報告的習慣,所以我不打算寫啥報告,直接逆向分析,爭取把這個程序的源代碼全部搞出來。
這個小程序里面還真的有我們可以借鑒的功能呢,等我把這些小功能逆出來分享在這里吧。
樣本下載:https://cdn.lyshark.com/courseware/分析樣本(1).zip
在逆向還原代碼時,應該從主函數開始,逐步遞進,層層恢復,借助IDA+OD等工具,恢復代碼,我們的目的只有一個,那就是讓恢復的代碼能夠順利通過編譯,並實現同樣的運行效果即可。
還原 sub_40CAB0 函數
逆向還原子過程 sub_40CAB0(): 先還原功能性模塊,第一個需要還原的位置是sub_40CAB0
此處代碼比較簡單,還原沒有任何難度,但需要注意有個內嵌子過程需要后期恢復。
#include <Windows.h>
#include <iostream>
int sub_40CAB0()
{
HMODULE LibraryA;
FARPROC ProcAddress;
int result;
char proc_name[8];
char kernel_base[16];
char tasklist_[32];
char rundll32[16];
strcpy(kernel_base, "KERNEL32.dll");
strcpy(proc_name, "WinExec");
LibraryA = LoadLibraryA(kernel_base);
ProcAddress = GetProcAddress(LibraryA, proc_name);
std::cout << "得到WinExec地址: " << ProcAddress << std::endl;
strcpy(rundll32, "rundll32.exe");
result = 1; // 此處函數需要繼續逆向分析,暫時使用1代替
if (result)
{
strcpy(tasklist_, "taskkill /f /im rundll32.exe");
return ((int(__stdcall *)(char *, DWORD))ProcAddress)(tasklist_, 0);
}
return result;
}
int main(int argc, char *argv)
{
sub_40CAB0();
getchar();
return 0;
}
上方有個地方我們需要繼續跟進,所以先用result = 1;
代替,后面的過程我們需要跟進,上方代碼我們確保可以便宜通過,並成功執行,如下。
逆向還原子過程 sub_40EC00(): 我們繼續還原子過程sub_40EC00()
該過程稍微復雜一點,還原代碼如下。
#include <Windows.h>
#include <iostream>
// 取進程數,並判斷是否是所需進程
int __cdecl sub_40EC00(int a1)
{
HMODULE KERNEL32Base;
FARPROC CreateToolhelp32SnapshotBase;
DWORD *v3;
FARPROC v11;
FARPROC v9;
FARPROC lstrcmpiABase;
FARPROC Process32NextBase;
int v10;
FARPROC Process32FirstBase;
char lstrcmpiAAscii[12];
CHAR LibFileName[16];
char Process32NextAscii[16];
char Process32FirstAscii[16];
CHAR CreateToolhelp32SnapshotAscii[28];
// 取kernel32基地址
strcpy(LibFileName, "KERNEL32.dll");
KERNEL32Base = LoadLibraryA(LibFileName);
// CreateToolhelp32Snapshot
strcpy(CreateToolhelp32SnapshotAscii, "CreateToolhelp32Snapshot");
CreateToolhelp32SnapshotBase = GetProcAddress(KERNEL32Base, CreateToolhelp32SnapshotAscii);
std::cout << "CreateToolhelp32Snapshot 基地址 = >" << CreateToolhelp32SnapshotBase << std::endl;
// Process32Next
strcpy(Process32NextAscii, "Process32Next");
Process32NextBase = GetProcAddress(KERNEL32Base, Process32NextAscii);
std::cout << "Process32Next 基地址 => " << Process32NextBase << std::endl;
// Process32First
strcpy(Process32FirstAscii, "Process32First");
Process32FirstBase = GetProcAddress(KERNEL32Base, Process32FirstAscii);
std::cout << "Process32First 基地址 => " << Process32FirstBase << std::endl;
// lstrcmpiA
strcpy(lstrcmpiAAscii, "lstrcmpiA");
lstrcmpiABase = GetProcAddress(KERNEL32Base, lstrcmpiAAscii);
std::cout << "lstrcmpiA 基地址 => " << lstrcmpiABase << std::endl;
// 調用函數,獲取進程類型
// dwFlags:指定了獲取系統進程快照的類型
// th32ProcessID:指向要獲取進程快照的ID,獲取系統內所有進程快照時是0
v10 = ((int(__stdcall *)(int, DWORD))CreateToolhelp32SnapshotBase)(2, 0);
std::cout << "獲取進程快照: " << v10 << std::endl;
// 申請臨時空間
v3 = (DWORD *)operator new(296u);
*v3 = 296;
// 調用獲取進程信息快照
if (!((int(__stdcall *)(int, DWORD *))Process32FirstBase)(v10, v3))
return 0;
return 0;
}
int main(int argc, char *argv)
{
sub_40EC00(11);
getchar();
return 0;
}
嘗試恢復非判斷流程,恢復后,我們編譯並調用看看效果,是否滿足條件了。
接着繼續恢復判斷表達式,此處的sub_407070
是子過程,我們暫時使用if (!1)
代替。
#include <Windows.h>
#include <iostream>
// 取進程數,並判斷是否是所需進程
int __cdecl sub_40EC00(int a1)
{
HMODULE KERNEL32Base;
FARPROC CreateToolhelp32SnapshotBase;
DWORD *v3;
FARPROC lstrcmpiABase;
FARPROC Process32NextBase;
int v10;
FARPROC Process32FirstBase;
char lstrcmpiAAscii[12];
CHAR LibFileName[16];
char Process32NextAscii[16];
char Process32FirstAscii[16];
CHAR CreateToolhelp32SnapshotAscii[28];
// 取kernel32基地址
strcpy(LibFileName, "KERNEL32.dll");
KERNEL32Base = LoadLibraryA(LibFileName);
// CreateToolhelp32Snapshot
strcpy(CreateToolhelp32SnapshotAscii, "CreateToolhelp32Snapshot");
CreateToolhelp32SnapshotBase = GetProcAddress(KERNEL32Base, CreateToolhelp32SnapshotAscii);
std::cout << "CreateToolhelp32Snapshot 基地址 = >" << CreateToolhelp32SnapshotBase << std::endl;
// Process32Next
strcpy(Process32NextAscii, "Process32Next");
Process32NextBase = GetProcAddress(KERNEL32Base, Process32NextAscii);
std::cout << "Process32Next 基地址 => " << Process32NextBase << std::endl;
// Process32First
strcpy(Process32FirstAscii, "Process32First");
Process32FirstBase = GetProcAddress(KERNEL32Base, Process32FirstAscii);
std::cout << "Process32First 基地址 => " << Process32FirstBase << std::endl;
// lstrcmpiA
strcpy(lstrcmpiAAscii, "lstrcmpiA");
lstrcmpiABase = GetProcAddress(KERNEL32Base, lstrcmpiAAscii);
std::cout << "lstrcmpiA 基地址 => " << lstrcmpiABase << std::endl;
// 調用函數,獲取進程類型
// dwFlags:指定了獲取系統進程快照的類型
// th32ProcessID:指向要獲取進程快照的ID,獲取系統內所有進程快照時是0
v10 = ((int(__stdcall *)(int, DWORD))CreateToolhelp32SnapshotBase)(2, 0);
std::cout << "獲取進程快照: " << v10 << std::endl;
// 申請臨時空間
v3 = (DWORD *)operator new(296);
*v3 = 296;
// 調用獲取進程信息快照
if (!((int(__stdcall *)(int, DWORD *))Process32FirstBase)(v10, v3))
return 0;
// 此處我們先把子過程!sub_407070(v3 + 9, a1)用1代替,后期需要繼續調試
if (!1)
return v3[2];
// 調用獲取進程列表,此處就是調用獲取第一個進程列表
if (!((int(__stdcall *)(int, DWORD *))Process32NextBase)(v10, v3))
return 0;
while (1)
{
// 這是一個對比函數,主要用來對比傳入的ID是否是需要的進程,如果是則跳出循環
if (!((int(__stdcall *)(DWORD *, int))lstrcmpiABase)(v3 + 9, a1))
break;
// 獲取下一個進程信息
if (!((int(__stdcall *)(int, DWORD *))Process32NextBase)(v10, v3))
return 0;
}
Sleep(1);
return v3[2];
}
int main(int argc, char *argv)
{
sub_40EC00(1258);
getchar();
return 0;
}
由於缺失代碼,所以此處不強制要求程序能跑起來,只需要能夠通過編譯即說明完成工作。
逆向還原子過程 sub_407070(): 子過程 sub_40EC00
中嵌套了另一個子過程,我們繼續遞進,將sub_407070
子過程逆出來,這個過程主要實現機制轉換,比對工作。
主要功能:判斷是否是大寫字母,是則轉為小寫,並將傳入的兩個值進行比對。
#include <Windows.h>
#include <iostream>
// 進制轉換
int sub_407070(unsigned char *x, unsigned char *y)
{
int item_a, item_b;
do
{
item_a = *x++;
if (item_a >= 'A' && item_a <= 'Z')
item_a += 32;
item_b = *y++;
if (item_b >= 'A' && item_b <= 'Z')
item_b += 32;
} while (item_a && item_a == item_b);
return item_a - item_b;
}
int main(int argc, char *argv)
{
unsigned char a[] = "ABCD";
unsigned char b[] = "QWERTYU";
int ref = sub_407070(a, b);
std::cout << "轉換與比對: " << ref << std::endl;
int ref1 = sub_407070(b, a);
std::cout << "轉換與比對: " << ref1 << std::endl;
getchar();
return 0;
}
至此,我們通過IDA跳回到主函數,此時我們已經完全恢復好主函數中的sub_40CAB0()
子過程了,該子過程可以跳過了,源代碼總結如下.
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
// 進制轉換
int sub_407070(unsigned char *x, unsigned char *y)
{
int item_a, item_b;
do
{
item_a = *x++;
if (item_a >= 'A' && item_a <= 'Z')
item_a += 32;
item_b = *y++;
if (item_b >= 'A' && item_b <= 'Z')
item_b += 32;
} while (item_a && item_a == item_b);
return item_a - item_b;
}
// 取進程數,並判斷是否是所需進程
int __cdecl sub_40EC00(int a1)
{
HMODULE KERNEL32Base;
FARPROC CreateToolhelp32SnapshotBase;
DWORD *v3;
FARPROC lstrcmpiABase;
FARPROC Process32NextBase;
int v10;
FARPROC Process32FirstBase;
char lstrcmpiAAscii[12];
CHAR LibFileName[16];
char Process32NextAscii[16];
char Process32FirstAscii[16];
CHAR CreateToolhelp32SnapshotAscii[28];
// 取kernel32基地址
strcpy(LibFileName, "KERNEL32.dll");
KERNEL32Base = LoadLibraryA(LibFileName);
// CreateToolhelp32Snapshot
strcpy(CreateToolhelp32SnapshotAscii, "CreateToolhelp32Snapshot");
CreateToolhelp32SnapshotBase = GetProcAddress(KERNEL32Base, CreateToolhelp32SnapshotAscii);
std::cout << "CreateToolhelp32Snapshot 基地址 = >" << CreateToolhelp32SnapshotBase << std::endl;
// Process32Next
strcpy(Process32NextAscii, "Process32Next");
Process32NextBase = GetProcAddress(KERNEL32Base, Process32NextAscii);
std::cout << "Process32Next 基地址 => " << Process32NextBase << std::endl;
// Process32First
strcpy(Process32FirstAscii, "Process32First");
Process32FirstBase = GetProcAddress(KERNEL32Base, Process32FirstAscii);
std::cout << "Process32First 基地址 => " << Process32FirstBase << std::endl;
// lstrcmpiA
strcpy(lstrcmpiAAscii, "lstrcmpiA");
lstrcmpiABase = GetProcAddress(KERNEL32Base, lstrcmpiAAscii);
std::cout << "lstrcmpiA 基地址 => " << lstrcmpiABase << std::endl;
// 調用函數,獲取進程類型
// dwFlags:指定了獲取系統進程快照的類型
// th32ProcessID:指向要獲取進程快照的ID,獲取系統內所有進程快照時是0
v10 = ((int(__stdcall *)(int, DWORD))CreateToolhelp32SnapshotBase)(2, 0);
std::cout << "獲取進程快照: " << v10 << std::endl;
// 申請臨時空間
v3 = (DWORD *)operator new(296);
*v3 = 296;
// 調用獲取進程信息快照
if (!((int(__stdcall *)(int, DWORD *))Process32FirstBase)(v10, v3))
return 0;
// 調用子過程
if (!sub_407070((unsigned char *)v3 + 9, (unsigned char *)a1))
return v3[2];
// 調用獲取進程列表,此處就是調用獲取第一個進程列表
if (!((int(__stdcall *)(int, DWORD *))Process32NextBase)(v10, v3))
return 0;
while (1)
{
// 這是一個對比函數,主要用來對比傳入的ID是否是需要的進程,如果是則跳出循環
if (!((int(__stdcall *)(DWORD *, int))lstrcmpiABase)(v3 + 9, a1))
break;
// 獲取下一個進程信息
if (!((int(__stdcall *)(int, DWORD *))Process32NextBase)(v10, v3))
return 0;
}
Sleep(1);
return v3[2];
}
// 主函數
int sub_40CAB0()
{
HMODULE LibraryA;
FARPROC ProcAddress;
int result;
char proc_name[8];
char kernel_base[16];
char tasklist_[32];
char rundll32[16];
strcpy(kernel_base, "KERNEL32.dll");
strcpy(proc_name, "WinExec");
LibraryA = LoadLibraryA(kernel_base);
ProcAddress = GetProcAddress(LibraryA, proc_name);
std::cout << "得到WinExec地址: " << ProcAddress << std::endl;
strcpy(rundll32, "rundll32.exe");
result = sub_40EC00((int)rundll32);
if (result)
{
strcpy(tasklist_, "taskkill /f /im rundll32.exe");
return ((int(__stdcall *)(char *, DWORD))ProcAddress)(tasklist_, 0);
}
return result;
}
int main(int argc, char *argv)
{
sub_40CAB0();
getchar();
return 0;
}
編譯通過即可。
還原 sub_4089A0 函數
這個主函數就有趣多了,層層嵌套,復雜度已經上來了,我給大家描述一下我們需要還原的子過程,以及每個過程所在層級,這樣我們可以看圖,層層遞進依次恢復代碼。
逆向還原子過程 sub_4070E0(): 此子過程是最內側的,實現的是大寫轉小寫,並比較長度,返回差值,其還原后代碼如下。
這里告訴大家一個規范,當IDA中逆向出unsigned __int8 *x
其實可以使用unsigned char *x
代替。
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
// 先逆最內側的函數
int __stdcall sub_4070E0(unsigned char *x, unsigned char *y, int count)
{
int StringPtr_A;
int StringPtr_B;
do
{
StringPtr_A = *x++;
if (StringPtr_A >= 'A' && StringPtr_A <= 'Z')// 判斷英文是否是大寫
StringPtr_A += 32; // 大寫轉小寫
StringPtr_B = *y++;
if (StringPtr_B >= 'A' && StringPtr_B <= 'Z')
StringPtr_B += 32;
--count;
} //
// 比較所有變量是否為空
// 此處需要注意優先級,雙等於號優先級最高,其次才是與運算
while (count && StringPtr_A && StringPtr_A == StringPtr_B);
return StringPtr_A - StringPtr_B;
}
int main(int argc, char *argv)
{
unsigned char sz[32] = "hello lyshark";
unsigned char sz2[32] = "hello world";
// 傳入兩個字符串,以及字符串長度
int ref = sub_4070E0(sz, sz2, 10);
std::cout << "兩者差值: " << ref << std::endl;
ref = sub_4070E0(sz2, sz, 10);
std::cout << "兩者差值: " << ref << std::endl;
getchar();
return 0;
}
運行后看結果吧。
逆向還原子過程 sub_407130(): 該過程比較簡單,內部嵌套了上方子過程,我們將其恢復一下。
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
// 先逆最內側的函數
int __stdcall sub_4070E0(unsigned __int8 *x, unsigned __int8 *y, int count)
{
int StringPtr_A;
int StringPtr_B;
do
{
StringPtr_A = *x++;
if (StringPtr_A >= 'A' && StringPtr_A <= 'Z')// 判斷英文是否是大寫
StringPtr_A += 32; // 大寫轉小寫
StringPtr_B = *y++;
if (StringPtr_B >= 'A' && StringPtr_B <= 'Z')
StringPtr_B += 32;
--count;
} //
// 比較所有變量是否為空
// 此處需要注意優先級,雙等於號優先級最高,其次才是與運算
while (count && StringPtr_A && StringPtr_A == StringPtr_B);
return StringPtr_A - StringPtr_B;
}
// 定義全局變量
unsigned __int8 byte_4180D0[8] = { 32 ,0 };
// 逆中層
int __stdcall sub_407130(int array_ptr)
{
int index;
unsigned __int8 *i;
index = 0;
// 此處獲取數組指針,然后與byte_4180D0比較,比較第一位
for (i = (unsigned __int8 *)array_ptr; !sub_4070E0(i, byte_4180D0, 1); ++i)
++index;
// 返回比較后的數組索引
return index + array_ptr;
}
int main(int argc, char *argv)
{
int ref_count = sub_407130(5);
std::cout << &ref_count << std::endl;
getchar();
return 0;
}
逆向還原子過程 sub_4070B0(): 這個過程,主要實現了在指定字節數組中判斷某個字符是否存在
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
// 在指定字節數組中判斷某個字符是否存在
BYTE *__stdcall sub_4070B0(BYTE *byte_array, unsigned char value)
{
BYTE *byte_array_ptr;
char i;
byte_array_ptr = byte_array;
for (i = *byte_array; i; i = *++byte_array_ptr) // 每次取出后一個字符
{
if (i == value) // 判斷字符是否與value相等
break; // 如果存在指定字符,則直接終止循環
}
return *byte_array_ptr != value ? 0 : byte_array_ptr; // 判斷v2,不等於value則直接返回0,否則返回v2
}
int main(int argc, char *argv)
{
getchar();
return 0;
}
不出意外,可以順利通過編譯檢查。
逆向還原子過程 sub_4074C0(): 此子過程相對於上方過程稍微復雜一點,但其實現的目的只有一個,就是從原始位置拷貝字符串放入目標位置,並在結尾處以0填充。
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
// 對字符串的拷貝處理
BYTE *__stdcall sub_4074C0(BYTE *string_dst, char *string_src, int count)
{
BYTE *result;
BYTE *dst_end;
int v6;
char is_null;
result = string_dst;
dst_end = string_dst;
if (*string_dst)
{
while (*++dst_end) // 將目標字符串指針移動到最后面
;
}
v6 = count - 1; // 最后一個元素需要填充,所以索引要減去1
if (count) // 不為0執行
{
do
{
is_null = *string_src;
*dst_end++ = *string_src++; // 取出原字符串 ,並將字符串放入到需要返回的空間中
if (!is_null) // 不為空則繼續
break;
} while (v6--);
}
*dst_end = '0';
return result; // 最后返回指針
}
int main(int argc, char *argv)
{
char dst[257];
char src[257] = "hello lyshark";
// 調用拷貝前3個字符,並在末尾填充0
memset(dst, 0, sizeof(dst));
sub_4074C0((BYTE *)dst, src, 3);
std::cout << "前3個字符: " << dst << std::endl;
// 調用拷貝后三個字符
memset(dst, 0, sizeof(dst));
sub_4074C0((BYTE *)dst, src, 5);
std::cout << "前5個字符: " << dst << std::endl;
getchar();
return 0;
}
經過逆向分析后,我們將其通過VS編譯,並運行測試是否可使用。
逆向還原子過程 sub_4073E0(): 該子過程只實現了一個簡單的字符串拷貝功能。
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
// 簡單實現了字符串拷貝
BYTE *__stdcall sub_4073E0(BYTE *dst, BYTE *src)
{
BYTE *result;
char *src_string_ptr;
bool string_is_null;
BYTE *dst_string_ptr;
char src_string_is_null;
result = dst; // 此處傳指針,a1同樣受影響
src_string_ptr = (char *)src + 1;
string_is_null = *src == 0; // 判斷a2是否為空,字符串是否結尾
*dst = *src;
dst_string_ptr = dst + 1;
if (!string_is_null) // 此處時返回值,往上看,也就說明此處判斷字符串是否為空
{
do
{
src_string_is_null = *src_string_ptr; // 取出指針中的字符,給v6
*dst_string_ptr++ = *src_string_ptr++; // 字符串拷貝
} while (src_string_is_null); // 字符串 a2 不為空
}
return result; // 返回字符串
}
int main(int argc, char *argv)
{
BYTE dst[257];
BYTE src[257] = "hello lyshark";
sub_4073E0(dst, src);
std::cout << "拷貝結果: " << dst << std::endl;
getchar();
return 0;
}
編譯運行,得到輸出。
逆向還原子過程 sub_407320(): 該子過程實現了字符串拼接操作,沒有調用原生strcat函數。
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
// 實現字符串連接操作
BYTE * __stdcall sub_407320(BYTE *dst, char *src)
{
BYTE *result;
BYTE *dst_ptr;
BYTE *v5;
char src_ptr;
char *v7;
char v8;
result = dst;
dst_ptr = dst;
if (*dst) // dst不為空
{
while (*++dst_ptr) // 移動到字符串末尾
;
}
v5 = dst_ptr + 1; // 末尾的下一個位置
src_ptr = *src;
v7 = src + 1; // 指向原src字符串
*(v5 - 1) = *src;
if (src_ptr)
{
do
{
v8 = *v7; // 取出src中的字符,依次給v8
*v5++ = *v7++; // 將V7拷貝到V5 相當於把src連接到dst后面
} while (v8); // 判斷src是否是字符串的結束
}
return result;
}
int main(int argc, char *argv)
{
BYTE dst[257] = "lyshark ";
BYTE src[257] = "yyds 永遠的傷";
sub_407320(dst, (CHAR *)src);
std::cout << "拼接后: " << dst << std::endl;
getchar();
return 0;
}
編譯運行后,看一下 拼接結果把。
至此所有的子過程已經全部恢復完畢,並可以正常使用了,接下來需要恢復子過程的頂層sub_4075C0()
該過程的恢復要比上方復雜許多,我們慢慢來分析吧。
逆向還原中間層過程 sub_4075C0(): 由於該子過程過於龐大,短期內無法直接全部恢復,為防止出現錯誤,我們向上一層,先恢復上一層代碼。
上一層子過程sub_4089A0()
代碼量較少,我們先來恢復這一段。
#include <Windows.h>
#include <iostream>
// 上層調用
int __cdecl sub_4089A0(int a1, int a2, int a3, int a4)
{
HMODULE msvcrt_handle;
HMODULE user32_handle;
FARPROC memset_base;
FARPROC wsprintf_base;
msvcrt_handle = LoadLibraryA("MSVCRT.dll");
user32_handle = LoadLibraryA("USER32.dll");
memset_base = GetProcAddress(msvcrt_handle, "memset");
wsprintf_base = GetProcAddress(user32_handle, "wsprintfA");
char key_[40];
char dst[1024];
memset(dst, 0, sizeof(dst));
((void(__cdecl *)(int, DWORD, int))memset_base)(a3, 0, a4);
((void(__cdecl *)(char *, DWORD, int))memset_base)(dst, 0, 1024);
strcpy(key_, "SYSTEM\\CurrentControlSet\\Services\\%s");
((void(__cdecl *)(char *, char *, int))wsprintf_base)(dst, key_, a1);
std::cout << "拼接注冊表: " << dst << std::endl;
// return sub_4075C0(2147483650, (int)dst, a2, 1, (BYTE *)a3, 0, a4, 0);
return 0;
}
int main(int argc,char *argv)
{
sub_4089A0(1,1,1,1);
return 0;
}
由於該方法過長,我們無需將所有的代碼全部逆出來,直接分析sub_4075C0()
函數參數,將我們需要的分支結構恢復即可。
首先該函數參數return sub_4075C0(2147483650, (int)v14, a2, 1, (_BYTE *)a3, 0, a4, 0);
經分析后如下所示。
#include <Windows.h>
#include <iostream>
int __cdecl sub_4075C0(int a1, int a2, int a3, int a4, BYTE *a5, int a6, int a7, int a8)
{
return 1;
}
int main(int argc,char *argv)
{
char v14[1021];
int a2;
int a3;
int a4;
memset(v14, 0, sizeof(v14));
sub_4075C0(2147483650, (int)v14, a2, 1, (BYTE *)a3, 0, a4, 0);
return 0;
}
根據F5的分析,我們先把大體的循環分支等結構寫出來,因為這是最基本的框架,接着在依次恢復每個分支中的子功能。
#include <Windows.h>
#include <iostream>
int __cdecl sub_4075C0(int a1, int a2, int a3, int a4, BYTE *a5, int a6, int a7, int a8)
{
if (1)
{
// 打開成功執行
}
else
{
// 第一層循環
switch (a8)
{
// 分支0內部
case 0:
switch (a4)
{
case 1:
case 2:
if (1)
{
}
break;
case 3:
if (1)
{
}
break;
case 4:
if (1)
{
}
break;
case 7:
if (1)
{
for (int x = 0; x < 1; x++)
{
}
}
break;
default:
break;
}
break;
case 1:
while (1)
{
if (1)
{
break;
}
}
break;
case 2:
while (1)
{
if (1)
{
break;
}
switch (a4)
{
case 1:
case 2:
case 3:
case 4:
case 7:
break;
}
break;
}
break;
case 3:
break;
default:
break;
}
}
return 1;
}
int main(int argc,char *argv)
{
char v14[1021];
int a2;
int a3;
int a4;
memset(v14, 0, sizeof(v14));
sub_4075C0(2147483650, (int)v14, a2, 1, (BYTE *)a3, 0, a4, 0);
getchar();
return 0;
}
由於代碼中大量使用了動態獲取API函數地址,所以為了還原簡單,我們將直接調用API實現功能,不在使用GetProcAddress獲取動態地址調用。
筆者正在抽時間分析,恢復代碼,(最近很忙,只能慢慢來了),兩周后繼續分析。