1. 程式人生 > >OD CE找資料總結(上)

OD CE找資料總結(上)

一、找點選選怪物call以及選怪狀態標誌

1、ce搜未知資料,選擇一個怪,搜變動,換一個,再搜變動,最後找到最像的檢視訪問地址程式碼,記下後,od附加跳轉下段,返回上一層,用注入軟體測試一下。

2、找到後,記下call上下程式碼和call內部程式碼,遺蹟ce搜到的程式碼,方便以後定位基地址。

3、一般引數是存放怪物id等資訊的地址和另外一個引數(可能是用來存放計算得到的怪物id等資訊,就是ce搜到的存放怪物id的記憶體地址) 向上找,一般格式是mov[變數地址]數值。

4、和選擇怪物call相關的動作,ce搜到的資料就可以直接od跳轉到除錯,向上找就能找到相關的動作call。

5、或者直接bp send。

6、ce搜出來的所點選怪物的id的地址,完後通過它od裡找到的是一個call吧。某處地址的資料存入了ce找到的地址裡面了。具體這個某處地址的資料是怎麼來的,沒點選肯定啥也沒有。

點選了才會在某個地方計算,點選的座標和怪物的座標等等資料,計算結果才會算出這個怪物的id。具體估計是在某個庫裡的call裡面計算,所以你找不到的,點選選怪call,如果要自動執行,就必須先知道要攻擊的怪物的ID等資訊,所以你找哪裡把怪物id存放到那個地址裡,不是白費功夫嗎。

選怪狀態標誌:

查詢經驗:

1、有些遊戲如天龍八部,每次調試出現錯誤,再啟動,遊戲的反彙編指令地址和特徵碼都在變動,所以每次找到重要資料,都要記錄反彙編指令和特徵碼還有所在模組名字。

2、選擇怪物,ce搜未知初始化資料,完後不選擇怪物,搜變動,來回搜 完後跳躍,打坐,搜未變動,幾個回合後,剩下四五個了,完後選擇一個 找訪問的指令,寫入的也可以,完後od跳轉向上層找來源,發現最後都會有個來源的地址的資料不停地變動,而且那層call不停的斷下來,ce也搜不到訪問的指令,那就說明只有使用者選擇了怪物,才會有資料存進那個地址 不然就是不停地變動(可能被其他指令訪問存取)。

3、第一種情況遇到後不著急,ce檢視另外一個搜到的結果,檢視訪問的程式碼 完後od跳轉,是這種如上圖指令:mov ecx,dword ptr ds:[esi+0xC],完後發現是個二叉樹遍歷,完後發現是個二叉樹陣列 。

數組裡每個二叉樹的指定偏移都是選中怪物狀態的標誌,od不斷下來,那些標誌和資料也不變化,那就找到了。完後找樹根,比如剛才那個指令下斷點,完後記憶體視窗更隨 完後ce搜包含esi的地址,完後搜。

包含esi地址的地址, 以此類推,找到了00745410  |.  8B30          mov esi,dword ptr ds:[eax] 完後發現eax來源ds:[edi+0x10] edi是基地址0x986618 斷下來後資料不變動,那就找到了。


二、尋路call 以及 座標加密

1、ce搜尋未知、跑動、改變、不跑、不改變。

2、一般人物跑動狀態是bool a=0 1 或者int a = 0不動 1跑動 2 走動 一般搜尋0到2之間數字 有可能1是不動 0是跑。

3、ce找到小數字,檢視寫入的程式碼,此時ce可能搜尋到有很多個貌似得記憶體地址,一定要看跑的時候往裡面寫入的指令,比如跑的時候寫入0,就監視器中找mov byte ...0之類的語句,選擇幾個,檢視寫入的程式碼,首先看遊戲跑一下,監視器中有沒有多彈出來的某條指令,如果有,那看是不是mov byte 之類的,因為可能是bool值,一個位元組。

如果沒有多彈出來的指令,就看每條指令類似mov byte,或者直接吧ce搜出來的地址,鎖住,看遊戲能跑動不,一般不能跑動的就是要找的了。記下來 od跳轉,完後下段 ctrl+f9上一層看這個call引數有沒有座標數值或者是存座標的結構地址 或者把該call nop掉看看。

4、完後一般尋路call,都是先讀取障礙物圖call,完後檢視座標是否在障礙物上,完後是尋路call(武林外傳的)。

5、call引數如果是個地址,一定要在call內部看看這個地址,哪些偏移被訪問了,沒有斷下來的時候裡面存的是不是被訪問的資料,如果沒有,說明其他地方把資料寫入了這地址的偏移裡面了,還要再找找哪裡寫入的。

6,座標可能加密,一般是SHL  SHR。

7,如果座標加密,當前尋路call下段後,引數裡存放的地址,跟隨進去後看到的是加密後的座標,所以要檢視哪裡寫入了這些座標 一般下斷點後,斷下來的是讀取現在的座標完後,加每一步的偏移,最後寫入那個地址裡 而我們要找到的啥時候把現在的座標寫入的,所以要返回人物畫面,重新進入遊戲 斷下來後一步步找。


三、技能call

1、ce搜未知,選擇怪物,搜變動,沒選擇,搜0。

2、訪問程式碼,雙擊怪物,看ce數量為1的程式碼,od跳轉,一般格式是

while(1):

{
     判斷滑鼠的目標
   if(人物在攻擊狀態)
        讀取目標id
        普攻
  
}

3、以上找不到,就在技能欄放兩個技能,ce搜尋未知初始化資料,完後把第一個欄的技能移動到別的欄,完後ce搜0,完後第一個欄放上一個技能, 再搜變動,直到找到幾個類似資料,選擇較大的ce搜到的三個,最小的數字是技能序號,只檢視訪問大的的程式碼。

檢視訪問的程式碼。開啟監視器。遊戲裡掉整一下技能位置。看監視器跳出的指令,記下od查詢到技能陣列,完後陣列第一個元素是地址 ce檢視方位這個地址的指令,od跳轉到地址處,完後附近call裡面的某個call就是技能call,一般引數0或者1比較多,有個引數是可以隨著技能不同而變化。

4、也可以bp send,完後向上跳轉,點選od上的k鍵,複製下,完後每個call都下斷點,沒有使用技能就斷下來的,就pass掉,完後剩餘的nop掉再恢復,看看有沒有任何動作,如果有的話,就基本不可能是了。接著向上層call,按同樣方法nop再恢復,看有沒有啥動作,如果一點都沒有,就先在這個call裡面開始,遇到的每一個call都同理nop掉在恢復,看有沒有動作,沒有的就進入同樣道理直到找到。

 

發包結構:
sctruct  發包結構
{
    dword starsddress
    dword lastaddress 1,ce哪裡改變
}
void 發包執行緒()
{
    while(1)
      if(結構.starsddress==lastaddress)
      {
          continue;
      }
      else
      {
          send();
         結構.starsddress=lastaddress      
     }
}
void  封包寫入(int 長度 ,dword  地址)
{
    結構地址= 結構.staraddress
    int  i=0
    while(長度!=0)
    {
        結構地址[i]=地址[i]
        i++
    }
   結構.lastaddress = 結構.startaddress+長度  2,跳到這裡
}
 
 
 
struct  目標技能結構
{
   bool  是否普通攻擊
   dword  id
   dword  x
   dword  y
   dword objectID
}
void 目標攻擊封包組合函式()
{
    組合封包
    風暴寫入(長度,地址)  3,返回這一層
}
void  迴圈技能()
{
   while(1)
  {
         if(目標技能結構.是否普通攻擊==true)
          {
                 普通封包組合函式()4,返回到這一層
          }
          if(目標技能結構.objectID !=0)
          {
                 技能封包組合函式();
                if(目標怪物是否死亡)
                    目標技能結構id =0;
          }
  }
}
普通攻擊call()
{
}

 

五,滑鼠點選普通攻擊call

1,有時候k堆疊表啥都沒有,換個od,或者ollyice(大陸od改進版) 刪掉除錯udd檔案再試試。

2,ce搜怪物id記憶體地址,檢視訪問的程式碼,返回遊戲,攻擊怪物,完後在監視器中檢視,最先出來的指令(實在不行就看下一個出來的指令) 完後od跳轉,看是不是該條指令下面吧這個怪物id訪問出來後,存進了別的記憶體地址,完後ce 搜這個記憶體地址,檢視訪問的指令。

完後,再去攻擊怪物監視器中檢視,最後出來的訪問的指令(不行就倒數第二齣來的 以此類推) 完後od跳轉,看指令下面的call,進入call開頭下斷點,返回後刪掉這個call,再攻擊怪物,看行不行,有沒有任何動作(如果死屏的話,估計不是這個call了)完後撤銷修改,再看有沒有什麼動作,有的話估計也不可能是了。

3,bp send 下段後返回,檢視堆疊表,複製下來,找到最後的的call(nop掉不能有任何多餘動作) 完後在這call裡裡面,分三層方法:

進入call後逐一排除,只要nop掉,什麼動作都沒有就行,至於恢復後有什麼動作無所謂。

nop掉後,看游標,如果有戰鬥的游標,點選後不打怪物,的話,可能就是他了。

進入後檢視複製的堆疊表,看看哪個call在這個call的裡面,完後在他附近找call,nop 一下看看。

 

六,揹包陣列和揹包物品名稱

 

1、

struct Goords
{
 DWORD ID;
 char name[32];
 int num;
}
可以ce搜 物品id  物品名字  或者物品數量

d1911b526f83

 

2、提示vp protect 程式發生未知錯誤,重新啟動遊戲不登陸,重新開啟od 完後提示錯誤, 完後退出遊戲, 在登陸游戲可解決,b不行,就 結束qqprotect,或者od程序,或者換個od。

3、ce搜到藥品名字,修改的時候不要改資料型別為text 再修改,會出錯。

4、od向上找基地址的時候,一定要注意每次檢視的找到的每次偏移,不下斷點的時候,還有沒有資料,比如找血基地址 [esi+8c]+c 一定要先看+c這個地址有沒有原來的資料,完後看esi+8c 裡面有沒有原來的資料,沒有的話就要查詢哪裡寫入了。

5、搜尋物品名陳,有時候要搜,金創藥(50) 的unicode編碼。

6、有時候要用ce搜到的的指令(搜物品名字的unicode 完後檢視訪問,當滑鼠移動到物品上,監視器中彈出來的指令 記下) od跳轉過去,遇到[local.x+xx] 或者堆疊記憶體都存在的,雖然不斷下來,裡面的一直在變化 ,也可以通過向上條件斷點[local.x+xx]==xx 斷下來,可以直接記下地址,完後[0x245678c]==xxx 245678c。

可以使記憶體或者堆疊地址,如果此時裡面資料是要找的資料,說明還要往上看往上找斷下來沒有要找的資料,就是這裡存進去的。

7、我猜可能是因為當我滑鼠移動到這個物品的時候才會顯示物品名稱。此時,是更具游標位置,算出移動到哪個格子,才會算出哪個物品,才會顯示出名稱。

也就是說,此時你根據ce搜尋訪問那個地址(ce搜到的儲存物品名稱比如金創藥)的程式碼,完後滑鼠移動到金創藥上,此時ce監視器中跳出幾個mov ax,[ebx] 完後od跳到這裡,向上找ebx來源,最終卡到一個地址處,裡面即使不斷下來,也是很多資料不停地變動。

因為此時你沒有吧滑鼠移動到金創藥上,所以這個地址裡面的指標式垃圾指標,只有當你滑鼠移動到金瘡藥上的時候,某個call才會根據游標位置算出具體是在哪個物品上,完後把這個物品的關係地址發到,那個記憶體地址裡,你才會看到,但是因為這個地址裡的資料不停地被一些垃圾資料寫入,你下斷點會無數次斷下來,ce搜尋出一堆,根本不知道當你滑鼠移動到金創藥上是哪裡,寫入資料到那個記憶體地址裡。

8、實在od遇到第七的情況,找不到名稱基地址,就ce遍歷搜尋物品陣列取出來的物件。

9、通過搜尋揹包物品的數量,比如說揹包裡面有個紅藥有100個,那麼,我們就用CE搜尋100。

然後吃掉一個,改變它的數量,再搜尋99,以此類推,最後會得到一個或者幾個數值,剩下的就慢慢測試吧。

10、用物品在揹包格里面的位置來搜尋,比如說紅藥在揹包第一個格子裡面,我們用CE就搜尋未知初始化數值。

然後改變紅藥在揹包的位置,移動到其他位置,搜0,移動回來,搜改變的數值,以此類推。 (c語言程式設計的特性,物品存在即大於0,不存在為0)

11、ce搜尋儲存物品名字的地址要範圍搜尋,因為物品名字前面可能還有一些字串和它是一體的,完後在結果中找最大的記憶體地址。完後檢視訪問這個地址的程式碼,如果沒有看到訪問的指令,那就搜尋儲存這個物品名字的記憶體地址,可能是訪問了這個儲存物品名字的指標,完後吃一下藥物,看看有沒有顯示出來的指令。

12、ce搜尋物品名字,最好是把文字寬度設定的寬一點,可以看有多餘的字元出現,可能性就少了,比如:金創藥</seg><obj>。

13、可以先找到吃藥call,進入後分析找到從物品陣列取出的物品結構call。

14、ce搜尋名字的地址,要範圍搜尋,因為物品名字前面可能還有一些字串和他是一體的,完後結果中找最大的記憶體地址,完後檢視訪問這個地址的程式碼 如果沒有看到。

訪問的指令,那就搜尋儲存這個物品名字的記憶體地址,可能是訪問了這個儲存物品名字的指標,完後吃一下藥物,看看有沒有顯示出來的指令。

 

七、人物角色姓名藍色怒氣血量

1、條件斷點 eax == a247080 對嗎。

2、mov ecx,dword ptr ss:[ebp+0x1C] ebp到上層發現是00ce7cf0,其實只要此時發現程式名字.00ce7cf0 就是基地址了,但是ebp=00ce7cf0,不是ebp = [00ce7cf0] ,別搞錯了。

3,遇到這種指令mov ecx,dword ptr ss:[ebp+0x1C],彆著急檢視哪裡寫入資料到[ebp+1c]裡面了,而是要看沒有斷下來,此時裡面的資料還是不是原來的資料,如果是的話,那就找ebp來源。

4,ce搜素血量完後被怪物打,檢視變動的數值,完後修改一下數值,看看修改哪個後血量變動,就是它了 (搜出來的綠色的地址也可以 但是修改數值後不能改動人物血量)。完後檢視寫入的指令,再被怪物打,完後監視其中看最後的類似於mov [],eax這種。完後od跳轉,向上跟, 最後找到後 +4 +8等位置一般為怒氣,或者其他相關。

5、ce檢視寫入的指令的時候,比如遇到mov dword ptr ds:[esi+0x278],edx  找的是esi的來源,而不是edx的來源切記。

找人物名稱經驗:

1,ce搜尋寬字元(unicode)或者窄字元(g2b) unicode:3575505b386e67720d54cf65。

2,ce搜尋後找mov ax,word ptr ds:[ecx+ebp*2]這種型別的,一般都是陣列形式取出來的,或者不行換一個試試。

3,ce直接檢視訪問的指令(沒有經過附加條件比如滑鼠點一下完後才檢視ce監視器), 完後od跳轉下段,無限斷下來,這種情況下就不用條件斷點 直接斷下來向上找。

 

 

八,找明文包

 

1、

006C5802  |.  8B06          mov eax,dword ptr ds:[esi]
006C5804  |.  8B4E 04       mov ecx,dword ptr ds:[esi+0x4]
006C5807  |.  3BC1          cmp eax,ecx
006C5809  |.  74 1A         je short elementc.006C5825

 

找到類似的 完後esi+4的地址下記憶體寫入斷點。


2、在send call哪裡下入斷點,檢視data地址處的記憶體,隨便下一個寫入斷點,od跳轉後,到開頭看看。


3、弄兩個相同動作,檢視堆疊表,哪些些call一樣,對比出來最後一個一樣的call。
 

 

 

九,根據技能和普通攻擊分析出遊戲大概的迴圈攻擊功能框架

 

1、

struct 發包結構
{
****
DWORD STARTADRESS;
DWORD LASTADRESS;
****


}

發包結構 結構;

void 發包執行緒()
{
 while(1)
 {
   if (結構.STARTADRESS == 結構.LASTADRESS)
   {
 contiune;
   }
else
{
 send();//發包 ①bp send先斷下來 完後上下找到+4 +8的對比發包結構是否有資料的地方 完後下寫入斷點
 
 結構.STARTADRESS = 結構.LASTADRESS;//將封包結尾地址賦值給首地址
}
 }  
}

void 封包寫入(int 長度,DWORD 地址)③在這裡下斷點
{
結構地址 = 結構.STARTADRESS;
int i = 0
while(長度!=0)
{
 結構地址[i] = 地址[i]; //將要傳送的封包寫入到結構地址當中去 ②跳到這裡
 i++;
}

結構.LASTADRESS = 結構.STARTADRESS + 長度

}

struct 目標技能結構
{
   bool 是否普攻攻擊;
DWORD ID;
DWORD X;
DWORD Y;
DOWRD OBJECTID;


}

void 普通攻擊封包組合函式()⑤在這裡下斷點
{
組合過程
 
封包寫入(長度,地址) ④返回到這裡

}

void 技能攻擊封包組合函式()
{
組合過程
 
封包寫入(長度,地址)

}
void 迴圈技能()
{
   while(1)
{
 if (目標技能結構.是否普攻攻擊==真) ⑦向上找到迴圈 檢視條件 發現只有等於真 才會發普攻包  於是在技能結構下斷點檢視什麼時候把真 寫

入的
 {
    普通攻擊封包組合函式()   ⑥返回到這裡
     
     
  if (目標怪物是否死亡)
  {
   目標技能結構.ID = 0;
  }
 }
  
  
  
 if (目標技能結構.ID != 0)
 {
  技能攻擊封包組合函式()
   
  if (目標怪物是否死亡)
  {
   目標技能結構.ID = 0;
  }
 }
 
}
}


void 普通攻擊CALL()
{
目標技能結構.是否普攻攻擊 = 是    ⑧在這裡斷下 向上找到普攻call
目標技能結構.OBJECTID = 讀取目標ID
}


void 技能CALL(DWORD ID,0,0,0,0)
{
目標技能結構.OBJECTID = 讀取目標ID
目標技能結構.ID = ID
}

 

2、bp下斷點,完後向上層,複製堆疊k表資料,完後找到上層第一個不停斷下的call的下一層call,完後nop掉,如果此時按技能鍵沒有任何效果, 撤銷nop,看看有沒有變化。

 

如果有,那麼遊戲的功能call和封包組合call可能不在一個支線上。可能在別的岔道口的支線上,因為根據迴圈技能框架分析知道,我們剛才找到的第一個無限斷下來的call的所在的call可能是一個無限迴圈判斷是否有技能發生,他的判斷條件就是看看某些結構等東西里面是否有某個標誌,有的話就會呼叫封包組合函式,而我們剛才說的nop掉的那個call是一個封包組合call,或者是封包組合call和功能call的集合。

 

因為此時撤銷nop發現,還是有相關動作發生,說明剛才說的那個結構裡面的某個標誌還存在,所以迴圈技能不停地呼叫這個被nop掉的call。你撤銷nop,他正好呼叫了,也說明這個call只是封包組合call,不包含功能call 因為只有功能call才會把那個標誌寫入某個結構裡,完後就在剛才被nop掉的call下斷點,完後檢視引數(或者向上看看有沒有跳轉指令,跳過這個被nop的call的,完後斷下後和撤銷斷點後看看裡面的值有沒有變化。

 

注意:這裡的跳轉指令類似於技能框架裡的判斷某個結構裡的某個標誌是否存在一個值。如果存在就執行功能封包call,完後就是可能在功能封包call裡寫入資料到發包結構裡,完後傳送封包,最後就是恢復那個結構裡的某個標誌為某個值,也可能是在剛才說的被nop掉的封包組合call的下面恢復那個結構裡的某個標誌為某個值,具體看這個被nop掉的call下面還有沒有類似mov指令。

 

如果都是pop之類的那恢復那個標誌的指令就存在於剛才說的被nop的那個封包組合call裡面支線)  記憶體視窗跟隨進去,完後撤銷斷點,看看裡面哪些值發生變化了。完後ce搜尋檢視寫入的指令,完後od跳轉,如果此時看到的只是一些簡單的賦值指令。估計不是功能call,那就向上層返回,感覺像功能call的話,就nop掉看看,沒有任何動作的話,估計就是了 。

 

而且一般像拾取call的話,引數比較少,一般就一個call就可以完成。 找到這個call後,注入測試如果那個被nop掉的call後,在撤銷nop 發現沒有任何動作,那麼就測試這個call看看是不是功能與封包組合call的集合call 或者進入這個call再細找詳細哪個是功能call,注意剛才說的 一般拾取這些call 引數少 一個call就可以實現。


遇到這種call doword ptr ds:[eax+0c]斷下來無線斷的情況,在他的下層call裡找條件判斷,檢視哪裡跳過那個下層call,按照上述方法也可以,此時注意每次檢視跳轉標誌位的值是否改變,要下斷點檢視的。

 

注意每次斷點後這個標誌位記憶體地址的值還是不是一樣,不一樣就不行了,有時候不好找到正確的標誌位的話,可以看看哪些跳轉地方的標誌位的記憶體地址的值每次執行某個功能(比如普攻)的時候就會發生改變,不執行這個功能就不改變的話,就ce查哪裡寫入了這個改變的值。 

 

od跳轉後也可以向上層按照上面方法找到,是在找不到標誌位的話,可以試試啥時候啥條件成立就會把eax設定為0049C883,比如只有某個記憶體(標誌位)裡的值為1的時候就會把eax的值變為0049C883,那麼就找到標誌了 剩下就是ce檢視哪裡寫入。

 

 

十,怪物座標、陣列等

 

<座標>


1、ce搜座標換文字,換位元組,搜不出來就是加密了,就是230,經過加密變成256032。


2、od跳到程式碼處,向上找,注入工具測試,完後找到以後,進入call,一個看找類似235200這樣的數字,完後ce搜尋寫入地址的程式碼,找到加密來源。


<陣列結構>


1、我最常用的就是通過怪物的血量來找,搜尋怪物的血值方法和上面類似。


  2、通過怪物的名字搜尋,這個方法比較麻煩,有時候要看運氣。而且還要看怪物名字是什麼型別的,文字/UNICODE,搜一種怪物名字完後跑向另外一種怪物,完後檢視那些記憶體地址變為這種怪物,完後選幾個檢視寫入地址。


  3、可以通過人物於怪物的距離。


  4、可以通過怪物的等級。


  5、選怪call的引數分析。


  6、按照技能結構分析的經驗 找到選怪call。


  7、進入單步執行,找到第一個call,返回一個從陣列中選出來的怪物id,完後進入這個call查詢從哪裡返回出來的,直到找到一個數組。

 

轉載自 看雪學院 公眾號