Try   HackMD

Hack The Box - Reversing (Easy Part 4)

image

Ghi chú:

Difficulty: Easy

Challenge 22: RAuth

My implementation of authentication mechanisms in C turned out to be failures. But my implementation in Rust is unbreakable. Can you retrieve my password?

Download Challenge Here

Solution

Chúng ta có thể sử dụng lệnh 'file' để cung cấp cái nhìn tổng quan về loại tệp.

image

Mở chương trình trong IDA.

Thoạt đầu quan sát, chương trình yêu cầu đầu vào là một chuỗi. Sau một vài lần gỡ lỗi, tôi nhận ra rằng chúng tôi cần vá tại 0x690D từ jz sang jnz và chúng tôi đã nhận được:

image

Không thể dễ thế! Dành nhiều thời gian hơn để đọc, tôi nhận thấy nó truy cập vào “salsa20” rất nhiều lần. Sau khi tìm kiếm, chúng tôi nhận được nhiều thông tin chi tiết về Salsa20.

image

Trạng thái ban đầu của Salsa20 được tạo thành từ mười sáu từ 32 bit được sắp xếp dưới dạng ma trận 4×4 bằng cách sử dụng keynonce. Chúng ta cần tìm 2 tham số. Ta có trạng thái ban đầu của Salsa20, nó sử dụng:

  • ef39f4f20e76e33bd25f4db338e81b10key (xmmword_39CA0 + xmmword_39CB0)
  • d4c270a3nonce
  • 05055FB1A329A8D558D9F556A6CB31F324432A31C99DEC72E33EB66F62AD1BF9cipher (xmmword_39CC0 + xmmword_39CD0)
  • 193978899768A08F66D39017B2E040C237193763C581E261fake_flag

Chúng ta rút gọn ma trận: expaef39f4f20e76e33bnd 3d4c270a3 2-byd25f4db338e81b10te k

65787061 65663339 66346632 30653736 
65333362 6E642033 64346332 37306133
00000000 00000000 322D6279 64323566
34646233 33386538 31623130 7465206B

image

Phần còn lại là giải mã Salsa, bạn có thể đọc nếu muốn. Cách chương trình in mật khẩu giống như cách in cờ. Bây giờ chúng ta viết mã để giải quyết phần còn lại.

from Crypto.Cipher import Salsa20

key = b"ef39f4f20e76e33bd25f4db338e81b10"  
nonce = b"d4c270a3"  
# Password: TheCrucialRustEngineering@2021;)
cipher = Salsa20.new(key=key, nonce=nonce)
data = (bytes.fromhex("05055FB1A329A8D558D9F556A6CB31F324432A31C99DEC72E33EB66F62AD1BF9"))
password = cipher.decrypt(data)
print(password)
# Fake flag: HTB{F4k3_f74g_4_t3s7ing}
cipher = Salsa20.new(key=key, nonce=nonce)
data = ((bytes.fromhex("193978899768A08F66D39017B2E040C237193763C581E261")))
fakeflag = cipher.decrypt(data)
print(fakeflag)

Bây giờ chúng ta có thể bắt đầu làm việc trên dịch vụ từ xa.

image

Flag: HTB{I_Kn0w_h0w_t0_5al54}

Challenge 23: Partial Encryption

Static-Analysis on this program didn't reveal much. There must be a better way to approach this

Download Challenge Here

Solution

Chúng ta có thể sử dụng lệnh 'file' để cung cấp cái nhìn tổng quan về loại tệp.

image

Mở chương trình trong IDA. Tôi chỉ biết sub_7FF775DF1050 với tham số giải mã để lấy hàm được giải mã nhưng tôi không biết thuật toán. Nếu ai có hình có thể bình luận bên dưới.

image

image

image

image

image

Flag: HTB{W3iRd_RUnT1m3_DEC}

Challenge 24: Golfer - Part 1

A friend gave you an odd executable file, in fact it is very tiny for a simple ELF, what secret can this file hide?

Download Challenge Here

Solution

Chúng ta có thể sử dụng lệnh 'file' để cung cấp cái nhìn tổng quan về loại tệp.

image

Mở chương trình trong IDA.

  0800004C                 public start
  0800004C start                                  
  0800004C                 jmp     start_0
  08000051 ; ---------------------------------------------------------------------------
  08000051                 inc     bl
  08000053                 inc     dl
  08000055                 mov     ecx, 800000Ah
  0800005A                 call    loc_800012F
  0800005F                 mov     ecx, offset byte_8000008
  08000064                 call    loc_800012F
  08000069                 mov     ecx, offset dword_8000024
  0800006E                 call    loc_800012F
  08000073                 mov     ecx, 800000Eh
  08000078                 call    loc_800012F
  0800007D                 mov     ecx, 800000Ch
  08000082                 call    loc_800012F
  08000087                 mov     ecx, 8000023h
  0800008C                 call    loc_800012F
  08000091                 mov     ecx, offset byte_8000009
  08000096                 call    loc_800012F
  0800009B                 mov     ecx, 8000021h
  080000A0                 call    loc_800012F
  080000A5                 mov     ecx, offset byte_8000006
  080000AA                 call    loc_800012F
  080000AF                 mov     ecx, 800000Dh
  080000B4                 call    loc_800012F
  080000B9                 mov     ecx, 8000022h
  080000BE                 call    loc_800012F
  080000C3                 mov     ecx, 8000021h
  080000C8                 call    loc_800012F
  080000CD                 mov     ecx, offset byte_8000005
  080000D2                 call    loc_800012F
  080000D7                 mov     ecx, 8000021h
  080000DC                 call    loc_800012F
  080000E1                 mov     ecx, offset dword_8000020
  080000E6                 call    loc_800012F
  080000EB                 mov     ecx, 8000023h
  080000F0                 call    loc_800012F
  080000F5                 mov     ecx, 800000Fh
  080000FA                 call    loc_800012F
  080000FF                 mov     ecx, offset byte_8000007
  08000104                 call    loc_800012F
  08000109                 mov     ecx, 8000022h
  0800010E                 call    loc_800012F
  08000113                 mov     ecx, 8000025h
  08000118                 call    loc_800012F
  0800011D                 mov     ecx, 800000Bh
  08000122                 call    loc_800012F
  08000127
  08000127 start_0         proc near              
  08000127                 xor     al, al
  08000129                 inc     al
  0800012B                 mov     bl, 2Ah ; '*'
  0800012D                 int     80h
  0800012F
  0800012F loc_800012F                           
  0800012F                                        
  0800012F                 push    ebp
  08000130                 mov     ebp, esp
  08000132                 mov     al, 4
  08000134                 int     80h
  08000136                 leave
  08000137                 retn
  08000137 start_0         endp

Sau khi tìm kiếm về NASM, tôi tìm thấy một số thông tin hữu ích

image

Chương trình cố gắng gọi sys_exit. Ta có thể bỏ qua nó và đọc đoạn trên, nó sẽ gọi sys_write với kí tự của flag được đưa vào ecx

image

image

Flag: HTB{y0U_4R3_a_g0lf3r}

Challenge 25: Up a Stream

Java streams are great! Why use 10 lines of completely readable code, when you could mash it all into 67 characters? +1, PR Approved!

Download Challenge Here

Solution

Ta nhận được tệp stream.jarouput.txt. Tôi giải nén stream.jar và nhận được Challenge.class. Sử dụng https://www.decompiler.com/ để mở Challenge.class, ta nhận được tệp Challenge.java.

import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Challenge {
   public static void main(String[] var0) {
      String var1 = "FLAG";
      Stream var10000 = dunkTheFlag(var1).stream();
      PrintStream var10001 = System.out;
      Objects.requireNonNull(var10001);
      var10000.forEach(var10001::println);
   }

   private static List<String> dunkTheFlag(String var0) {
      return Arrays.asList(((String)((List)((String)((List)((String)((List)var0.chars().mapToObj((var0x) -> {
         return (char)var0x;
      }).collect(Collectors.toList())).stream().peek((var0x) -> {
         hydrate(var0x);
      }).map((var0x) -> {
         return var0x.toString();
      }).reduce("", (var0x, var1) -> {
         return var1 + var0x;
      })).chars().mapToObj((var0x) -> {
         return (char)var0x;
      }).collect(Collectors.toList())).stream().map((var0x) -> {
         return var0x.toString();
      }).reduce(String::concat).get()).chars().mapToObj((var0x) -> {
         return var0x;
      }).collect(Collectors.toList())).stream().map((var0x) -> {
         return moisten(var0x);
      }).map((var0x) -> {
         return var0x;
      }).map(Challenge::drench).peek(Challenge::waterlog).map(Challenge::dilute).map(Integer::toHexString).reduce("", (var0x, var1) -> {
         return var0x + var1 + "O";
      })).repeat(5));
   }

   private static Integer hydrate(Character var0) {
      return var0 - 1;
   }

   private static Integer moisten(int var0) {
      return (int)(var0 % 2 == 0 ? (double)var0 : Math.pow((double)var0, 2.0D));
   }

   private static Integer drench(Integer var0) {
      return var0 << 1;
   }

   private static Integer dilute(Integer var0) {
      return var0 / 2 + var0;
   }

   private static byte waterlog(Integer var0) {
      var0 = ((var0 + 2) * 4 % 87 ^ 3) == 17362 ? var0 * 2 : var0 / 2;
      return var0.byteValue();
   }
}

Đoạn mã mô tả:

  • hydrate: Giảm giá trị ASCII của mỗi ký tự đi 1.
  • moisten: Nếu số là chẵn, giữ nguyên giá trị, ngược lại, làm bình phương giá trị.
  • drench: Dịch trái số nguyên một bit.
  • dilute: Chia giá trị cho 2 và cộng lại với giá trị ban đầu.
  • waterlog: Thực hiện một phép toán phức tạp trên giá trị, sau đó chuyển đổi thành số byte. Nếu một điều kiện được đáp ứng, giá trị được nhân đôi; ngược lại, giá trị được chia cho 2.

Bản thân tôi rất ít khi làm các đề java. Tôi đã tìm hiểu các Stream API để hiểu rõ hoạt động:

  • Stream reduction operation cho phép chúng ta tạo ra một kết quả duy nhất từ các phần tử của Stream bằng cách áp dụng các phép tính lặp đi lặp lại trên các phần tử của nó. Đọc thêm tại đây.
  • Stream peek() là một phương thức được sử dụng để thực hiện hành động trên mỗi phần tử của một luồng mà không làm thay đổi luồng đó. Đọc thêm tại đây.

Ta phân tích luồng chương trình như sau:

  1. Chuyển đổi Flag thành một luồng các ký tự trong chuỗi.
  2. Gom các kí tự đó thành một List.
  3. Thực hiện phương thức hydrate() trên mỗi ký tự trong danh sách, giảm giá trị Unicode của ký tự đi một đơn vị. Tuy nhiên nó dùng trong phương thứcpeek => giá trị không đổi.
  4. Chuyển các kí tự thành chuỗi rồi gộp lại thành một chuỗi ĐÃ ĐƯỢC ĐẢO NGƯỢC. Vì họ dùng reduce("", (var0x, var1) -> { return var1 + var0x; }. Ta có ví dụ như sau: flag=HTB{minhkingkong} => '}gnokgnikhnim{BTH'
  5. Tiếp tục chuyển chuỗi đã bị đảo ngược thành danh sách các kí tự và chuyển thành giá trị nguyên. Lưu các giá trị đó vào một danh sách mới và ép kiểu các giá trị trong đó thành int.
  6. Thực hiện phương thức moisten(), drench(), waterlog(), dilute() để mã hóa danh sách trên. Tuy nhiên phương thức waterlog() dùng trong phương thứcpeek => giá trị không đổi.
  7. Chuyển đổi mỗi giá trị int thành một chuỗi thập lục phân. Nối các chuỗi thập lục phân thành một chuỗi duy nhất.
  8. Lặp lại như trên năm lần và trả kết quả để in.

Ta biết chuỗi đầu ra được lặp lại 5 lần, từ đó ta trích được chuỗi: b71bO12cO156O6e43Od8O69c3O5cd3O144Oe4O6e43O37cbOf6O69c3O1e7bO156O3183O69c3O6cO8b3bOc0O1e7bO156OfcO50bbO69c3Oc0O102O6e43OdeOb14bOc6OfcOd8O

Từ đây tôi viết chương trình để tìm ra flag

data = [0xb71b, 0x12c, 0x156, 0x6e43, 0xd8, 0x69c3, 0x5cd3, 0x144, 0xe4, 0x6e43, 0x37cb, 0xf6, 0x69c3, 0x1e7b, 0x156, 0x3183, 0x69c3, 0x6c, 0x8b3b, 0xc0, 0x1e7b, 0x156, 0xfc, 0x50bb, 0x69c3, 0xc0, 0x102, 0x6e43, 0xde, 0xb14b, 0xc6, 0xfc, 0xd8]
flag = ""
for i in range(len(data) - 1, -1, -1):
    # dilute
    value = int(data[i] / 1.5)
    # drench
    value >>= 1
    # moisten
    if value % 2 != 0:
        value = int(value ** 0.5)
    flag += chr(value)
print(flag)

Bạn có thể đọc đoạn mã ở dưới để hiểu được luồng của chương trình.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Challenge {
    public static void main(String[] arrstring) {
      String string = "HTB{JaV@_STr3@m$_Ar3_REaLlY_Hard}";
      Challenge.dunkTheFlag(string).stream().forEach(System.out::println);
    }

    private static List<String> dunkTheFlag(String string3) {
        return Arrays.asList(string3.chars().mapToObj(n -> Character.valueOf((char)n)).collect(Collectors.toList()).stream().peek(c -> Challenge.hydrate(c)).map(c -> c.toString()).reduce("", (string, string2) -> string2 + string).chars().mapToObj(n -> Character.valueOf((char)n)).collect(Collectors.toList()).stream().map(c -> c.toString()).reduce(String::concat).get().chars().mapToObj(n -> n).collect(Collectors.toList()).stream().map(n -> Challenge.moisten(n)).map(n -> (int)n).map(Challenge::drench).peek(Challenge::waterlog).map(Challenge::dilute).map(Integer::toHexString).reduce("", (string, string2) -> string + string2 + "O").repeat(5));
    }

    private static Integer hydrate(Character c) {
        return c.charValue() - '\u0001';
    }

    private static Integer moisten(int n) {
        return (int)(n % 2 == 0 ? (double)n : Math.pow(n, 2.0));
    }

    private static Integer drench(Integer n) {
        return n << 1;
    }

    private static Integer dilute(Integer n) {
        return n / 2 + n;
    }

    private static byte waterlog(Integer n) {
        n = ((n + 2) * 4 % 87 ^ 3) == 17362 ? n * 2 : n / 2;
        return n.byteValue();
    }
}
Flag: HTB{JaV@_STr3@m$_Ar3_REaLlY_Hard} 

Challenge 26: Spooky License

After cleaning up we found this old program and wanted to see what it does, but we can't find the licence we had for it anywhere. Can you help?

Download Challenge Here

Solution

Chúng ta có thể sử dụng lệnh 'file' để cung cấp cái nhìn tổng quan về loại tệp.

image

Mở chương trình trong IDA.

image

Bạn có thể sử dụng z3 hoặc angr, tùy bạn. Nhưng tôi đề cập đến việc sử dụng angr với việc thực hiện điều kiện challange, z3 khi phép tính không kéo dài.

import angr
import claripy
import sys
def main(argv):
    path_to_binary = "spookylicence"
    proj = angr.Project(path_to_binary, load_options={'auto_load_libs':False})
    
    sym_arg_size = 32
    sym_arg=claripy.BVS('sym_arg', 8*sym_arg_size)
    argv.append(sym_arg)

    initial_state  = proj.factory.entry_state(args=argv,add_options={angr.sim_options.ZERO_FILL_UNCONSTRAINED_REGISTERS,angr.sim_options.ZERO_FILL_UNCONSTRAINED_MEMORY})
    simulation = proj.factory.simulation_manager(initial_state )
    
    exit_address = 0x400000+0x187d
    avoid_address = 0x400000+0x1890
    simulation.explore(find=exit_address, avoid=avoid_address)
    if simulation.found:
        solution = simulation.found[0].solver.eval(argv[1], cast_to=bytes)
        print(solution)
    else:
        print("Failed")

if __name__ == '__main__':
  main(sys.argv) 
Flag: HTB{The_sp0000000key_liC3nC3K3Y}    

Challenge 27: Encryption Bot

My friend send me a encrypted message by using encryption bot. This is a important message. Can you decrypt the message for me?

Download Challenge Here

Solution

Chúng ta có thể sử dụng lệnh 'file' để cung cấp cái nhìn tổng quan về loại tệp.

image

Mở chương trình trong IDA.

image

Chương trình kiểm tra độ dài dữ liệu ta nhập vào có là 27, sau đó chuyển nó thành hệ nhị phân.

image

Mục đích chuyển thành hệ nhị phân để bắc cầu - chuyển đổi dữ liệu theo hệ 6. Kết quả đó sẽ được chuyển thành hệ thập phân và lấy kí tự ASCII.

image

data = "RSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQabcdefghijklmnopqrstuvwxyz"
flag_encrypt = "9W8TLp4k7t0vJW7n3VvMCpWq9WzT3C8pZ9Wz"
index = [data.index(char) for char in flag_encrypt if char in data]

binary = ''.join(format(num, '06b') for num in index)
binary_chunks = [binary[i:i+8] for i in range(0, len(binary), 8)]

flag = ''.join([chr(int(chunk, 2)) for chunk in binary_chunks])
print(flag)
Flag: HTB{3nCrypT10N_W1tH_B1Ts!!}

Challenge 28: Cyberpsychosis

Malicious actors have infiltrated our systems and we believe they've implanted a custom rootkit. Can you disarm the rootkit and find the hidden data?

Download Challenge Here

Solution

Chúng ta có thể sử dụng lệnh 'file' để cung cấp cái nhìn tổng quan về loại tệp.

image

Loadable kernel modules (. ko files) là một phần mở rộng tệp quan trọng trong hệ điều hành Linux. Nó cho phép người dùng và nhà phát triển mở rộng khả năng của nhân Linux và tùy chỉnh hệ thống của họ theo nhu cầu cụ thể.

Tệp .ko là một tệp đối tượng đã được biên dịch chứa mã được thiết kế đặc biệt để hoạt động với nhân Linux. Tệp này có thể chứa mã cho các tính năng hoặc chức năng mới, chẳng hạn như trình điều khiển thiết bị, mô-đun hệ thống tệp hoặc mô-đun bảo mật.

Mở chương trình trong IDA.

image

Trong lập trình và bảo mật hệ thống, _fentry__ là một hàm liên quan đến Linux Kernel, nó là nơi mà một hàm sẽ nhảy đến ngay sau khi được gọi, trước khi bất kỳ mã chính nào của hàm đó được thực thi. Đây là một phần của cơ chế ftrace, một công cụ trong hạt nhân Linux được sử dụng để debug và theo dõi hệ thống.

Ngay từ đầu, tôi xác định rằng nó có thể liên quan đến rootkit hoặc một số dạng phần mềm độc hại có thể thay đổi bảng lệnh gọi hệ thống để ẩn mô-đun khỏi một số kiểm tra nhất định.

Ta cần hiểu luồng tư duy như sau: rootkit cần đánh lừa người dùng ở chế độ người dùng, tức là thực hiện kiểm soát giao diện giữa User-mode và Kernel-mode. Một trong số đó có kỹ thuật hooking: chuyển hướng một cuộc gọi hệ thống đến một chức năng tùy chỉnh.

Đầu tiên nó sẽ tìm syscall table, sau đó là thay thế các cuộc gọi hệ thống bằng các hàm của kẻ tấn công. Trong thực tế, chỉ cần sửa syscall table - nơi chứa địa chỉ của các lệnh gọi hệ thống.

image

Hàm get_syscall_table_bf(): Hàm này dùng để lấy địa chỉ của bảng syscall.

image

module_hide(): Chức năng này dường như được thiết kế để ẩn mô-đun khỏi danh sách mô-đun hạt nhân Linux. Theo link để hiểu thêm

  • Khai báo biến prevnext trỏ đến cấu trúc list_head, đại diện cho một nút trong danh sách liên kết.
  • Thực hiện loại bỏ mô-đun hiện tại khỏi danh sách.
  • Gán địa chỉ không hợp lệ cho con trỏ nút tiếp theo của mô-đun hiện tại.
  • Đặt cờ toàn cục module_hidden để chỉ ra mô-đun bị ẩn.

image

Chương trình thực hiện lấy địa chỉ của 3 hàm getdents getdents64 kill và thay thế địa chỉ đó bằng các hàm custom.

Đoạn mã khá dài nên mình không upload đoạn mã lên mà tiến hành mô tả từng hàm custom:

  1. hacked_getdents và hacked_getdents64 - Hàm này thay đổi hành vi của hàm getdents. Nó cấp phát bộ nhớ cho một biến v5 và kiểm tra vùng nhớ vừa được cấp. Sau đó, sao chép dữ liệu từ User-space và Kernel-space. Xử lý dữ liệu trả về từ hàm gốc getdents. Cuối cùng, hàm này giải phóng bộ nhớ được cấp phát và trả về kích thước của dữ liệu đã được xử lý. Đọc tại đây để biết thêm chi tiết kĩ thuật https://github.com/m0nad/Diamorphine/blob/master

image

Là một rootkit, tôi tìm rootkit source. Ta cần cũng tìm thấy một số lệnh để vô hiệu hóa rootkit. Tôi nhận ra rằng mục tiêu của chúng tôi là hiển thị tệp máy chủ để chúng tôi có thể nhận được cờ.

image

  1. hacked_kill() - cho phép ta tắt tính năng "hidden" các module
  • Quá trình đầu tiên: "kill -46 0" thao tác này sẽ "tắt khả năng tàn hình" rootkit khiến nó hiển thị. Đọc để hiểu thêm.
  • Quy trình thứ hai: kill -64 0 nó sửa đổi thông tin xác thực kernel bằng cách sử dụng các hàm prepare_credscommit_creds, đặt một số trường nhất định thành 0. Lưu ý rằng việc sử dụng struct cred để đặt giá trị UID và GID thành 0 không đảm bảo tiến trình sẽ trở thành root. Nếu các giá trị khác như EUID, SUID, FSUID cũng không được đặt thành 0 thì quyền truy cập có thể vẫn khác với quyền root.
  • Quy trình thứ ba: kill -31 0 bỏ ẩn bất kỳ tiến trình nào, kill -<num> 0 đang thực hiện orig_kil()

Lệnh lsmod được sử dụng để hiển thị danh sách các mô-đun hạt nhân đang được tải trong hệ thống.

Ta thực hiện rmmod diamorphine vì trạng thái các module trong Linux Kernel vẫn diamorphine, chúng ta phải gỡ bỏ nó và hiển thị tất cả các file. Từ đó ta truy cập hệ thống với người dùng đang đăng nhập là root

image

Flag: HTB{N0w_Y0u_C4n_S33_m3_4nd_th3_r00tk1t_h4s_b33n_sUcc3ssfully_d3f34t3d!!} 

Challenge 29: ARMs Race

The famous hacker Script K. Iddie has finally been caught after many years of cybercrime. Before he was caught, he released a server sending mysterious data, and promised his 0-days to anyone who could solve his multi-level hacking challenge. Now everyone is in an ARMs race to get his exploits. Can you be the one to solve Iddie's puzzle?

Download Challenge Here

Solution

Khi kết nối Instance, ta được truy cập đến trang web với nội dung là mã Hex.

image

Ta đang ở Level 1/50, ta có thể dự đoán rằng khi ta vượt qua 50 level thì ta nhận được flag. Ngoài ra, chuỗi hexa ta cung cấp có thể tạo thành các câu lệnh của ARM. Nó yêu cầu ta tính toán thanh ghi R0 của các câu lệnh ARM ấy. Ta sẽ cần sử dụng socket để kết nối đến địa chỉ được cấp, cũng như gửi kết quả tính được của thanh ghi R0. Các trường hợp khi ta kết nối:

  • Timeout! You took too long to provide input. - khi kết quả gửi chậm.
  • Value not recognized - khi kết quả gửi không phải là hex.
  • Wrong answer - khi kết quả gửi không đúng.
  • Level */50: - kết quả đúng và qua level kế tiếp.

Ta viết chương trình để kết nối đến địa chỉ IP được cấp và thực hiện tính toán. Lưu ý:

  • mov, movw, movt được sử dụng không có quy luật.
  • Cờ CF không có gây ảnh hưởng đến các phép tính có dấu.
import socket
from capstone import *
def recvuntil(sock, delim=b'\n'):
    data = b''
    while not data.endswith(delim):
        data += sock.recv(1)
    return data

def HexToArm(data):
    data = data.upper()
    csarmv7 = Cs(CS_ARCH_ARM, CS_MODE_ARM)
    csarmv7.detail = True
    disassembly = []
    for insn in csarmv7.disasm(bytes.fromhex(data), 0):
        disassembly.append(f"{insn.mnemonic} {insn.op_str}")
    R0, R1, R2 = 0, 0, 0
    for line in disassembly:
        if "mov" in line:
            value = int(line.split("#")[1].strip(), 16)
            if "r0" in line:
                R0 = value
            elif "r1" in line:
                R1 = value if "movw" in line or "mov " in line else (R1 + (value << 16))
            elif "r2" in line:
                R2 = value if "movw" in line or "mov " in line else (R2 + (value << 16))
        if any(op in line for op in ["r0, r0, #0", "r0, r0, r1"]):
            action = line.split()[0]
            if action == "add":
                R0 += R1 + R2
            elif action == "sub":
                R0 = (R0 - R1 - R2) + 0xFFFFFFFF
            elif action == "and":
                R0 &= R1 & R2
            elif action == "eor":
                R0 ^= R1 ^ R2
            elif action == "mul":
                R0 *= R1 * R2
            elif action == "orr":
                R0 |= R1 | R2
            elif action == "rsb":
                R0 = 0x100000000 - R0
    return hex(R0 & 0xFFFFFFFF)

def main():
    host = "IP"
    port = 

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))
    for i in range(50):
        response = recvuntil(sock)
        decoded_response = response.decode()    
        print(decoded_response)
        if "Wrong answer" in decoded_response or i == 50:
            sock.close()
            break

        colon_index = decoded_response.find(': ')
        content = decoded_response[colon_index + 1:].strip()
        response = recvuntil(sock, b'Register r0:')
        decoded_response = response.decode()

        result = HexToArm(content)
        sock.send((result + '\n').encode())
        print(decoded_response+result+"\n")
    sock.close()

if __name__ == "__main__":
    main()

Phiên bản tốt hơn của đoạn mã

import socket
from unicorn import *
from unicorn.arm_const import *
def recvuntil(sock, delim=b'\n'):
    data = b''
    while not data.endswith(delim):
        data += sock.recv(1)
    return data

def HexToArm(hex_str):
    code_bytes = bytes.fromhex(hex_str)
    
    # Initialize Unicorn for ARM architecture
    mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
    
    # Map 2MB memory for this emulation at address 0x10000
    ADDRESS = 0x10000
    mu.mem_map(ADDRESS, 2 * 1024 * 1024)
    
    # Write machine code to be emulated to memory
    mu.mem_write(ADDRESS, code_bytes)
    mu.reg_write(UC_ARM_REG_R0, 0x0)
    
    # Emulate machine code in infinite time
    try:
        mu.emu_start(ADDRESS, ADDRESS + len(code_bytes))
    except UcError as e:
        print("ERROR: %s" % e)
    
    # Read back the value of R0 register
    r0 = mu.reg_read(UC_ARM_REG_R0)
    
    return str(r0)

def main():
    host = "IP"
    port = 

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))
    for i in range(51):
        response = recvuntil(sock)
        decoded_response = response.decode()    
        print(decoded_response)

        colon_index = decoded_response.find(': ')
        content = decoded_response[colon_index + 1:].strip()
        response = recvuntil(sock, b'Register r0:')
        decoded_response = response.decode()

        result = HexToArm(content)
        sock.send((result + '\n').encode())
        print(decoded_response+result+"\n")
    sock.close()

if __name__ == "__main__":
    main()