Tut - Tool Game 4D: God of War series

Thảo luận trong 'Tut - Tool hướng dẫn dịch game' bắt đầu bởi Yugisokubodai, 25/12/16.

  1. povanteo

    povanteo Thành viên cấp 1

    Tham gia ngày:
    25/12/15
    Bài viết:
    51
    Đã được thích:
    22
    Lót dép hóng source bản dịch.
     
  2. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    Ý mình là, dựa vào địa chỉ s3 để tìm path là không đúng (or chỉ với GOW).
    (Đã test với game Metal Slug XX cho kết quả hoàn toàn khác.
    (chỉ sp chứa path - do cờ sp chưa được dùng đến)
    [​IMG]

    Lệnh sceIoRead có dùng đến fd của sceIoOpen, biến này kiểu int (4byte).
    Trong hình của bạn s3 = 8CFF4A4 -> 5 = fd.
    Cái fd này giống id, mở 1 file là 4, mở thêm 1 file tăng lên 5,...
    Nó chỉ là 1 số, nên chuẩn nhất thì phải cần đến sceIoOpen.
     
  3. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    sceIoRead có tham số data là địa chỉ buffer để ghi dữ liệu vào đó, còn sceIoOpen không có.
    Ở đây đang đặt write breakpoint nên chỉ có sceIoRead được gọi, không phải sceIoOpen.
     
  4. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    Ngắn gọn.
    buffer, sceIoRead -> fd
    sceIoOpen + path = fd

    buffer->fd bằng sceIoRead là đúng rồi
    Nhưng từ fd đó k thể trực tiếp lấy được path mà phải cần tiếp đến sceIoOpen.
    Bởi input, output lệnh này k hề có liên quan đến path k hiểu sau bạn lại dừng tại fd,
    dựa vào block gần fd -> path thì là không khoa học rồi .

    .
     
  5. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    Ah hiểu ý bạn rồi.
    CPU ghi font vào Ram từ sau sceiOread, nhưng trước đó nó đã open cái file rồi.
    Để tối về sửa lại.
     
  6. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    III. Bắt tay vào việc: xác định text

    Trong phần trước, ta đã xác định được font chữ (nói đúng hơn là texture chữ) để chỉnh sửa nó. Tuy nhiên, nếu để ý kỹ thì sẽ thấy hai điểm khó khăn trong phần font của bản Anh ngữ mà ta đã xác định:

    - Bảng font chỉ gồm 26 chữ cái La Tinh và một số ký tự Ascii khác, không đủ nhiều để có thể chèn chữ La Tinh có dấu trong tiếng Việt. Số lượng chữ có dấu và không dấu trong tiếng Việt (không tính chữ in hoa) đã hơn 60. Vậy phải làm sao?
    - Bảng font chỉ có một kiểu chữ là in hoa, không có chữ thường.

    Đối với vấn đề chữ hoa, thực ra là trong game vẫn hiển thị đầy đủ chữ hoa và chữ thường. Chỉ là cùng một kiểu chữ nên khó nhận ra. Đối với chữ thường, phần code trong game sẽ làm chữ nhỏ hơn phần chữ hoa nhưng thực chất vẫn dùng cùng một texture với chữ hoa. Đối với vấn đề thiếu ký tự có dấu cho tiếng Việt, ta có thể giải quyết dễ dàng nếu để ý các phiên bản ngôn ngữ khác của GOW GOS. Cụ thể là bản tiếng Nhật, với số lượng chữ Kana và chữ Hán lên tới hàng ngàn chữ. Nhưng làm thế nào để mở rộng bản font đặng còn thêm chữ vào? Vấn đề này tôi sẽ giải thích ở phần sau.

    Còn bây giờ, việc cần làm là xác định vị trí của text trong Iso. Hay nói chuẩn xác hơn là xác định kiểu encoding của text. Text encoding có nhiều kiểu, chẳng hạn kiểu đơn giản nhất là chuẩn Ascii với các ký tự La Tinh cơ bản không dấu tương ứng với mỗi con số (hệ thập lục) như 41=A, 42=B, 6A=j, 6B=k,.... Ngoài ra còn nhiều kiểu encoding khác phức tạp hơn như Shift-Jis, Unicode,... và cũng có những kiểu encoding không theo một quy chuẩn nào, thường thấy trong game thế hệ cũ như Nes, Snes, Saturn, PSX,... Với những kiểu encoding khác nhau thì tuy cùng một chữ cái nhưng giá trị tương ứng của nó là khác nhau. Chẳng hạn nếu theo chuẩn Ascii thì chữ "A" tương đương với số 41, nhưng theo một chuẩn khác thì chữ này có thể là số 5C,....

    1. Xác định encoding theo cách thủ thuật

    Cách làm này mang tính hú họa, đoán mò, được chăng hay chớ và sẽ không áp dụng được nếu như game không dùng kiểu Ascii hoặc nếu text bị mã hóa/nén. Nhưng may mắn thay là GOS bản tiếng Anh và các ngôn ngữ Âu châu khác đều dùng Ascii, và cũng không bị mã hóa hay nén nên ta có thể áp dụng trong trường hợp này. Dù sao đây cũng là cách đơn giản nhất, ít tốn thời gian nhất nên cứ thử.

    Đầu tiên, tìm một đoạn text ở vị trí thuận tiện nhất, chẳng hạn ở gần save point hoặc đoạn mở đầu.

    [​IMG]

    Như đã biết, mỗi chữ La Tinh trong kiểu encoding Ascii tương đương với một con số thập lục. Như vậy, phần text trong đoạn mở đầu sẽ nằm đâu đó trong Iso.
    Tiếp theo, mở Iso bằng Hex editor và tìm kiếm chuỗi text:

    "Kratos, the Ghost of Sparta"

    hoặc có thể dò tìm những cụm từ khác trong câu trên.

    kết quả ta tìm được địa chỉ dòng text trên.

    [​IMG]

    Áp dụng cách chia địa chỉ cho $800 để tính LBA như ở phần trước, ta biết được phần text này nằm trong file DATA\ENGLISH\R6_INTRO.BIN

    2. Xác định encoding theo cách khoa học

    Trong phần trước ta đã xác định được font bằng cách set write break point cho texture. Kết quả cho biết là chức năng syscall zz_sceIoRead là thứ đã ghi dữ liệu texture từ UMD (Iso) vào Ram. Và zz_sceIoRead sử dụng đường dẫn đến file mà zz_sceIoOpen đã mở trước đó. Lần này ta không biết trước dữ liệu (text) được ghi vào Ram là những gì, nên không thể set write break point. Nhưng ta đã biết zz_sceIoRead là thứ ghi dữ liệu vào Ram, vậy có thể set execute break point ở zz_sceIoRead để CPU dừng xử lý mỗi khi nó gặp phải syscall này. Cách set như hình dưới.

    [​IMG]

    (Từ debug console, chuyển từ tab Regs sang tab Funcs, kéo xuống dưới để thấy các syscall và double click vào syscall cần set)

    Ngoài ra, còn một chức năng khác cũng có thể đọc dữ liệu từ file trong UMD và ghi vào Ram, là sceIOReadAsync. Do vậy nên set break point ở cả hai syscall này.

    Sau khi set break point, khởi động lại game. Có thể có nhiều lần CPU dừng xử lý khi gặp hai syscall ta vừa set, vì có thể nó cần mở nhiều file khác nhau, trong đó có file ta cần xác định.

    Sau nhiều lần CPU dừng, ta có thể xác định sceIOReadAsync mới là chức năng đọc chuỗi text trên.

    [​IMG]

    Lần lượt click Step Into để theo dấu từng lệnh xử lý. Lúc này ta thấy $a1 chứa giá trị địa chỉ mà CPU đọc chuỗi text từ Ram (0x9DE9C00) và $s1 là địa chỉ phần đường dẫn đến file đã mở (0x9DE01F0).

    [​IMG]

    Như vậy ta đã xác định được chuỗi text cần tìm nằm ở file nào.

    Lưu ý: nên save state trước đoạn cần debug, vì có thể các địa chỉ ghi vào Ram thay đổi mỗi lần. Khi save state ngay trước đoạn cần debug, vì như thế ta đã save luôn cả "chủng tử" (seed) cho số ngẫu nhiên được chọn làm địa chỉ ghi Ram. Tham khảo thêm bài này (https://gokuraku-shujo.blogspot.com/2016/10/so-ngau-nhien.html) để biết thêm về số ngẫu nhiên.


    3. Tìm Pointer

    Pointer là con số chỉ đến địa chỉ bắt đầu của từng câu thoại. Vì sao phải dùng nó? Vì nó giúp cho việc viết lời thoại (text) trong game được linh động hơn. Khi nhà sản xuất thay đổi câu chữ, làm độ dài của chuỗi thay đổi theo thì họ không phải mất công sửa đổi thôi. Nói nôm na, Pointer giống như một dạng label.

    Đọc bài này (https://gokuraku-shujo.blogspot.com/2015/03/dich-thuat-ff9.html) để hiểu thêm về Pointer.

    Sau khi xác định được file chứa text, mở file bằng Hex editor và ta dễ dàng xác định được Pointer bằng mắt thường đối với những ai đã quen với việc này:

    - Gồm 4 byte (thường chỉ sử dụng 2 byte cuối vì GOS không có text nào đủ dài để dùng đến 2 byte đầu)
    - Mỗi Pointer cách nhau 0x0E byte

    Tuy nhiên cách xác định bằng mắt thường chỉ áp dụng cho trường hợp file nhỏ, người đã quen. Còn đối với những trường hợp khác thì nên sử dụng debugger để tìm, cách thức tương tự như những phần trước.

    Chẳng hạn, câu thoại đầu tiên được đọc từ vị trí 0x9DEA1C3 trong Ram. Chỉ cần set read break point tại địa chỉ đó thì CPU sẽ dừng lại ngay trước lệnh đọc tại địa chỉ này. Từ đó ta truy ngược code xử lý và sẽ thấy đoạn:

    [​IMG]

    08ABB4E0 lw v0,0x4(t0) #đọc giá trị tại địa chỉ $t0 + 4, chứa vào $v0. Địa chỉ $t0 là 0x9DE9E50 + 4 = 0x9DE9E54. Giá trị tại địa chỉ này là 000002BB.
    08ABB4E4 addu v0,a0,v0 # lấy giá trị hiện tại của v0 + giá trị của a0 rồi ghi vào v0. Giá trị hiện tại của v0 là 0x9DE9F08, vị trí bắt đầu của khối text.

    Như vậy có thể thấy mối liên kết giữa Pointer và địa chỉ bắt đầu câu thoại trong GOS như sau:

    - Địa chỉ bắt đầu text = địa chỉ bắt đầu khối text + giá trị Pointer

    Đây là điều hết sức bình thường và quen thuộc với các hệ máy console.

    (Còn tiếp)




     
    RomHackingVN thích bài này.
  7. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    "Sau nhiều lần CPU dừng, ta có thể xác định sceIOReadAsync mới là chức năng đọc chuỗi text trên."

    Xác định bằng cách nào ? (nếu custom encoding)

    Giả sử khoảng game đọc >100 block thì cách này không ổn rồi.facepalm~~
    Có cách nào tối ưu hơn không ?

    ---
    "..truy ngược code xử lý và sẽ thấy đoạn..."
    Có vẻ đơn giản nhưng thực tế có phải vậy ?

    Ps: Mục thủ thuật thì có vẻ ai cũng làm được,
    nhưng "khoa học" thì đâu vài ba giải thích được,
    (cảm giác phần sau cũng đặt bp on read từ text rồi tra ngược ra được glyph table (hay VWF gì đó))smoking~~
    hy vọng phần sau sẽ có video demo chi tiết cho mục "khoa học".
    (Để mọi người thấy "simple" đến mức nào8-|)
     
  8. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    Nếu là người hay debug thì bạn hiểu là có những lúc ta không chắc phải set break point ở điểm nào, và lúc đó chỉ còn cách loại trừ từng cái một.
    Trong trường hợp này, đặt cả Oread và OreadAsync thì chỉ có OreadAsync phản ứng, vậy là loại trừ được cái kia. Sau khi xác định được text block nằm chỗ nào rồi thì bước tiếp theo là xác định kiểu encoding của nó đâu có khó, chỉ là thay đổi và quan sát kết quả thôi.


    Bạn nói đúng. Nói chỉ vỏn vẹn một dòng nhưng để ra được một dòng đó thì khá mất thời gian. Dân chuyên disassemble hiểu điều này, tìm ra được một manh mối rồi truy lùng trong đống log để tìm manh mối tiếp theo, cứ như thế cho đến khi tỏ tường hết. Nhưng không phải là làm mò, mà là dựa vào những căn cứ hợp lý và logic.

    Đó là thế giới của dân disassemble, ở đây không đi sâu vào chi tiết mà chỉ giới thiệu hướng đi chính, vì mục đích của bài này là cho nhiều người đọc chứ không phải một vài người trong giới.

    Mấy bài kiểu này đã ít người đọc, nếu đi sâu vào nữa càng ít hơn.

    Nếu muốn coi quá trình đào sâu thì có thể tham khảo clip dưới đây.

    Đề bài:



     
  9. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    Mình nghĩ theo cách của số ít lẫn nhiều đây.

    ---
    Lý thuyết thì có hàm đọc -> đổi vài thứ đọc được -> quan sát -> font, encode,... mọi thứ. Ờ đúng rồi.

    Vậy game đọc đi đọc lại vài trăm block thì cũng mò?moustache~~
    (cách này kiểu brute force, mà đã bf thì chỉ khi bí đường)
    *Tựa cách thủ thuật, cứ đoán từng file rồi thay đổi chắc chắn sẽ tìm thấy cái cần tìm. Nhiều khi lại nhanh hơn cách này. (à mà bài này toàn thấy thủ thuật tốt hơn debug)

    Chưa nói đến block đọc đã mã hóa, thay đổi block -> crash game.
    Còn cách nào khác không? (chỉ hỏi lại)

    ---
    "...dân debug...thế giới của dân disassemble...",

    Nói vậy có vẻ các mục sau sẽ không có gì mới.

    Thấy "some simple" nghĩ ai cũng làm được mà không gặp bất kỳ khó khăn nào chứ,
    hóa ra chỉ dân "trong nghề" mới thực hành được thôi sao?

    1 đoạn up mở + 1 clip ngắn có giảm người đọc?
    Mà biết đâu, >3k view bài này chỉ hóng link download. ( nhìn vào comment là hiểu)
    (Hay là...]:). Thả thính detected, AE lưu ý nhégiggle~~)

    Ps: Vài ý kiến cá nhân.|-)
     
  10. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    Cách debug phải dựa trên cơ sở lập luận: tại sao bạn chọn đoạn này mà không chọn đoạn kia? Bản thân việc đó đã loại trừ được rất nhiều trường hợp vô nghĩa không cần mò tới, chứ không giống như cách đoán mò rồi thử từng file trong Rom. Nhiều game mở ra còn chẳng thấy file nào ngoại trừ cái cục Iso to tướng.

    Còn trong việc debug cũng có nhiều cách tiếp cận, có cách này nhanh gọn hơn cách kia. Trong trường hợp này thì chưa nghĩ ra được cách nào nhanh hơn. Và cách chọn Oread và OreadAsync cũng không chuyện nó đọc hàng trăm block đâu (không nhớ chính xác là bao nhiêu, nhưng đọc đâu đó là nó chỉ có thể mở/đọc cùng lúc với giới hạn là bao nhiêu file)

    Nếu bạn có đọc những bài hướng dẫn khác tôi viết thì sẽ thấy có điểm chung với bài này:

    - Giới thiệu
    - Có vẻ đi sâu
    - Nhưng thực tế không đủ sâu để răm rắp làm theo là được

    Vì mục đích của tôi chỉ là khơi dậy trong người đọc sự hứng thú tìm hiểu, khi đó có ai tò mò thắc mắc gì thêm thì mới tiếp. Cộng đồng dịch game giờ cũng khá mạnh rồi, nhưng chủ yếu vẫn dựa trên các thủ thuật đơn giản để sống chứ chưa thực sự dựa trên nền tảng lý luận và kiến thức để làm.
    Từ trước đến nay mục đích này vẫn chưa đạt được. Có lẽ bạn là người duy nhất hỏi nhiều đến khía cạnh chuyên sâu hơn.

    P/s: lý do chính để không đi kỹ vào phần debug vì khá mất thời gian. Ở đây chỉ gợi ra hướng đi chính. Ai có hứng thú thì tự họ tiến hành thì sẽ được thôi.
     
    #30 Yugisokubodai, 16/1/17
    Chỉnh sửa cuối: 16/1/17
  11. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    Mỗi kỹ thuật đều có ưu/nhược điểm của riêng mình.
    Mỗi người sẽ có những thủ thuật khác nhau,
    thủ thuật của bạn không cần lập luận, phân tích nhưng với người khác thì khác.

    --
    Lúc game render text, từ đống rendered đó truy ngược code xử lý -> text. (cả font, glyph).
    Câu này chắc đủ cho mấy mục "khoa học".|-)

    ---
    Mình không hỏi chuyên sâu, mặc định nó đã vậy.

    Ví dụ minh họa: Cách thực tế vs Cách của bài viết 8-|

    Thực tế là kỹ thuật assembly hacking không hề simple mà là khó khăn nhất (rõ hơn: xem smwiki, wikipedia).
    Biết chắc câu trả lời nhận được sẽ chung chung theo những yêu cầu kiến thức của kỹ thuật này,
    (Ví dụ: học asm, phần cứng, lập trình, ...),
    và nó chẳng giúp được gì cho người hỏi.

    ---
    Ps: "tiến hành thì sẽ được", vậy à ?
    Không biết có phải muốn show hàng không, nhưng đã là hướng dẫn thì ít nhất
    người khác phải thực hành theo được, nếu không vui lòng nêu rõ trong bài viết.
     
  12. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    Quên mất một cách: truy từ Vram. Từ hình ảnh (chữ) trong Vram có thể truy ra block DMA nào chuyển dữ liệu vào đó, từ đó có thể biết được text được đọc từ đâu để chuyển vào Ram.

    Đúng, nó là khó khăn nhất lúc mới bắt đầu. Bản thân tôi cũng tốn nhiều năm (tôi bắt đầu để ý assembly từ năm 2009, mấy năm liền không hiểu gì mặc dù đã dịch được vài game từ những năm đầu, bằng những thủ thuật) để hiểu nó là cái gì. Nhưng một khi đã bắt được nhịp rồi thì nó lại là cách dễ dàng nhất, và hiệu quả nhất, làm gì cũng được. Những cách thủ thuật thì không phải lúc nào cũng dùng được, vì vậy nên qua nhiều bài hướng dẫn khác nhau, tôi khuyến khích người đọc tìm hiểu thêm những kiến thức về:

    - Lập trình, chủ trọng vào low level hơn vì nó liên quan phần dưới
    - Phần cứng [*]

    Bạn nói những kiến thức này không giúp ích được gì thì e rằng bạn chưa bao giờ hack thứ gì đó thật sự nghiêm túc. Khi không có source thì thứ duy nhất ta dựa vào được là kiến thức về phần cứng + assembly.


    Dĩ nhiên là làm theo thì sẽ được, nếu không được thì bài này hướng dẫn tào lao. Nhưng việc hướng dẫn có mức độ, tôi chỉ nêu ra hướng đi chính và những thứ khác thì ai có hứng thú sẽ tự tìm tòi thêm.

    P/s: tôi nghĩ trước giờ mình viết hướng dẫn thì có bài này là cụ thể nhất, như đúng ý bạn nói.
    Và cũng không có nhiều người quan tâm.

    [*] Nói phần cứng là nói đến hardware register, cách hoạt động của nó,...
     
  13. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    Mình comment dài quá rồi.facepalm~~
    Ngắn gọn: trả lời như không trả lời.

    Ps: Chắc viết mấy hướng dẫn này để note lại cho riêng mình, tiếp tục đi.clap~~
     
  14. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    Đây là phần dành cho bạn [DC] và những ai quan tâm.

    Hướng dẫn cụ thể hơn cho phần tìm Pointer.

    Tiền đề là đã xác định được text trong Ram cũng như trong Iso.
    Như trong hình, đoạn text mở đầu game nằm ở vị trí 0x9DE9183. Giờ tìm pointer của nó như sau:

    [​IMG]

    Giờ ta đặt Read break point tại 0x9DE9183. CPU sẽ dừng xử lý ngay trước khi nó đọc địa chỉ này.

    [​IMG]

    Và ta được:
    - Tại 0x8807804 thực hiện lệnh: LB t1, 0x0 (a3) với ý nghĩa: ghi giá trị của 01 byte tại địa chỉ chứa trong a3 vào t1. Lúc này a3 = 9DE9183, tức địa chỉ của câu thoại ta đang cần tìm pointer.

    [​IMG]

    Giờ ta truy ngược về trước để tìm ra giá trị 9DE9183 được ghi vào a3 từ lúc nào. Nhìn lên 5 dòng lệnh, ta thấy:

    - Tại 88077F0 thực hiện lệnh: Move a3, a1. Có nghĩa là giá trị 9DE9183 của a3 được chuyển từ a1 sang.
    - Giờ ta lại truy ngược, giá trị này từ đâu đưa vào a1.
    - Nhìn lên 4 dòng ta thấy: tại z_un_88077E4 thực hiện lệnh: Andi a3, a1, 0x3.
    - Như vậy có 2 khả năng xảy ra: dòng chảy của lệnh chảy từ 88077E0 đến 88077E4; hoặc từ một nơi khác nhảy đến 88077E4.
    - Thử đặt Execute break point tại 88077E0 (bằng cách double click địa chỉ này trong cửa sổ debug), load lại game thì thấy CPU không dừng. Điều này chứng tỏ dòng lệnh không chảy từ 88077E0 đến 88077E4.
    - Vậy làm sao xác định được dòng lệnh chảy từ đâu đến 88077E4? Hãy xem register RA, hoặc click vào tab RA.
    - Register RA chứa địa chỉ trở về sau khi dòng lệnh nhảy đến một nơi nào đó (ở đây là 88077E4).
    - Giá trị hiện tại của RA là 8A56304.

    [​IMG]

    Như vậy có thể thấy:

    - Tại 8A562F8 thực hiện lệnh: Move a1, s3: chuyển nội dung của s3 sang a1
    - Sau đó tại 8A562FC thực hiện lệnh: Jal z_un_08877e4: nhảy đến subroutine 8877E4 (trước khi nhảy thực hiện Move a2, aw). Sau khi hoàn thành mọi công việc ở subroutine này, dòng lệnh lại trở về 8A56308.
    - Điều này cho thấy giá trị 9DE9183, địa chỉ của text, bắt nguồn từ s3. Giờ ta tiếp tục truy từ đâu chuyển vào s3.


    [​IMG]

    - Nhìn lên trên để truy ngược dòng lệnh, ta thấy tại 8A56274 xử lý: Move s3, s1. Để ý thì thấy lúc này giá trị của s3 là 84001E0, và sau lệnh này thì nó sẽ mang giá trị 9DE9183 nhận được từ s1.
    - Tiếp theo ta truy tìm 9DE9183 được ghi vào s1 từ khi nào.
    - Lại tiếp tục dò ngược về trước, ta thấy tại 8A561F8 xử lý: Move s1, a2. Cho thấy giá trị 9DE9183 xuất phát từ a2. Giờ lại truy tìm từ đâu ghi vào a2.

    [​IMG]

    - Lại nhìn lên trên cách 4 dòng code, thấy: z_un_8A561E8. Làm tương tự như chỗ z_un lần trước, ta xác định được dòng chảy của code là từ 8A54834: Move a2, t0 rồi nhảy đến subroutine z_un_8A561E8. Trước khi nhảy đến đây thì CPU đã chuyển giá từ t0 sang a2. Vậy ta tiếp tục dò tìm từ đâu chuyển vào t0.
    - Tại 8A54820 ta thấy: Move t0, a1. Vậy giá trị 9DE9183 xuất phát từ a1.

    [​IMG]

    - Để ý mũi tên màu da cam chỉ vào 8A54820: Move t0, a1. Mũi tên này cho hay đã có sự phân nhánh từ trước đó, tại vị trí nào đó rẽ sang 8A54820.
    - Giờ kéo lên trên và ta thấy đầu mũi tên xuất phát từ 8A54774: BEQ t0, zero, 0x08A54820. Nếu t0 = 0 thì sẽ nhảy tới 8A54820. Thử đặt Execute break point tại 8A54774 thì sẽ thấy giá trị t0 lúc này là 4B, khác 0. Như vậy dòng chảy không nhảy cóc đến 8A54820 mà thực hiện từng lệnh một đến địa chỉ 8A54820. Và a1 lúc này vẫn là 9DE9183.

    [​IMG]

    - Nhìn lên thấy z_un_8A54758: Addiu sp, sp, -0x10. Giống như trước, xem giá trị RA, lúc này là: 8A5291C. Như vậy dòng chảy của lệnh xuất phát từ 8A52XYZ nhảy đến subroutine z_un_8A54758.
    - Đặt Execute break point tại 8A52914: Jal z_un_8A54758. CPU dừng ngay trước lệnh này, và nhìn lên 3 dòng ta thấy tại 8A52908: Move a1, a2.
    - Điều này cho thấy 9DE9183 được chuyển từ a2 sang a1. Ta tiếp tục tìm nó từ đâu chuyển vào a2.

    [​IMG]

    - Tiếp tục nhìn lên, tại z_un_8A528D8: Addiu, sp, sp, -0x60. Giống như trước, bằng cách xem RA ta dễ dàng xác định dòng chảy xuất phát từ 8ABBXYZ nhảy đến 8A528D8.
    - Tại 8ABBE9C: Move a2, s2. Lệnh này cho thấy giá trị 9DE9183 được chuyển từ s2 sang a2.

    [​IMG]

    - Chú ý tại 8ABBE88: Jal z_un_880CE3C. Có thể khi nhảy tới routine 880CE3C thì s2 nhận được giá trị 9DE9183 từ đâu đó. Nhưng khi đặt thử Execute break point tại 8ABBE88 thì ta thấy s2 vẫn đang giữ giá trị 9DE9183. Nghĩa là nó đã nhận từ trước đó và không liên quan đến subroutine z_un_880CE3C.
    - Từ 8ABBE88: Jal z_un_880CE3C truy ngược về trước 3 dòng, ta thấy tại 8ABBE7C: move s2, a1. Vậy giá trị của s2 đến từ a1.
    - Nhìn lên z_un_8ABBE64: Addiu, sp, sp, -0x20. Tương tự như trước, xem RA để xác định dòng code chảy từ đâu đến đây.

    [​IMG]

    - Giá trị Ra lúc này là 8ABBF40, cho thấy dòng code chảy từ 8ABBFXY đến z_un_8ABBE64.
    - Tìm đến 8ABBF40, nhìn lên trên, tại 8ABBF34: move a1, v0. Điều này cho thấy giá trị 9DE9183 đến từ v0.

    [​IMG]

    - Từ 8ABBF34: move a1, v0 nhìn lên 3 dòng: 8ABBF28: JAL z_un_8ABB4B0. Đặt Execute break point tại đây thì thấy v0 lúc này mang giá trị 9DE8E68, không phải giá trị ta cần tìm. Như vậy có thể kết luận 9DE9183 đến từ đâu đó trong subroutine z_un_8ABB4B0 này.

    [​IMG]

    - Sau khi nhảy đến z_un_8ABB4B0, quan sát routine này (hoặc click vào "Step Into" để bám theo từng lệnh một) ta thấy có đoạn:

    8ABB4E0: LW v0, 0x4 (t0) chứa giá trị tại địa chỉ (t0 + 4) vào v0.
    8ABB4E4: Addu v0, a0, v0: v0= a0 + v0.

    Như vậy đây là 2 dòng xử lý làm thay đổi giá trị của v0.

    [​IMG]

    Đi vào chi tiết từng dòng lệnh:

    8ABB4E0: LW v0, 0x4 (t0) chứa giá trị tại địa chỉ (t0 + 4) vào v0.

    Lúc này t0= 9DE8E10. Vậy ta được v0= giá trị tại (9DE8E10 + 4 = 9DE8E14). Nhảy tới địa chỉ 0x9DE8E14 trong Ram, ta được giá trị 02BB.

    (PSP kiểu Little Endian)

    [​IMG]




    Ở dòng tiếp theo:

    8ABB4E4: Addu v0, a0, v0: v0= a0 + v0. Lúc này a0= 9DE8EC8. Dễ dàng nhận thấy đây là địa chỉ bắt đầu của cả khối text.

    Như vậy v0= 9DE8EC8 + 02BB=9DE9183. Đây chính là địa chỉ bắt đầu câu text trong đoạn mở đầu mà ta cần tìm.

    [​IMG]

    Như vậy có thể kết luận: giá trị 02BB tại 9DE8E14 trong Ram chính là Pointer chỉ đến địa chỉ câu text tại 9DE9183. Từ đó dễ dàng tìm ra vị trí của Pointer này trong file R6_INTRODUCE.BIN bằng chức năng tìm kiếm của Hex editor.

    Và ta được công thức: địa chỉ bắt đầu text = địa chỉ bắt đầu cả khối text + giá trị Pointer.
     
  15. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    drunk~~
    Nếu xác định địa chỉ chuổi kế tiếp sẽ dễ hơn.
    Ví dụ: "Ares was dead" -> hex: 41726573207761732064656164

    - Mở game đến câu thoại đầu tiên, -> bấm pause để dừng game.
    - Tại 'PPSSPP', bấm Crtl+M để mở 'Memory View', chọn (User memory).
    - Bấm Ctrl+F nhập chuổi hex -> chọn OK -> xong.

    [​IMG]

    Chỗ truy ngược về 88077E4, còn 1 cách (biết rồi thì thôi nhé) là
    xem tab Stack frames, nó sẽ hiện đống từ 88077E4 trở về trước.
    (sau đó phân tích như bài viết, khỏi out từ từ nữa)

    Ảnh : Bản EU có chút khác biệt (do compiler)
    [​IMG]
    ps: trường hợp có cơ bản mips thì tham số hàm sẽ từ a0->a3. (quy ước là vậy)
    lúc này trace or đặt breapoint vào 1 số hàm trong stack (thử).

    Khi breakpoint nếu có tham số aX mong đợi thì, xem stack->suy luận, không -> gỡ breakpoint.
    (còn biết xài bp condition thì tốt, tùy sở thích nhé)
    [​IMG]
    Một phần chi tiết là quá đủ rồi (đã gồm debugging skills + asm).
    Ở đây...
    Số người hack với tool sẵn > thủ thuật > debugger = 1.
    Đọc mã high level còn không hiểu huống gì đến asm.
    Họ quan tâm cũng nuốt không nổi đâu, (cả mình).

    Giỏi reversing engineering thì chuyển qua chơi với cracker/hacker vui hơn nhé.
    Đoán vậy.rofl~~
     
  16. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    Cách tìm text trong Memory OK, nó cũng như cách tìm trong file sau khi xác định được file. Nhưng nếu text không phải Ascii thì cũng không áp dụng được.
    Còn cách tìm trong stack thì tôi nghĩ là cũng khó áp dụng, vì ta tìm được địa chỉ trong đống stack đó nhưng rất khó xác định đó là gì. Còn cách truy ngược dòng lệnh có thể lâu hơn nhưng ta có thể quan sát, nắm được các hoạt động liên quan tới địa chỉ đó, từ đó biết được nó là cái gì.

    P/s: thực ra nếu để cho con người viết thì sẽ không có chuyện chuyển một giá trị lòng vòng qua lại nhiều lần giữa các register. Cái này chắc là do compiler chuyển đổi từ high language sang low language nên không hiệu quả.
     
    #36 Yugisokubodai, 21/1/17
    Chỉnh sửa cuối: 21/1/17
  17. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    Sai rồi, encoding nào cũng chuyển sang hex được, -> search được.
    Hex search của ppsspp chưa hoàn thiện, nên dump RAM để dùng tool khác để search (vd: cheat engine, relative search,...).
    Trường hợp block encode, thì lúc vẽ thường đã decode, nên cách này hiệu quả.
    Xa xôi hơnIdea~~, Vậy nếu game decode từng câu lúc vẽ thì sao ?
    Không dùng được cách này chứ saorofl~~
    Tùy thủ thuật cá nhân nhé.

    Chỉ là một cách khác.
    Mình không nói suy được ngay, thấy được tổng quan. Vẫn dùng nhiều kỹ thuật debug ở đoạn này, nên đọc lại comment. Tùy kỹ thuật cá nhân nhé.

    Ps: Dev mới hiểu, Yo.ninja~~
    Hiệu quả hay đủ hiệu quả ? Ai mà biết họ cần gì.
    Tùy khả năng dùng lại của hàm.
    Lúc code có chia ra nhiều hàm, nhưng compiler sẽ tùy trường hợp để có speed nhanh nhất (gộp nếu cần thiết (inline), ...nhiều phương pháp private khác).
    Sẽ có nhiều trường hợp, ai biết dev đến lv nào, nên tự kiểm tra.
     
  18. Yugisokubodai

    Yugisokubodai Thủ lĩnh nhóm khác
    Đồng Sự

    Tham gia ngày:
    26/12/15
    Bài viết:
    239
    Đã được thích:
    1,056
    Đã đọc kỹ thì thấy bạn nói: tìm trong Ram khi đã biết encoding.
    Đặt trường hợp chưa biết dạng encoding thì có tìm được text trong Ram?
     
  19. [DC]

    [DC] Seek and Destroy
    Thành viên BQT

    Tham gia ngày:
    2/8/16
    Bài viết:
    48
    Đã được thích:
    14
    RAM<=>File, mà tìm thì nó thành thủ thuật rồi, thử relative search,....
    Vẫn fail, phải quay lại cách #26 rồi rofl~~(mình k xài cách này)|-)

    Ps: Google, Thủ thuật first.bandit~~
    Cùng đường -> debug+thủ thuật.ninja~~
    Tuyệt vọng -> analyze+debug.moustache~~
     
    #39 [DC], 21/1/17
    Chỉnh sửa cuối: 21/1/17
  20. qwe1300

    qwe1300 Thành viên mới

    Tham gia ngày:
    29/8/16
    Bài viết:
    13
    Đã được thích:
    0
    Hướng dẫn phần mở rộng font thêm chữ đi bác.
    Chắc có thể áp dụng cho các hệ máy khác?
     
Tags:

Chia sẻ trang này