簡而言之,有一天我很無聊,請我的朋友Maximus Hackerman給我一些要做的事情。迅速,他通過一個簡單的可執行文件發送,稱為“ reververeme.exe”(virustotal鏈接),簡單地評論了“在您自己的應用程序中打印隱藏的消息”。自然,沒有提供CPP標頭文件。運行可執行文件後,它只是作為基於控制台的應用程序打開的,打印“發送了所有魔術數據包,很快就會退出,蜂鳴器!”然後幾乎立即退出;我想“很快”是主觀的。
幸運的是,似乎沒有反欺騙,所以我想哈克曼當天感覺很好。
連接windbg和一個拆卸器後,我們可以看到應用程序拋出的,最初似乎是divide by zero 。
但是,經過仔細檢查,我們發現在應用程序打印其唯一可見消息之前就拋出了此例外。重要的是要注意,在此之前,有兩個VEH(矢量範圍的異常處理程序)略微註冊,因此此例外很可能是故意的,並用於扭動控制流。便宜的技巧真的要試圖把我們趕下去!
忘記車輛目前,可以肯定地假設,如果應用程序正在“發送”數據包,那麼它是使用winsock send函數來執行此操作的(儘管該應用程序清楚地說它們是“魔術”數據包,所以我們會看到)。
正如預期的那樣,事實證明,您無法通過魔術發送數據包,因此Winsock send功能確實已導入。
通過在發送函數上放置斷點,我們可以查看發送時是否有任何相關數據,我們可以在發送時抽象。可悲的是,當緩衝區加載到功能中時,數據已被加密。但是,我們可以確定緩衝區的長度始終為3個字節,插座始終綁定為0x69 ( NICE )的硬編碼值,並且發送函數斷點總計達到14次。
有幾種方法可以解決上述加密。一種是將其完全逆轉,這可能是很大的努力,另一個是在加密之前找到所需的數據並在加密之前將其抽象。後者比前者要容易得多,因此我們要做。不過,請隨時扭轉加密,我簡要介紹了,這並不令人恐懼。另外,如果您覺得很花哨,則可以始終覆蓋套接字處理值並與之達成接收申請。
可悲的是,除了最初的消息外,只讀數據段中沒有有用的字符串,因此從那個方面沒有有用的指針!
回到車輛,我們可以看到兩個註冊的處理程序都用於將一些未知對象複製到分配的內存緩衝區中。這似乎是通過使用memcpy來完成的,將函數用作第二個參數,進而使用硬編碼整數作為其第二個參數。值得注意的是,這些硬編碼值在任何時候都不超過14。我只在屏幕截圖中包含了其中一輛車,因為除了硬編碼值外,它們基本相同。
通過在一個memcpy函數之一上進行斷點並檢查第二個參數中的子函數,我們可以看到硬編碼整數(以下示例中的13 / 0DH)設置為A1參數的第一個字節,而A1+1包含一個字符,就在指針被指出後。
如果我們檢查其他14個對此功能的調用中的一些,則可以找到相同的行為重複;將字符從1到14安排,使用相應的數字作為訂單的指標,我們可以看到它們開始拼寫一些清晰的單詞。現在,一旦我們知道它是什麼,我們可能會變得超級懶惰,只是製作一個控制台應用來打印出消息,但這確實感覺就像作弊。另外,消息可能在某個時候發生變化。因此,讓我們編寫一個代碼洞穴以在加密之前攔截它們。
抱歉,但是我們正在使用C ++!如果您希望使用C#,請隨時通過整個平台調用970萬個功能,然後完成後回到這裡。無論如何,首先,我們需要決定在哪裡編碼洞穴。幸運的是,我們已經知道!通過分析上述函數,無論多麼簡短,我們就知道,通過突出顯示的點, RDX包含索引, RDX+1包含相應的字符。以下是討論功能的裝配代碼。
現在,從邏輯上講,從mov [rsp+arg_8], rdx最佳的跳躍位置是我們並不真正在乎第三個參數,但是確實想攔截RDX寄存器和RDX+1 。要做這個孩子,我們將需要一些字節:10個字節用於MOV指令,將代碼洞穴的地址移至寄存器(我們將在10點新聞中使用RAX ,更多地使用此內容),而JMP指令中的2個字節為2個字節,以跳到寄存器。對於那些擁有進一步數學的PTSD的人,總計12個字節。現在,在我們去所有貝西姨媽並用writeProcessMemory上劃出該代碼之前,我們需要考慮到上面的組件中沒有理想的位置可以替換12個字節。如果我們想從偏移0x2905 ( mov [rsp+arg_8], rdx )跳躍,並且我們需要12個字節才能這樣做,那麼這需要我們來抵消0x2917 ,這是兩個MOV指令之間的Smack Bang。不幸的是,如果我們只是簡單地將我們的字節寫入那裡,它將完全堵塞組裝,並可能引起一些,呃,“有趣”的副作用。結果,要添加一些單字節說明以填充填充物,然後將其四捨五入到指令結束時,這將變得更容易(也許更刺眼,對不起)。歡迎在0x90。
無論如何,現在我們知道了我們的計劃是什麼,所以讓我們編寫一些代碼:提示Intense Hacker Man Music !
下面是一旦字節寫入應用程序的組件,最初的跳到我們的代碼洞穴的跳躍將看起來像,並帶有自己的NOP幻燈片。
但是,在我們實際編寫和替換任何組件之前,我們需要以暫停狀態啟動該應用程序;這將在早期階段停止應用程序運行時,以便可以在應用程序進入我們感興趣的指令之前進行內存更改。下面的代碼提取物顯示了此過程,我不會在此回購的源文件中查看它,並且它大多對其進行說明。
現在,該過程已經產生,我們需要獲取該過程的基本地址。通常,您可以為此使用EnumProcessModules,但是由於我們立即暫停了主處理線程,PEB不包含填充的PEB_LDR_DATA結構,特別是InMemoryOrderModuleList ,因此我們目前無法獲得基礎地址。順便說一句,這似乎並未在MSDN上的任何地方進行記錄。幸運的是,這相對容易繞過。通過很快恢復過程,查詢模塊,然後重懸於過程中,我們可以獲得所需的信息,而無需進步太大。與Windows中的大多數內容一樣,Microsoft喜歡重申其操作系統是優越的,而不是再次記錄所需的功能: NtSuspendProcess和NtResumeProcess 。很方便地,我父親是比爾·蓋茨(Bill Gates)的朋友,他告訴我這些功能在ntdll.dll中耦合在一起,因此我們可以使用我之前完成的以下課程來獲取它們:
現在,我們擁有所需的兩個函數,我們可以恢復過程,查詢模塊並重新懸浮該過程:
您可能想知道,為什麼WARY循環等待兩個模塊發現而不是一個模塊?好吧,微軟希望通過也沒有提到EnumProcessModules找到的第一個模塊是Ntdll.dll,而是在無證意大利麵條網的背面獲得肉丸的成績,第二個模塊將是可執行的。雖然這聽起來很合理,但是一旦找到可執行文件,它將與ntdll.dll交換索引。這是一個例子:
僅查詢第一個模塊後的結果:
查詢兩個模塊後的結果:
在進行進一步之前,我們需要編寫將初始跳躍到代碼洞穴的彙編代碼,而代碼洞穴本身。從本質上講,我們將從偏移0x2905開始覆蓋一些內存,跳躍,進行代碼洞穴間諜活動,然後跳回0x2911 ,以繼續正常的程序流。最初,我們將地址轉移到RAX寄存器為MOV RAX, 0x0 ,因為我們會跳到的地址是動態的,我們還不知道它是什麼。 RAX是一個安全使用的登記冊,因為它的揮發性寄存器,並且無論如何在不久之後都被覆蓋。有趣的事實,這就是任天堂對原始Mario Bros平台遊戲編程的方式,並有很多跳躍(告訴我我很有趣)!以下是編譯器中代碼的外觀;它可以以其他方式創建,但是我選擇將所需的彙編指令分為字節碼。如果您想在家中這樣做,請使用此網站。
實際代碼洞穴的代碼更為複雜,並且在此文件中也註釋了它的邏輯,但這是粗略的過程:
R10RDX的下部移至R11BRDX的第二個字節移入R11B+12 )到R10的地址(是0x2911 )R10我們還需要將覆蓋(NOP幻燈片)覆蓋的彙編代碼重寫到我們的代碼洞穴中,以保留堆棧等。該代碼在“ Predetermined Assembly ”區域中引用,不包括NOP。以下是醜陋的榮耀中的代碼洞穴:大部分都是困難的部分。
在這一點上,我們實質上有我們需要將隱藏消息從內存中刪除的一切,我們只需要實現它即可。回顧一下,我們有一個懸掛過程的句柄,代表彙編邏輯的2個字節數組,懸掛過程的基礎地址,存儲在modules[0]中,以及我們需要編寫跳躍邏輯的位置的偏移。下面的代碼段創建要跳起來的地址,代碼洞穴的地址(跳至),我們的3字節內存存儲的地址,將地址寫入彙編代碼,然後在恢復暫停過程之前將地址寫入彙編代碼:
codeCaveStorageAddr的神奇的哈利·波特風格鑄造是將地址轉換為字節,並且循環中的硬編碼值是用於彙編數組中的動態地址位置。當然,這可以以更乾淨的方式完成(不要寫魔術數字孩子),我不得不手動寫出所有這些字節後就懶了。最後幾個要寫的位是要使用ReadProcessMemory讀取的循環,存儲器,將字符和索引存儲到有序的字節數組中,發信號字節,使用WriteProcessMemory ,噹噹前內存的讀取完成並打印隱藏的消息時。我們知道,發送功能僅發生14次,因此一旦填充字節數組,我們就退出了while循環;可以通過更多的內存編輯來更改此過程,以向我們的應用程序發出信號,表明該過程正在“退出”,並且我們的循環可以停止,而不是使用14個硬編碼值,但對於此示例而言。
結果?我們的隱藏消息印在我們自己的控制台應用程序中!如果有人好奇,NPT是對其他軟件最大hacker-im-iall-iall-Him寫道的引用。