三種做法
1、ida靜態分析修改指令
main函數反編譯的代碼
由於運行之後的是亂碼,所以可以猜測生成flag的函數沒有執行,所以需要跳到生成flag的函數執行,但是前面的中斷函數不能執行,需要nop掉,並且後面退出程序的函數不能執行,需要跳到彈框函數繼續執行。(修改的路徑和文件名不要有中文,我用ida修改的時候踩了坑,大家可以試一試)
ida修改代碼的方法:
1、鼠標停留在要修改的彙編代碼上,然後點擊Edit > Patch program > Assemble(中文:編輯 > 修補程序 > 彙編)
2、修改完成後:Edit > Patch program > Apply pathes to input file > OK(中文:編輯 > 修補程序 > 修補程序應用到輸入文件 > 確定)
修改之前的彙編代碼
修改之後的彙編代碼
修改完成之後,直接運行文件,得到flag
2、ollydbg動態調試,nop大法
將文件導入ollydbg後,直接右鍵 > 中文搜索引擎 > 智能搜索,找到Flag
雙擊之後向上找到IsDebuggerPresent函數,點擊這句彙編,F4讓程序運行到此處
F8兩次,發現一個跳轉,根據之前ida的分析,這應該就是那個if語句的判斷,跳過的中間部分就是生成flag的函數,所以我們把這個跳轉nop掉
繼續F8執行,執行到int 3,這是中斷語句,所以也nop掉
F8執行完生成flag的函數後,後面有一個大跳轉,跳到退出程序的函數
所以我們把這個跳轉也給nop掉,繼續F8,執行完一個MessageBoxA(彈框)函數後,發現程序此時處於Running狀態,彈出一個什麼也沒有的框,其實這是另外一個彈框函數,真正輸出flag的彈框函數是後面那個,在我們之前那個ida的修改之後的彙編圖也可以發現,確實是有一個沒有被調用的彈框函數,所以我們之前可以那個nop掉的跳轉改爲跳轉到下面那個彈框函數,但既然說了是nop大法,就nop到底
點擊中止之後,發現又要執行一個跳轉,跳過了我們真正的彈框函數
將這個跳轉nop掉,接着F8,就可以看到flag了
3、 分析代碼寫腳本
main函數代碼
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // ecx
CHAR *lpMem; // [esp+8h] [ebp-Ch]
HANDLE hHeap; // [esp+10h] [ebp-4h]
hHeap = HeapCreate(0x40000u, 0, 0);
lpMem = (CHAR *)HeapAlloc(hHeap, 8u, MaxCount + 1);
memcpy_s(lpMem, MaxCount, &unk_409B10, MaxCount);
if ( sub_40102A() || IsDebuggerPresent() )
{
__debugbreak();
sub_401000(v3 + 4, lpMem);
ExitProcess(0xFFFFFFFF);
}
MessageBoxA(0, lpMem + 1, "Flag", 2u);
HeapFree(hHeap, 0, lpMem);
HeapDestroy(hHeap);
ExitProcess(0);
}
關鍵函數sub_401000的兩個參數,v3後面沒有用到,向上找lpMem的賦值語句,memcpy_s,將unk_409B10地址的值給了它,雙擊查看
進入sub_401000函數內部,代碼
unsigned int __fastcall sub_401000(int a1, int a2)
{
int v2; // esi
unsigned int v3; // eax
unsigned int v4; // ecx
unsigned int result; // eax
v2 = dword_409B38;
v3 = a2 + 1 + strlen((const char *)(a2 + 1)) + 1;
v4 = 0;
result = ((v3 - (a2 + 2)) >> 2) + 1;
if ( result )
{
do
*(_DWORD *)(a2 + 4 * v4++) ^= v2;
while ( v4 < result );
}
return result;
}
a2也就是lpMem,發現後面的異或語句有v2,向上找v2的賦值語句,找到v2 = dword_409B38,雙擊dword_409B38,找到內容
這裏是四個字節顯示的,又由於小端存儲,所以順序是顛倒的,我們可以將其轉換成一個字節查看
然後根據源碼寫腳本,寫的有點不太明白,記錄一下
代碼參照文章:https://www.cnblogs.com/DirWang/p/11420740.html
x=[0xbb,0xaa,0xcc,0xdd]
y=[0xBB,0xCC,0xA0,0xBC,0xDC,0xD1,0xBE,0xB8,0xCD,0xCF,0xBE,0xAE,0xD2,0xC4,0xAB,0x82,0xD2,0xD9,0x93,0xB3,0xD4,0xDE,0x93,0xA9,0xD3,0xCB,0xB8,0x82,0xD3,0xCB,0xBE,0xB9,0x9A,0xD7,0xCC,0xDD]
i=0
z=[]
while i<len(y):
t=chr(y[i]^x[i%4])
z.append(t)
i+=1
print(z)
print(''.join(z))
['\x00', 'f', 'l', 'a', 'g', '{', 'r', 'e', 'v', 'e', 'r', 's', 'i', 'n', 'g', '_', 'i', 's', '_', 'n', 'o', 't', '_', 't', 'h', 'a', 't', '_', 'h', 'a', 'r', 'd', '!', '}', '\x00', '\x00']
flag{reversing_is_not_that_hard!}
這裏就可以知道爲什麼調用第一個彈窗會輸出空白,因爲第一個彈窗函數,是直接從第一個字符輸出的,但是第一個字符解碼後爲'\0',直接截斷,所以會輸出空白,第二個彈窗是從lpMem+1開始輸出的