XCTF-csaw2013reversing2

三種做法

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開始輸出的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章