題目越來越難啦…

Likemyasp

  • 看題目就知加了殼,但又找不到脫殼工具,只好自己手脫了( 瞎G8亂脫 )
  • 64位程序,使用x64dbg來脫殼,先來到EP( 這個EP是加殼後的EP,而脫殼的目的是找到沒加殼時的OEP )

Untitled

ctrl+n,在VirtualProtect下斷點

Untitled

  • 然後按F9執行( 2次 ),發現總共會在VirtualProtect函數中斷下兩次
  • 在第2次時,按alt+f9執行到返回,然後一直單步F8,直到看到如下圖的地方
  • 綠框中的地方很像**main函數的參數( argv、argc )因此這附近可能就存在我們想要的東西( 未必是OEP )**,單步F7進入紅框那個call

Untitled

  • 發現應該是找對了,下面也有提示成功or失敗的字串
  • 然後直接用x64dbg自帶的Scylladump出來( 直接按Scylla中的dump就可以 )
  • 注:這樣dump出來的文件並不能直接運行,但也可以在IDA中進行分析

Untitled

  • 將dump出來的exe拉入IDA,雖然會提示錯誤,但不要管繼續進入
  • start函數中可以看到程序的加密邏輯( 下圖是我重命名後的結果,一開始IDA並不能識別出printfscanf 這些函數)
  • 可以看出該加密是個可逆的加密,直接寫腳本就可解出flag

Untitled

腳本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;

int main() {
__int64 enc[6] = { 0xD803C1FC098,0x0E20360BC097,0x0FE02A1C00A0,0x0FA0121040CB,0x0F2032104092,0x0D6015884082 };
for (int i = 0; i < 6; i++) {
cout << (char)(((enc[i] >> 37) ^ 0xa) % 256);
cout << (char)(((enc[i] >> 23) ^ 0x14) % 256);
cout << (char)(((enc[i] >> 14) ^ 0x1E) % 256);
cout << ((unsigned char)(~enc[i]));
}
}

FindME

拉入IDA,發現一層又一層的函數調用,當時懶得繼續點,直接用angr試試看,結果成功了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import angr
import sys

def is_good(state):
return b'You Gift' in state.posix.dumps(1) # state.posix.dumps(1)代表輸出,若輸出中包含'Well done! You find the secret!'即代表是我的目的地

def is_bad(state):
return b'Wait' in state.posix.dumps(1) # state.posix.dumps(1)代表輸出,若輸出中包含'The flag is wrong! Maybe something run before main'即代表要避開的地方

def main(argv):
bin_path = argv[1] # argv[1]是調用.py文件時傳入的參數,即【python3 mySolve.py xxx】中的xxx
p = angr.Project(bin_path)

init_state = p.factory.entry_state()
sm = p.factory.simulation_manager(init_state,veritesting = True)
sm.explore(find = is_good, avoid = is_bad) #除了傳入地址外,也能以這種方式來判斷

if sm.found: #當sm.found不為空時
found_state = sm.found[0]
print("Solution: {}".format(found_state.posix.dumps(0)))

if __name__ == "__main__":
main(sys.argv)

Petals

找到main函數,整體邏輯十分清析,除了紅框中兩個未知函數

Untitled

進入sub_5618355E5209函數,是個比較普通的加密,應該可以直接腳本爆破

Untitled

進入sub_5618355E560C函數,是個比較

Untitled

腳本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;

unsigned __int64 __fastcall func(char* input, unsigned int len)
{
int i; // [rsp+18h] [rbp-118h]
unsigned int j; // [rsp+1Ch] [rbp-114h]
__int64 v5[33]; // [rsp+20h] [rbp-110h] BYREF
unsigned __int64 v6; // [rsp+128h] [rbp-8h]

memset(v5, 0, 256);
for (i = 0; i <= 255; ++i)
*((BYTE*)v5 + i) = ~(i ^ len);
for (j = 0; len > j; ++j)
input[j] = *((BYTE*)v5 + (unsigned __int8)input[j]);
return 0;
}

int main() {
char enc[] =
{
0xD0, 0xD0, 0x85, 0x85, 0x80, 0x80, 0xC5, 0x8A, 0x93, 0x89,
0x92, 0x8F, 0x87, 0x88, 0x9F, 0x8F, 0xC5, 0x84, 0xD6, 0xD1,
0xD2, 0x82, 0xD3, 0xDE, 0x87,0
};
char flag[56] = { 0 };
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 256; j++) {
flag[i] = j;
func(flag, 25);
if (flag[i] == enc[i]) {
cout << (char)j;
break;
}
}
}
// 66ccff#luotianyi#b074d58a
}

根據提示可知需要將解出的東西進行md5加密,隨便找個線上網站加密一下。有4個結果,本來想每個都試一下,結果一下就撞對了( d780c9b2d2aa9d40010a753bc15770de )

Untitled

前可见古人,后得见来者

拉入IDA,找到main函數,看上去挺清晰的,進入加密函數看看

Untitled

加密函數也不難,可以寫腳本直接爆破

Untitled

腳本如下,輸出結果為pvkq{loqsx_kxn_oxn_bo_kxn_iye},一看就不是flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <string>
using namespace std;

int dword_41A000 = 3;

int __cdecl sub_4118C0(string& input, int len)
{
int result; // eax
int j; // [esp+D0h] [ebp-14h]
int i; // [esp+DCh] [ebp-8h]

for (i = 0; i < len; ++i)
{
if (input[i] < 65 || input[i] > 'Z')
{
if (input[i] >= 97 && input[i] <= 122)
input[i] = (input[i] + dword_41A000 - 97) % 0x1Au + 97;
}
else
{
input[i] = (input[i] + dword_41A000 - 65) % 0x1Au + 65;
}
}
for (j = 0; ; ++j)
{
result = j;
if (j >= len)
break;
input[j] ^= 0x22u;
}
return result;
}

int main() {
string enc = "Q[LVYMPVTC}LCS}PCS}GP}LCS}N@J_";
string flag = "Q[LVYMPVTC}LCS}PCS}GP}LCS}N@J_"; // 隨便填充什麼都可以(但長度要夠)
int len = enc.length();
for (int j = 0; j < len; j++) {
for (int i = 1; i < 256; i++) {
flag[j] = (char)i;
sub_4118C0(flag, len);

if (flag[j] == enc[j]) {
cout << (char)i;
break;
}
}
}
}
  • 後來發現加密函數中的dword_41A000這個變量被TlsCallback_0_0函數交叉引用
  • 而TLS回調函數( 可上網查一下什麼是TLS )的調用時機在main函數之前,因此dword_41A000很有可能在TlsCallback_0_0被修改過

Untitled

進入TlsCallback_0_0查看,發現果然如此,所以只要將上方腳本中的dword_41A000的值+10就可以得到真的flag

Untitled

ur_so_naive

  • 是個apk文件,拉入jadx分析,去到com.new_star_ctf.u_navie/MainActivity查看程序的邏輯
  • 可以看到加密函數encry,它前面有個native關鍵字,代表函數在native層中實現
  • native層實現的函數保存在.so文件中,.so文件位於apk文件解壓後的\lib目錄下

Untitled

Untitled

libencry.so拉入IDA,找到encry函數,會發現一堆不明所以的東西,這是由一種叫做**JNI的東西導致IDA分析失敗(** 點擊查看JNI具體是什麼 )

Untitled

解決方法:對應JNI函數原型( 在上方網址中可找到 ),手動修改encry函數參數類型( 快捷鍵y ),修改後如下圖所示:

Untitled

  • 修改完之後IDA就能把一些函數正常分析出來,如下圖所示
  • 接下來分析加密邏輯,可以看到input經過一大<<|^加密,但這些都不是重點,重點是最後一步的input[index++]=v14^input[v13],這一句相當於input[i]^=input[i+1] (當i == size - 1時,input[i]^=input[0] ),這樣相當於是一個”環形加密”,無法直接爆破
  • 要解密必須要知道明文中的1-2個字符,而明文通常為flag{XXXX},因此明文猜測前4個字串為flag以此爆破後面的所有字符

Untitled

腳本如下( enckey都可在jadx中輕鬆獲取 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;

#define LOBYTE(x) (*((BYTE*)&(x))) // low byte

void func(char* input,char* key,int size) {
__int64 index; // x8
unsigned int v12; // w11
__int64 v13; // x13
unsigned int v14; // w11
bool v18; // zf
unsigned int v15, v16, v17;

index = 0LL;
do
{
v12 = (unsigned __int8)input[index];
if (size - 1LL == index)
v13 = 0LL;
else
v13 = index + 1;
input[index] = ((unsigned __int8)input[index] >> 1) & 0x7F | (input[index] << 7);
v14 = ((v12 >> 1) & 0xFFFF807F | ((unsigned __int8)v12 << 7)) ^ *(unsigned __int8*)key;
v15 = ((BYTE)v14 << 6) | ((unsigned __int8)v14 >> 2);
input[index] = v15;
v16 = (32 * (v15 ^ key[1])) | ((unsigned __int8)(v15 ^ key[1]) >> 3);
input[index] = v16;
v17 = (16 * (v16 ^ key[2])) | ((unsigned __int8)(v16 ^ key[2]) >> 4);
input[index] = v17;
LOBYTE(v14) = v17 ^ key[3];
input[index] = v14;
v18 = size == index + 1;
input[index++] = v14 ^ input[v13];
} while (!v18);


}

int main() {
char enc[] = { -36, 83, 22, -117, -103, -14, 8, 19, -47, 47, -110, 71, 2, -21, -52, -36, 24, -121, 87, -114, -121, 27, -113, -86 };
int len = 24;
char flag[25] = { 0 };
char key[] = "FALL";
flag[0] = 'f';
flag[1] = 'l';
char tmp[25] = { 0 };

for (int i = 2; i < 24; i++) {
for (int j = 0; i < 256; j++) {
strcpy(tmp, flag);
tmp[i] = j;
func(tmp, key, len);
if (tmp[i - 1] == enc[i - 1]) {
flag[i] = j;
break;
}
}
}
cout << flag;
}