每日優(yōu)鮮mfsig unidbg逆向分析

每日優(yōu)鮮mfsig unidbg逆向分析

so層

base64

前面已經(jīng)分析了Java層的調(diào)用劳景,以及unidbg實(shí)現(xiàn),接下來(lái)結(jié)合unidbg和ida對(duì)so層進(jìn)行逆向克胳。

ida打開(kāi)libsign.so类缤,函數(shù)窗口搜索Java,可以看到靜態(tài)注冊(cè)的Java_cn_missfresh_wsg_SecurityLib_nativeSign,進(jìn)入函數(shù)晤斩,修改a1JNIEnv *a1

image-20211225094228289

emmm焕檬,參數(shù)個(gè)數(shù)好像對(duì)不上,先不管這個(gè)“谋茫現(xiàn)在從結(jié)果往前倒推实愚,v15是輸出,它由v14賦值烹俗,v14v18v19賦值爆侣,但是從代碼看,這兩個(gè)好像都沒(méi)有被作為左值幢妄。打開(kāi)sub_36FC0兔仰,返回后F5一下,函數(shù)更新

image-20211225094728040

所以最終它是由sub_36FC0的第一個(gè)參數(shù)v17賦值的蕉鸳,進(jìn)去看看

image-20211225095448360

而它絕大部分的參數(shù)都被傳進(jìn)了一個(gè)函數(shù)sub_332F0乎赴,值得點(diǎn)進(jìn)去看看

image-20211225095833407
image-20211225095857927
image-20211225095937949
image-20211225095952965

然后就看到了幾百行的代碼,里面由很多類似log的東西潮尝,從中看出似乎用了hmac算法榕吼,暫時(shí)不清楚摘要算法是什么,也看到了msfn這個(gè)熟悉的字符串勉失。

但是幾百行代碼羹蚣,調(diào)用的函數(shù)少說(shuō)也有上10個(gè)了,層層調(diào)用乱凿,要從哪里開(kāi)始分析呢顽素。。

這時(shí)候就要用unidbg了徒蟆,先在sub_332F0下個(gè)斷點(diǎn)

public MissFresh() {
    //...
    emulator.attach().addBreakPoint(module.base + 0x332f0+1);
}
image-20211225100657360

結(jié)合ida來(lái)看胁出,r0就是存結(jié)果的地方,不過(guò)現(xiàn)在剛進(jìn)入函數(shù)段审,結(jié)果還沒(méi)存進(jìn)去全蝶。打印其他寄存器看看

image-20211225100924463

輸入blr,在函數(shù)返回的地方下個(gè)斷點(diǎn)寺枉,然后輸入c繼續(xù)執(zhí)行抑淫,然代碼運(yùn)行到函數(shù)返回處,這時(shí)候打印一下剛剛r0的地址的數(shù)據(jù)

image-20211225101422775

打印圖中地址的數(shù)據(jù)

image-20211225101454583

這個(gè)就是我們的結(jié)果姥闪,那么現(xiàn)在我們要對(duì)0x402e7000這個(gè)地址進(jìn)行跟蹤丈冬,看看是誰(shuí)對(duì)它進(jìn)行了寫操作

public MissFresh() {
    //...
    emulator.traceWrite(0x402e4000L, 0x402e4000L+16L);
}
image-20211225101958758

可以看到有2輪寫的操作,不過(guò)我比較關(guān)心第一輪甘畅,因?yàn)檫@時(shí)候結(jié)果已經(jīng)生成了埂蕊,第二輪只是簡(jiǎn)單的移一下位置往弓,在字符串頭部添加mfsn

那么ida跳轉(zhuǎn)到0x37f76蓄氧,它在sub_37F3C這個(gè)函數(shù)

image-20211225102254858

多熟悉的代碼啊函似,這一看就是base64,點(diǎn)擊看看aAbcdefghijklmn

image-20211225102448422

自定義的碼表喉童,接著下斷點(diǎn)看看輸入

emulator.attach().addBreakPoint(module.base + 0x37F3C+1);
image-20211225102639079

CyberChef驗(yàn)證一下

image-20211225102933609

完全沒(méi)問(wèn)題撇寞,那么接下來(lái)就是找base64的輸入是怎么來(lái)的,從樣式來(lái)看堂氯,前9位是時(shí)間戳的前9位蔑担,后4位是時(shí)間戳的后4位,所以接下來(lái)就是找中間的長(zhǎng)度為64的輸入咽白。

輪換

對(duì)sub_37F3C查看引用啤握,只有一個(gè)函數(shù)sub_37E5C,進(jìn)去看看

image-20211225103334974
image-20211225103529007

我們已經(jīng)知道v7就是base64的輸入晶框,而它是由a2賦值的排抬。繼續(xù)對(duì)sub_37E5C查找引用,只有一個(gè)函數(shù)sub_332F0

image-20211225103932863
image-20211225104006639

這時(shí)候我們已經(jīng)從數(shù)百行代碼里找到了最后生成結(jié)果的地方授段,接下來(lái)就是繼續(xù)往前回溯蹲蒲。

當(dāng)然,我們也可以根據(jù)mfsn這個(gè)字符串推測(cè)侵贵,v179是最后存結(jié)果的地址届搁,而它又在sub_37E5C被使用了,進(jìn)而找到突破口窍育。

總而言之咖祭,我們現(xiàn)在要找v116是怎么生成的。

image-20211225104757920

啥也別說(shuō)了蔫骂,看看sub_2F8F6

image-20211225104935825

下個(gè)斷點(diǎn)看看

emulator.attach().addBreakPoint(module.base + 0x2F8F6+1);

運(yùn)行之后牺汤,發(fā)現(xiàn)它調(diào)用了很多次辽旋,哪次才是我想要查看的呢。首先檐迟,初始化完成之前的我們肯定不需要查看补胚,初始化之后它被調(diào)用了3次,打印輸入輸出之后發(fā)現(xiàn)是第2次追迟。

image-20211225112922991

接下來(lái)對(duì)0x402a10f0進(jìn)行跟蹤溶其,看看誰(shuí)對(duì)它進(jìn)行了寫操作。

emulator.traceWrite(0x402a10f0L, 0x402a10f0L+32L);
image-20211225113115812

ida跳轉(zhuǎn)到0x36489敦间,發(fā)現(xiàn)是sub_363DC函數(shù)瓶逃。

image-20211225113333321

下斷點(diǎn)看看輸入

emulator.attach().addBreakPoint(module.base + 0x363DC+1);
image-20211225113516594
image-20211225113537658
image-20211225113548433

結(jié)合代碼可以分析出束铭,v14就是"9566",也就是時(shí)間戳的后4位厢绝,v17"ABCDEFGH"契沫,從sub_332F0看出,它是一個(gè)定值昔汉。

image-20211225113957105

結(jié)合代碼分析得出懈万,它是對(duì)輸入做一個(gè)輪換,然后得出結(jié)果靶病。代碼實(shí)現(xiàn)來(lái)驗(yàn)證一下会通。

_CONST = b'ABCDEFGH'

def sub_363DC(data, t2):
    msg = bytes((data[i] + t2[i%4] + _CONST[i%8]) & 0xff for i in range(len(data)))
    return msg

if __name__ == '__main__':
    data = bytes.fromhex('088280800810011a40314538444143354432354541304643333341333233313246373130453043343734414645303839354332393634453337353237363444464535313738383838423001')
    print(sub_363DC(data, b'9566').hex())
image-20211225114607586

完全對(duì)上了!

protobuf

接下來(lái)就是看輪換函數(shù)的輸入是怎么來(lái)的娄周,通過(guò)更換時(shí)間戳和請(qǐng)求參數(shù)涕侈,發(fā)現(xiàn)輸入的前面一段088280800810011a40和后面一小段3001是不會(huì)變的。但是它到底是什么呢昆咽,是完全固定的無(wú)意義的值驾凶,還是其他什么東西。先繼續(xù)往前追溯掷酗。

前面提到调违,加密疑似用了hmac算法,當(dāng)時(shí)我們不太清楚用了什么摘要算法泻轰,不過(guò)現(xiàn)在我們從輸入中間那段長(zhǎng)度位64的16進(jìn)制字符串技肩,猜測(cè)它用了SHA256摘要算法。接下來(lái)就是驗(yàn)證它是不是用了SHA256浮声,是不是標(biāo)準(zhǔn)的SHA256虚婿。

從前面已經(jīng)分析出sub_363DCr2,也就是第3個(gè)參數(shù)泳挥,存著輸入然痊。

image-20211225120036326

所以我們往前追溯v163的調(diào)用。

image-20211225120149338

進(jìn)去看看

image-20211225140100656

這個(gè)函數(shù)干了什么屉符,我們可以通過(guò)后續(xù)的log推測(cè)一下

image-20211225141735844

似乎是個(gè)protobuf序列化剧浸,那我們嘗試把之前得到的結(jié)果進(jìn)行個(gè)反序列化看看。

def decode(data):
    process = subprocess.Popen(
        ["protoc", "--decode_raw"],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )

    output = error = None
    try:
        output, error = process.communicate(data)
        output = output.decode()
    except OSError:
        pass
    finally:
        if process.poll() != 0:
            process.wait()
    return output

if __name__ == '__main__':
    data = bytes.fromhex('088280800810011a40314538444143354432354541304643333341333233313246373130453043343734414645303839354332393634453337353237363444464535313738383838423001')
    print(decode(data))
image-20211225141936839

反序列化成功矗钟,說(shuō)明使用了protobuf唆香。而16777218等于0x1000002,這個(gè)就是so調(diào)用初始化函數(shù)的時(shí)候傳入的值吨艇。

接下來(lái)就是編寫proto文件

syntax = "proto3";

message Data {
    int32 initNumber = 1;
    int32 a2 = 2;
    string sign = 3;
    int32 a6 = 6; 
}

編譯生成python文件

protoc meiriyouxian.proto --python_out .

調(diào)用驗(yàn)證

import meiriyouxian_pb2
data = meiriyouxian_pb2.Data()
data.initNumber = 0x1000002
data.a2 = 1
data.sign = '1E8DAC5D25EA0FC33A32312F710E0C474AFE0895C2964E3752764DFE5178888B'
data.a6 = 1

data2 = data.SerializeToString()
print(data2)
image-20211225143257895

當(dāng)然偷懶一點(diǎn)的辦法也有躬它,就是直接把前面和后面的字符寫死,反正最后變動(dòng)的只有中間的64個(gè)字符东涡。

hmac

接下來(lái)就是查找疑似HMAC-SHA256的中間值冯吓,繼續(xù)看看sub_348B4倘待,下斷點(diǎn)看看輸入

image-20211225143933889
image-20211225143945200

接下來(lái)從sub_348B4的第5個(gè)參數(shù)繼續(xù)往前回溯,

image-20211225144315920
image-20211225144340705

sub_2E5A4的輸出應(yīng)該是v145桑谍,輸入是v179延柠;sub_37D8C的輸出應(yīng)該是v179,輸入應(yīng)該是v202

由于sub_2E5A4有被多次調(diào)用锣披,選擇先在sub_37D8C下個(gè)斷點(diǎn)看看

emulator.attach().addBreakPoint(module.base + 0x37D8C+1);
image-20211225145659606
image-20211225145728122

輸入blr在函數(shù)返回處下斷點(diǎn)贞间,輸入c繼續(xù)執(zhí)行到函數(shù)返回處。查看原r0的值

image-20211225145851418
image-20211225145907681

所以sub_37D8C的返回值已經(jīng)有我們需要的值了,現(xiàn)在要看看它是怎么得出這個(gè)結(jié)果的。

image-20211225151921347

我們已經(jīng)知道a2是它的輸入障贸,所以先看看sub_2FB14

image-20211225152413418

看看sub_367F6

image-20211225153355859

進(jìn)入sub_36558看看

image-20211225153448580
image-20211225153511163

這些都是SHA256的標(biāo)志,再看看dword_9E030

image-20211225153602667

妥妥的SHA256的K值峻仇。

現(xiàn)在我們已經(jīng)有很大把握確定它是HMAC-SHA256,接下來(lái)就是找它的輸入邑商,以及它的key摄咆,方便我們驗(yàn)證它是否是標(biāo)準(zhǔn)的實(shí)現(xiàn)。

回到主體函數(shù)sub_332F0人断,繼續(xù)往前回溯

image-20211225154019122

這個(gè)似乎是HMACupdate部分吭从,下個(gè)斷點(diǎn)看看

image-20211225154159843
image-20211225154251091
image-20211225154308043

正好是Java層的請(qǐng)求參數(shù),沒(méi)有再拼接salt或者其他東西恶迈。

繼續(xù)往前回溯

image-20211225154423116

應(yīng)該是HMACinit函數(shù)涩金,進(jìn)入看看

image-20211225154536612

看看sub_2FA30

image-20211225154623219

兩個(gè)熟悉的數(shù)字0x360x5C,它們正是HMACmagic number暇仲。

下個(gè)斷點(diǎn)看看

emulator.attach().addBreakPoint(module.base + 0x2FA30+1);
image-20211225154939099
image-20211225155010828

這個(gè)極有可能就是HMACkey步做,有了key和輸入,在CyberChef上驗(yàn)證一下奈附,不行我們?cè)倮^續(xù)分析全度。

image-20211225155305021

完全正確!說(shuō)明是標(biāo)準(zhǔn)實(shí)現(xiàn)斥滤,接下來(lái)就是用代碼實(shí)現(xiàn)一下整個(gè)流程将鸵。

總結(jié)和代碼實(shí)現(xiàn)

sign的生成流程如下:

  1. HMAC-SHA256
  2. protobuf序列化
  3. 輪換函數(shù)
  4. 自定義base64
import binascii
import hashlib
import hmac

# 請(qǐng)自行生成
import meiriyouxian_pb2

_TABLE_RAW = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
_TABLE_MISS = b'abcdefghijklmnopqrstuvwxyz+ZYXWVUTSRQPONMLKJIHGFEDCBA/1234567890'
_TRANS = bytes.maketrans(_TABLE_RAW, _TABLE_MISS)
_TRANS_INV = bytes.maketrans(_TABLE_MISS, _TABLE_RAW)

_HMAC_KEY = b'PwwGKgCqZAc2PPb31TLnnqPNVFAAdq/X'
_CONST = b'ABCDEFGH'


def b64decode(data):
    left = len(data) % 4
    if left:
        data += '=' * (4 - left)
    data = data.encode().translate(_TRANS_INV)
    msg = binascii.a2b_base64(data)
    return msg

def b64encode(data):
    """
    sub_37F3C
    """
    msg = binascii.b2a_base64(data, newline=False)
    msg = msg.translate(_TRANS)
    msg = msg.decode().rstrip('=')
    return msg

def sub_363DC(data, t2):
    msg = bytes((data[i] + t2[i%4] + _CONST[i%8]) & 0xff for i in range(len(data)))
    return msg


def calc_sign(params, ts, body='', init=0x1000002):
    if isinstance(body, str):
        body = body.encode()
    if isinstance(ts, str):
        ts = ts.encode()
    if isinstance(params, (list, tuple, dict)):
        if hasattr(params, 'items'):
            params =  params.items()
        params = ''.join(f'{k}{v}' for k, v in sorted(params, reverse=True)).encode()
    data = params + body
    data2 = hmac.new(_HMAC_KEY, data, hashlib.sha256).hexdigest().upper()
    
    pbuf = meiriyouxian_pb2.Data()
    pbuf.initNumber = init
    pbuf.a2 = 1
    pbuf.sign = data2
    pbuf.a6 = 1
    data3 = pbuf.SerializeToString()
    
    ts1 = ts[:9]
    ts2 = ts[9:]
    data4 = sub_363DC(data3, ts2)
    data5 = ts1 + data4 + ts2
    sign = 'mfsn' + b64encode(data5)
    return sign

def test():
    ts = b'1640187039566'
    query = b'version9.7.0tdkeyJvcyI6ImFuZHJvaWQiLCJ2ZXJzaW9uIjoiMy4xLjkiLCJwYWNrYWdlcyI6ImNuLm1pc3NmcmVzaC5hcHBsaWNhdGlvbiomOS43LjAiLCJwcm9maWxlX3RpbWUiOjI4MywiaW50ZXJ2YWxfdGltZSI6MTQ5NjksInRva2VuX2lkIjoiajViSUs1SmV1bUxzZUVWMVptb3ZxNHNzT0J4OXBCUlJsNk9kbzRlQ01iemZWNWNlUmswSjZYK2lLWE4rVkdJQ3N5S1V0MFByS1lHSE5tMm5iSlZIOHc9PSJ9source_device_id359906070748939sessionandroid0.95648171296963921640187024465screen_width1440screen_height2560realVersionplatformandroidisShow0imeifbc64376480ee60e43e933dae0258d3fdevtka3JZZ1NRVzNZWW9ZMERIVDFvQmJ6MytoVkxsQWJuV1RnLzV2MnpXYVZGUTFqN09zUFIzeFd6WWo3dkNsb0J4MEY2Q1FGeTZhZXpQaA0KdFlZWXAzQkxicmxiTm5rejE0SEt5UE84UVpWOXdWRUxJem0rd0ZiV2QzVks4cFphMmphQWJFYmJrK3dFQXRCL1N6eEtXNmp3eHc9PQ==device_id359906070748939currentLng113.97177currentLat22.540642android_id581e0c22a2843d73android_channel_valuept-lingdu002access_tokenSM_Device_ID2021120821500831e6fbaf8244fa2c94916c1cfe02a8a701cd5c98e2bbb3dc'
    sign = calc_sign(query, ts)
    print(sign)
    assert sign == 'mfsnmtyAmde3nBaBUFN49M+lVLS5Kl5CEJBaI65LJJ90K7pbJ+K5JZcGJJdaJKKKE5FaIJgJGIddK6w2J6KJI6sFEJgDJkGDHk0bDl9IKJg1I6w1FkX5otu1nU'

if __name__ == '__main__':
    test()
image-20211225160432289

代碼僅供把玩。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末中跌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子菇篡,更是在濱河造成了極大的恐慌漩符,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驱还,死亡現(xiàn)場(chǎng)離奇詭異嗜暴,居然都是意外死亡凸克,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門闷沥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)萎战,“玉大人,你說(shuō)我怎么就攤上這事舆逃÷煳” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵路狮,是天一觀的道長(zhǎng)虫啥。 經(jīng)常有香客問(wèn)我,道長(zhǎng)奄妨,這世上最難降的妖魔是什么涂籽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮砸抛,結(jié)果婚禮上评雌,老公的妹妹穿的比我還像新娘。我一直安慰自己直焙,他們只是感情好景东,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著箕般,像睡著了一般耐薯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丝里,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天曲初,我揣著相機(jī)與錄音,去河邊找鬼杯聚。 笑死臼婆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的幌绍。 我是一名探鬼主播颁褂,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼傀广!你這毒婦竟也來(lái)了颁独?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤伪冰,失蹤者是張志新(化名)和其女友劉穎誓酒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡靠柑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年寨辩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歼冰。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡靡狞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出隔嫡,到底是詐尸還是另有隱情甸怕,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布畔勤,位于F島的核電站蕾各,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏庆揪。R本人自食惡果不足惜式曲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缸榛。 院中可真熱鬧吝羞,春花似錦、人聲如沸内颗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)均澳。三九已至恨溜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間找前,已是汗流浹背糟袁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躺盛,地道東北人项戴。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像槽惫,于是被迫代替她去往敵國(guó)和親周叮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容