LyScript 實現對記憶體堆疊掃描

2022-08-03 12:00:47

LyScript外掛中提供了三種基本的堆疊操作方法,其中push_stack用於入棧,pop_stack用於出棧,而最有用的是peek_stack函數,該函數可用於檢查指定堆疊位置處的記憶體引數,利用這個特性就可以實現,對堆疊地址的檢測,或對堆疊的掃描等。

LyScript專案地址:https://github.com/lyshark/LyScript

peek_stack命令傳入的是堆疊下標位置預設從0開始,並輸出一個十進位制有符號長整數,首先實現有符號與無符號數之間的轉換操作,為後續堆疊掃描做準備。

from LyScript32 import MyDebug

# 有符號整數轉無符號數
def long_to_ulong(inter,is_64 = False):
    if is_64 == False:
        return inter & ((1 << 32) - 1)
    else:
        return inter & ((1 << 64) - 1)

# 無符號整數轉有符號數
def ulong_to_long(inter,is_64 = False):
    if is_64 == False:
        return (inter & ((1 << 31) - 1)) - (inter & (1 << 31))
    else:
        return (inter & ((1 << 63) - 1)) - (inter & (1 << 63))

if __name__ == "__main__":
    dbg = MyDebug()

    connect_flag = dbg.connect()
    print("連線狀態: {}".format(connect_flag))

    for index in range(0,10):

        # 預設返回有符號數
        stack_address = dbg.peek_stack(index)

        # 使用轉換
        print("預設有符號數: {:15} --> 轉為無符號數: {:15} --> 轉為有符號數: {:15}".
              format(stack_address, long_to_ulong(stack_address),ulong_to_long(long_to_ulong(stack_address))))

    dbg.close()

通過上述封裝函數,即可實現對有符號和無符號數的轉換。

繼續完善該功能,我們使用get_disasm_one_code()函數,掃描堆疊地址並得到該地址處的反組合程式碼。

from LyScript32 import MyDebug

# 有符號整數轉無符號數
def long_to_ulong(inter,is_64 = False):
    if is_64 == False:
        return inter & ((1 << 32) - 1)
    else:
        return inter & ((1 << 64) - 1)

# 無符號整數轉有符號數
def ulong_to_long(inter,is_64 = False):
    if is_64 == False:
        return (inter & ((1 << 31) - 1)) - (inter & (1 << 31))
    else:
        return (inter & ((1 << 63) - 1)) - (inter & (1 << 63))

if __name__ == "__main__":
    dbg = MyDebug()

    connect_flag = dbg.connect()
    print("連線狀態: {}".format(connect_flag))

    for index in range(0,10):

        # 預設返回有符號數
        stack_address = dbg.peek_stack(index)

        # 反組合一行
        dasm = dbg.get_disasm_one_code(stack_address)

        # 根據地址得到模組基址
        if stack_address <= 0:
            mod_base = 0
        else:
            mod_base = dbg.get_base_from_address(long_to_ulong(stack_address))

        print("stack => [{}] addr = {:10} base = {:10} dasm = {}".format(index, hex(long_to_ulong(stack_address)),hex(mod_base), dasm))

    dbg.close()

得到的堆疊引數如下:

由此我們可以得到堆疊處的反組合引數,但如果我們需要檢索堆疊特定區域內是否存在返回到模組的地址,該如何實現呢?

其實很簡單,首先我們需要得到程式全域性狀態下的所有載入模組的基地址,然後得到當前堆疊記憶體地址內的實際地址,並通過實際記憶體地址得到模組基地址,對比全域性表即可拿到當前模組是返回到了哪裡。

from LyScript32 import MyDebug

# 有符號整數轉無符號數
def long_to_ulong(inter,is_64 = False):
    if is_64 == False:
        return inter & ((1 << 32) - 1)
    else:
        return inter & ((1 << 64) - 1)

# 無符號整數轉有符號數
def ulong_to_long(inter,is_64 = False):
    if is_64 == False:
        return (inter & ((1 << 31) - 1)) - (inter & (1 << 31))
    else:
        return (inter & ((1 << 63) - 1)) - (inter & (1 << 63))

if __name__ == "__main__":
    dbg = MyDebug()

    connect_flag = dbg.connect()
    print("連線狀態: {}".format(connect_flag))

    # 得到程式載入過的所有模組資訊
    module_list = dbg.get_all_module()

    # 向下掃描堆疊
    for index in range(0,10):

        # 預設返回有符號數
        stack_address = dbg.peek_stack(index)

        # 反組合一行
        dasm = dbg.get_disasm_one_code(stack_address)

        # 根據地址得到模組基址
        if stack_address <= 0:
            mod_base = 0
        else:
            mod_base = dbg.get_base_from_address(long_to_ulong(stack_address))

        # print("stack => [{}] addr = {:10} base = {:10} dasm = {}".format(index, hex(long_to_ulong(stack_address)),hex(mod_base), dasm))
        if mod_base > 0:
            for x in module_list:
                if mod_base == x.get("base"):
                    print("stack => [{}] addr = {:10} base = {:10} dasm = {:15} return = {:10}"
                          .format(index,hex(long_to_ulong(stack_address)),hex(mod_base), dasm,
                                  x.get("name")))

    dbg.close()

執行後,即可掃描到堆疊內的所有返回模組的位置。