吾愛破解 - LCG - LSG |安卓破解|病毒分析|huihengkj.com

 找回密碼
 注冊[Register]

QQ登錄

500彩票邀请码只需一步,快速開始

搜索
查看: 3330|回復: 51

[原創] [我的代碼傳記]upx脫殼重定位表修復

  [復制鏈接]
樓主
psycongroo 發表于 2020-11-1 21:51 回帖獎勵

前言

一個風和日麗的下午,鳥兒在枝頭高唱,花兒在草中盛放,而我,在電腦前暴怒。本來滿懷欣喜的脫了個簡單的upx殼,沒想到卻讓我暴跳如雷。

“這是什么啊,怎么用了官方的脫殼500彩票邀请码命令還運行不了。”對自己發自靈魂的拷問,再一次從旁印證,菜,真的是菜的摳腳。

于是乎我瘋狂點擊x32dbg,那一瞬間我以為我瘋了,自己反編譯調試自己的代碼,這總感覺有點令人抓狂。
在一兩下的F9運行之后,我找到了問題所在:

異常斷在了紅框的語句,咋一看似乎沒有什么問題,但多次與脫殼前的程序對比,我們就發現了一個問題:被賦值的地址是變動的

500彩票邀请码“啊哈!”,我掩蓋不住心中的狂喜。既然已經找到了問題的所在,只要把問題解決了那不就行了。

500彩票邀请码于是乎我用StudyPE載入了脫殼后的文件,輕輕點了一下鼠標,隨著清脆的點擊聲,問題迎刃而解。

一問到底

滿臉欣喜的我,笑容很快從臉上褪去,剩下的只有難以言表的寂寞和空虛,總感覺缺了點什么,我不禁的問自己,剛剛做了什么。

500彩票邀请码問題解決了嗎?是的,從結果上來看我們解決了問題,但從過程來看,哦不,我們完全沒有解決。

某些時候,借助一些工具亦或者投機取巧解決問題,總感覺不踏實,不知道各位如何,反正我有這種感覺。

500彩票邀请码尚在學習階段的我,于是乎決定一探究竟,找到根源之所在,連根拔起。這次,我又一次點開了x32dbg,與前幾分鐘不同的是,我多了幾分平靜,多了幾分祥和。

或許有足夠耐心的你,看著文筆尚不算太好,甚至意義不明的“技術文章”,至此仍然沒看到問題的說明,感到了不耐煩。客官先別著急,且聽我細細道來——這次問題的觸發歸根到底是原程序的重定位表沒有被寫到脫殼后程序的重定位表中來才使得涉及到內存地址的變量發生了錯誤。

500彩票邀请码讓我們言歸正傳,再一次定位到異常觸發的地方——那個賦值語句。既然脫殼后程序的內存地址沒有改變,就側面證明了加了殼的程序中,這個地址是由一段代碼更改而不是根據加殼程序的重定位表更改的。也就是說upx殼他模仿了Window系統根據重定位表改寫地址的方法,通過自己儲存原程序重定位表,再用自己代碼改寫。

500彩票邀请码盡管用了一段不算太長也不算太短的文字來描述了一下整個推理過程,但機智的我僅用了一瞬間就已經想到這些東西(自夸)。同時我又迅速的記住了發生錯的代碼的rva(其實是復制),然后打開脫殼前的程序,定位到了發生錯誤的地方,并對這個地址打下了硬件寫斷點:

顯然,此時的代碼還沒被upx解壓,但我們沒有關系,直接來到對應的內存地址并打下了硬件寫斷點,在幾次F9后,我們來到了一個改寫這個地方的代碼:

excellent!是循環!它出現了!或許在大家眼里,它只是一個微不足道的循環體,但我卻看到了勝利的曙光。這么大的重定位表,并且要對它操作,有循環基本是八九不離十的。當執行到紅框語句,對應地址就被更改了。隨后在一些微小的分析下,我梳理出了如下過程:

  1. edi指向一個記有偏移量的表,每次循環根據情況取1個或2個字節,直到遇到0字節。

  1. ebx對上一步結果進行累加,ebx最初值為第一個區段的內存虛擬地址減去4

3.ebx累加的結果其實就是需要變化的地址的地址,也就是重定位表結構中的VA和offset低位,以及基址的相加(不熟悉的學友萌可以去看看重定位表的結構,我也看了好多遍)。

typedef struct _MyReloadtion {
    DWORD virtualAddress;
    DWORD sizeOfBlock;
    vector<WORD> typeOffset;  //高4位表類型,一般都是3000  低12位表示偏移量
//所以這里ebx實際上就是 ebx = virtualAddress + typeOffset低12位
}MyReloadtion, *pMyReloadtion;

所以我們只要把ebx的結果分離出來,符合重定位表的結構,最后再寫進我們脫殼程序的重定位表中,便可大功告成。

頭皮發麻

既然我們都知道怎么做了,剩下所面臨的就是解決方法的問題。或許我們可以選擇hook,把循環里每一個ebx值記錄下來,經過變化就可以獲取到重定位表。

而作為老實人的我,由于并不知道怎么hook這種沒有在Call里面的代碼,所以選擇了第二種令人頭皮發麻的方法——模擬PE加載到內存中,獲取第一區塊的虛擬地址,再把位移表提取出來,通過模擬上述的步驟,來獲取重定位表。

500彩票邀请码這對于尚在襁褓之中的我來說無疑是具有十分的挑戰性的,可我都分析到這個地步了,豈能惹急你慫。此時我心中暗暗下了個毒誓,寫不出來王x蛋。

500彩票邀请码就這樣,我開始了漫長的C++之旅。

勝利之光

又一個陽光明媚的下午,葉兒在空中飛舞,蝶兒在花中跳舞,而我的頭發在空中飄落。

不得不說,打代碼是令人煩躁的,要用一成不變的言語來表示我千變萬化高超的思維,這簡直就是侮辱我的靈魂(開個玩笑)。為什么腦袋一團混沌,為什么情緒一向暴躁,很大的原因可能是我們解決問題的時候沒有分步看待并解決。

以繁化簡,讓復雜的問題分成幾個簡單的問題。而我在這里就分成以下幾個步驟:

  1. 提取位移表
  2. 模擬系統將PE文件載入內存。
  3. 模擬匯編中的循環,算出重定位表
  4. 將新的重定位表加到脫殼的程序里

500彩票邀请码當我理清了四個方向并逐個問題加以百度輔助解決后,心態就有了明顯的恢復,而臉上也露出了久違的猥瑣的笑容。

廢話說完了,接下來看看三個步驟的實施過程:

  1. 提取位移表:
    提取位移表很簡單,在x32dbg中,用文章之前的方法,定位到循環塊,找到edi指向的地址,右鍵提取,保存成bin文件即可。

  2. 模擬系統將PE文件載入內存并獲取第一個節區的內存地址:
    整體思路:
    2.1 通過讀取文件來獲取NT頭中FileHeader的區塊數量屬性以及OptionHeader的PE頭大小屬性,OptionHeader大小屬性,內存中節區對齊量屬性。
    2.2 將映像大小根據對齊量對齊后,載入整個PE頭。
    2.3 通過FileHeader大小,Signature大小,OptionHeader大小相加,最后得出區塊表的開始地址。
    500彩票邀请码 2.4 遍歷區塊表,加載對應的區塊并記錄第一個節區內存地址。


DWORD getReloadBase() {
    //1.獲取FileHeader和OptionHeader的一些關鍵變量
    HANDLE hfile = CreateFileA(targetName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    LPDWORD receiveSize = NULL;
    if (hfile == INVALID_HANDLE_VALUE || GetLastError() == ERROR_FILE_NOT_FOUND)
    {
        std::cout << "找不到目標文件" << GetLastError() << std::endl;
        return NULL;
    }
    // 讀取dos頭
    IMAGE_DOS_HEADER dosHeader;
    DWORD dosHeaderSize = sizeof(dosHeader);
    BOOL dosReadResult = ReadFile(hfile, &dosHeader, dosHeaderSize, receiveSize, NULL);
    if (!dosReadResult)
    {
        std::cout << "讀取dos頭錯誤" << GetLastError() << std::endl;
        return NULL;
    }
    // 讀取NT頭
    IMAGE_NT_HEADERS32 ntHeader;
    DWORD pointerResult = SetFilePointer(hfile, dosHeader.e_lfanew, NULL, FILE_BEGIN);
    if (pointerResult == INVALID_SET_FILE_POINTER)
    {
        std::cout << "設置讀取指針為NT頭時錯誤" << GetLastError() << std::endl;
        return NULL;
    }
    BOOL ntHeaderResult = ReadFile(hfile, &ntHeader, sizeof(ntHeader), receiveSize, NULL);
    if (!dosReadResult)
    {
        std::cout << "讀取NT頭錯誤" << GetLastError() << std::endl;
        return NULL;
    }
    WORD peHeaderSize = ntHeader.OptionalHeader.SizeOfHeaders;
    WORD setctionNums = ntHeader.FileHeader.NumberOfSections;
    DWORD imageSize = ntHeader.OptionalHeader.SizeOfImage;
    DWORD sectionAlign = ntHeader.OptionalHeader.SectionAlignment;

    //2. 對齊鏡像并載入PE頭
    int mImageSize = alignSize(imageSize, sectionAlign);
    mImageBase = new char[mImageSize];
    memset(mImageBase, 0, mImageSize);
    SetFilePointer(hfile, 0, NULL, FILE_BEGIN);
    ReadFile(hfile, mImageBase, peHeaderSize, receiveSize, NULL); //將文件頭寫入

    //3. 計算并獲取區塊表起始地址
    PIMAGE_NT_HEADERS mpNtheader = (PIMAGE_NT_HEADERS)((DWORD)mImageBase + dosHeader.e_lfanew);
    int mNtHeadersSize = sizeof(ntHeader.FileHeader) + sizeof(ntHeader.Signature) + ntHeader.FileHeader.SizeOfOptionalHeader;

    PIMAGE_SECTION_HEADER mpSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)mpNtheader + mNtHeadersSize);

    DWORD keyBaseAddress = NULL;

    //4. 遍歷區塊表加載區塊
    for (int index = 0; index < setctionNums; ++index)
    {
        DWORD va = mpSectionHeader->VirtualAddress;
        if (index == 0)
        {
            keyBaseAddress = va;
        }
        DWORD rawSize = mpSectionHeader->SizeOfRawData;
        DWORD vaSize = mpSectionHeader->Misc.VirtualSize;
        DWORD rawOffset = mpSectionHeader->PointerToRawData;

        if (rawSize == 0)
        {
            continue;
        }
        else
        {
            SetFilePointer(hfile, rawOffset, NULL, FILE_BEGIN);
            ReadFile(hfile, &mImageBase[va], rawSize, receiveSize, NULL);
        }
        mpSectionHeader++;
    }

    keyBaseAddress -= 4;
    CloseHandle(hfile);
    return keyBaseAddress;
}
  1. 模擬匯編中的算法得出重定位表
    整體思路:
    3.1 從位移表拿取一個字節,若大于EF取后兩個字節。
    3.2 將拿取到的字節與第一個節區內存地址-4相加。
    3.3 通過一些位運算拿到重定位表中的變量。
void buildReloadtionTable(DWORD baseAddress, char* pRelateTable) {
    //1. 位移+基值,變化后拿到offset
    vector<MyReloadtion> reloadtionTable;
    MyReloadtion reloadtion;

    int index = 0;
    int size = 0;
    // 記錄上一個計算出來的重定位表項的虛擬內存地址
    DWORD lastVirtualAddress = NULL;
    //為第一個區塊內存地址-4
    DWORD pReload = baseAddress;
    while (true)
    {
        //1.拿取位移表一個字節
        WORD relate = (BYTE)*pRelateTable;
        pRelateTable++;
        if (relate == 0)
        {
            reloadtion.sizeOfBlock = calSizeofBlock(size);
            reloadtionTable.push_back(reloadtion);
            break;
        }
        //2. 判斷拿到的字節是否大于EF
        if (relate > 0xEF)
        {
            relate = *pRelateTable;
            BYTE hightRelate = *(pRelateTable + 1);
            BYTE lowRelate = relate;
            relate = ((hightRelate << 8) | lowRelate);
            pRelateTable += 2;
        }
        pReload += relate;

        //3. 位運算獲取offset
        DWORD virtualAddress = pReload & 0xF000;
        WORD typeOffset = pReload & 0xFFF | 0x3000;

        if (lastVirtualAddress == NULL)
        {
            reloadtion = MyReloadtion();
            reloadtion.virtualAddress = virtualAddress;
        }
        else if(lastVirtualAddress != virtualAddress)
        {
            reloadtion.sizeOfBlock = calSizeofBlock(size);
            reloadtionTable.push_back(reloadtion);

            size = 0;
            reloadtion = MyReloadtion();
            reloadtion.virtualAddress = virtualAddress;
        }
        reloadtion.typeOffset.push_back(typeOffset);
        size++;
        lastVirtualAddress = virtualAddress;
    }
    //保存到文件
    saveToFile(reloadtionTable);
}
  1. 將新的重定位表加到脫殼的程序里:
    整體思路:
    4.1 為程序添加一個新的區塊(使用工具如PEStudy)
    4.2 將重定位表復制過去(HEX WORKSHOP)
    4.3 修改PE文件的重定位表指向(LordPE)

遺憾收場

500彩票邀请码那一天顯得特別寧靜,我如往常一樣坐在那里。“終于做出來了...”我輕輕的嘆了口氣,神態略顯輕松,滑動著鼠標的滾輪。看著眼前快速滑動的代碼行,心中百感交集。想想當初零行的代碼到現在上百行,對于剛入門WIN32以及C++來說確實是不容易。

500彩票邀请码再用了幾次寫的代碼修復程序后,心中卻開始感到了不滿。“為什么我每次都得自己找位移表,為什么我每次都得用工具新建區塊,為什么每次都要我修改重定位表的rva,為什么我的代碼不能一次實現呢?”

帶著這個遺憾,我寫下了這篇文章,不為別的,只為和大家分享。

那段匯編的循環代碼

500彩票邀请码31 C0 8A 07 47 09 C0 74 22 3C EF 77 11 01 C3 8B 03 86 C4 C1 C0 10 86 C4 01 F0 89 03 EB E2 24 0F C1 E0 10 66 8B 07 83 C7 02 EB E2

大家如果想探索文章提到的upx還原重定位表的部分,可以直接在調試器中用上面數據搜索直接定位到相關代碼。

關于代碼使用

將exe放到加了upx殼的程序的目錄下,將提取出來的位移表命名為rva.bin,加殼程序命名為target.exe,成功運行后,會生成一個叫result.bin的文件,這個就是重定位表。
500彩票邀请码 本程序并沒有做多版本的window測試,也沒有做upx多版本測試,目前只在自家的win10下測試并通過,使用的upx版本為3.96w。

結語

500彩票邀请码這篇文章廢話較多,作為技術文來說或許是不及格的。其實一直都想用一種敘事的手法來寫,這可能是新的嘗試呢,所以希望各位學友不嫌煩,能較為有趣的看完。

500彩票邀请码關于剛剛提到的遺憾,筆者也會著手嘗試,讓一切自動起來。

附件

免費評分

參與人數 28威望 +2 吾愛幣 +130 熱心值 +26 收起 理由
不能吃的李子 + 1 謝謝@Thanks!
GhostThrone + 1 謝謝@Thanks!
染個我 + 1 + 1 謝謝@Thanks!
十五先生 + 1 熱心回復!
foxcelles + 1 + 1 用心討論,共獲提升!
杜小康 + 1 + 1 用心討論,共獲提升!
嘟囔嘟囔 + 1 用心討論,共獲提升!
gaosld + 1 + 1 熱心回復!
ycsyywl + 2 + 1 文筆不錯,看著很有意思
fengbolee + 1 + 1 用心討論,共獲提升!
DamonBernard + 1 + 1 用心討論,共獲提升!
victos + 1 + 1 謝謝@Thanks!
0615 + 1 + 1 我很贊同!
Hmily + 2 + 100 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
永昌 + 1 謝謝@Thanks!
歐雨鵬 + 1 + 1 謝謝@Thanks!
__不說 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
Tolove + 1 + 1 請問哪里能買到您的著作
Poner + 7 + 1 歡迎分析討論交流,吾愛破解論壇有你更精彩!
深水夜藏 + 1 + 1 我很贊同!
zjun777 + 1 用心討論,共獲提升!
weemy96 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
濤之雨 + 1 + 1 用心討論,共獲提升!
火焰加魯魯 + 1 加油,路人小白路過
為之奈何? + 1 + 1 我很贊同!
Link_Stark + 2 + 1 謝謝@Thanks!
smile5 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
hszt + 1 + 1 用心討論,共獲提升!

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

推薦
 樓主| psycongroo 發表于 2020-11-4 19:22 |樓主
歐雨鵬 發表于 2020-11-4 12:47
前輩,還有一個不明白的地方,獲取了section的第一個區塊的RVA,為何要-4,看到你代碼中獲取的的結果keyBaseA ...

因為upx的匯編代碼里就是把第一個區塊的地址-4,然后加上位移表。這不是什么特別的東西,是upx自己的算法。你可以在匯編代碼里的那個循環里第一次的循環,看ebx的值,也就是我截圖的那句add ebx, eax。如果你用x32dbg,點上面的內存布局,找到一個節區叫upx0,看它地址,你就會得出這個結論。

如果復制進去,只需要任意一個16進制的文本編輯工具,如hexworkshop,010editor都可以,winhex沒用過,應該也是可以的。
推薦
 樓主| psycongroo 發表于 2020-11-3 22:15 |樓主
歐雨鵬 發表于 2020-11-3 21:54
這個studype版本賊新,1.09版本沒有這個固定基址的功能。
另外,想問一下樓主,在反匯編中跟到的重定位表 ...

偏移表是存在內存的,可以直接保存出來。如果你是想用hook的方法,也就是說不想自己再模仿他的算法,那么hook就會在循環里,每次循環記錄一次。
原來的upx,rsrc,syc不用刪除,你只要添加新的節區,然后往里面按重定位表的格式填virtual,size,offset,最后再更改PE頭中的指向重定位表的目錄地址就可以了。
沙發
hszt 發表于 2020-11-1 22:20
3#
你與明日 發表于 2020-11-1 22:23
很強,反正我自己接觸久了之后發現地址在0x400000也不錯,而且IDA也不用改ImageBase
4#
 樓主| psycongroo 發表于 2020-11-1 22:46 |樓主
你與明日 發表于 2020-11-1 22:23
很強,反正我自己接觸久了之后發現地址在0x400000也不錯,而且IDA也不用改ImageBase

確實,exe的話沒必要修重定位哈哈哈,只是遇到了就有這樣的想法,然后就去試試了。
5#
桂花糕乀 發表于 2020-11-1 22:58
文章 有趣,代碼雖然復制無聊,但文字有趣!
6#
wq5883 發表于 2020-11-2 08:02
雖然看不懂,也要支持一下。
7#
子尤 發表于 2020-11-2 13:46
反編譯這塊確實比較費腦子,支持一下
8#
Dawnth 發表于 2020-11-2 22:02
太厲害了,支持一下
9#
深水夜藏 發表于 2020-11-2 22:57
感謝分享,支持一下
10#
ldw471427015 發表于 2020-11-2 23:15
我說句實話  你應該去寫小說 或者 段子
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:本版塊禁止灌水或回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|處罰記錄|聯系我們|吾愛破解 - LCG - LSG ( )

GMT+8, 2020-11-26 23:53

Powered by Discuz!

500彩票邀请码Copyright © 2001-2020, Tencent Cloud.

快速回復 返回頂部 返回列表

500彩票邀請碼-彩經網